Apresentando SOCI - Biblioteca de Acesso ao Banco de Dados C ++

Entrada


A biblioteca em si é bastante madura - o primeiro lançamento no github remonta ao ano de 2004. Fiquei surpreso quando Habr, em um mecanismo de pesquisa, não me deu um único link para artigos que mencionam essa maravilhosa biblioteca.


Pronunciado como: soci , com ênfase na primeira sílaba.


SOCI suporta ORM , através da especialização de soci :: type_conversion .


Suporte ao banco de dados (DB) (back-end):



Não traduzirei manuais nem fornecerei código dos exemplos aqui, mas tentarei adaptar (com uma alteração na estrutura da tabela e outras simplificações) o código do meu projeto anterior para torná-lo mais visual e interessante.


Instalação


Fazemos o download de matérias-primas da ramificação principal , descompactamos e, dentro do diretório, executamos o comando:


No windows


$ mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64” ../ && cmake --build. - versão de configuração

ou, em vez do último comando, você pode abrir o projeto resultante no Visual Studio e compilar.
( Wilk perguntou sobre a construção com o cmake na linha de comando)


No nix


$ mkdir build && cd build && cmake ../ && sudo make install

soci-9999.ebuild

Se você é o proprietário do Gentoo Linux ou Calculate Linux e deseja ter a versão mais recente do SOCI no sistema a partir do repositório oficial no github, você pode salvar este arquivo de instalação no /usr/portage/dev-db/soci/ , vá para ele e execute o comando :


# ebuild soci-9999.ebuild manifesto && emerge -va = dev-db / soci-9999

 # Copyright 1999-2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 EAPI=6 if [[ ${PV} == *9999 ]] ; then SCM="git-r3" EGIT_REPO_URI="https://github.com/SOCI/${PN}.git" fi CMAKE_MIN_VERSION=2.6.0 inherit cmake-utils ${SCM} DESCRIPTION="Makes the illusion of embedding SQL queries in the regular C++ code" HOMEPAGE="http://soci.sourceforge.net/" if [[ ${PV} == *9999 ]] ; then SRC_URI="" KEYWORDS="~amd64 ~x86" else SRC_URI="https://github.com/SOCI/${PN}/archive/${PV}.tar.gz -> ${P}.tar.gz" KEYWORDS="amd64 x86" fi LICENSE="Boost-1.0" SLOT="0" IUSE="boost doc +empty firebird mysql odbc oracle postgres sqlite static-libs test" RDEPEND=" firebird? ( dev-db/firebird ) mysql? ( virtual/mysql ) odbc? ( dev-db/unixODBC ) oracle? ( dev-db/oracle-instantclient-basic ) postgres? ( dev-db/postgresql:= ) sqlite? ( dev-db/sqlite:3 ) " DEPEND="${RDEPEND} boost? ( dev-libs/boost ) " src_configure() { local mycmakeargs=( -DWITH_BOOST=$(usex boost) -DSOCI_EMPTY=$(usex empty) -DWITH_FIREBIRD=$(usex firebird) -DWITH_MYSQL=$(usex mysql) -DWITH_ODBC=$(usex odbc) -DWITH_ORACLE=$(usex oracle) -DWITH_POSTGRESQL=$(usex postgres) -DWITH_SQLITE3=$(usex sqlite) -DSOCI_STATIC=$(usex static-libs) -DSOCI_TESTS=$(usex test) -DWITH_DB2=OFF ) #use MYCMAKEARGS if you want enable IBM DB2 support cmake-utils_src_configure } src_install() { use doc && local HTML_DOCS=( doc/. ) cmake-utils_src_install } 

Escrevemos um pool para conexões com o banco de dados


db_pool.hpp
 #ifndef db_pool_hpp #define db_pool_hpp //      GCC,       //    ,  deprecated auto_ptr (   4) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #include <soci/soci.h> #include <soci/connection-pool.h> #pragma GCC diagnostic pop #include <iostream> #include <string> class db_pool { soci::connection_pool* pool_; std::size_t pool_size_; public: db_pool():pool_(nullptr),pool_size_(0) {} ~db_pool() { close(); } soci::connection_pool* get_pool() { return pool_; } bool connect(const std::string& conn_str, std::size_t n = 5) { if (pool_ != nullptr) { close(); } int is_connected = 0; if (!(pool_ = new soci::connection_pool((pool_size_ = n)))) return false; try { soci::indicator ind; for (std::size_t _i = 0; _i < pool_size_; _i++) { soci::session& sql = pool_->at(_i); //        sql.open(conn_str); //     sql << "SELECT 1;", soci::into(is_connected, ind); if (!is_connected) break; else if (_i+1 < pool_size_) is_connected = 0; } } catch (std::exception const & e) { std::cerr << e.what() << std::endl; } if (!is_connected) close(); return (pool_ != nullptr); } void close () { if (pool_ != nullptr) { try { for (std::size_t _i = 0; _i < pool_size_; _i++) { soci::session& sql = pool_->at(_i); sql.close(); } delete pool_; pool_ = nullptr; } catch (std::exception const & e) { std::cerr << e.what() << std::endl; } pool_size_ = 0; } } }; #endif 

Defina a estrutura da tabela na classe user_info


user_info.hpp
 #ifndef user_info_hpp #define user_info_hpp #include "db_pool.hpp" #include <ctime> #include <vector> #include <regex> #include <numeric> #include <algorithm> #include <iomanip> //   -        template<typename T> static void extract_integers(const std::string& str, std::vector<T>& result ) { result.clear(); using re_iterator = std::regex_iterator<std::string::const_iterator>; using re_iterated = re_iterator::value_type; std::regex re("([\\+\\-]?\\d+)"); re_iterator rit(str.begin(), str.end(), re), rend; std::transform(rit, rend, std::back_inserter(result), [](const re_iterated& it){return std::stoi(it[1]); }); } template<typename T> static void split_integers(std::string& str, const std::vector<T>& arr) { str = "{"; if (arr.size()) { str += std::accumulate(arr.begin()+1, arr.end(), std::to_string(arr[0]), [](const std::string& a, T b){return a + ',' + std::to_string(b);}); } str += "}"; } //   `users' class user_info { public: int id; //   std::tm birthday; //   std::string firstname, lastname; //    std::vector<int> friends; //   user_info():id(0),birthday(0),firstname(),lastname(),friends() {} void print() { std::cout.imbue(std::locale("ru_RU.utf8")); std::cout << "id: " << id << std::endl; std::cout << "birthday: " << std::put_time(&birthday, "%c %Z") << std::endl; std::cout << "firstname: " << firstname << std::endl; std::cout << "lastname: " << lastname << std::endl; std::string arr_str; split_integers(arr_str, friends); std::cout << "friends: " << arr_str << std::endl; } void clear() { id = 0; firstname = lastname = ""; friends.clear(); } user_info& operator=(const user_info& rhs) { if (this != &rhs) { id = rhs.id; birthday = rhs.birthday; firstname = rhs.firstname; lastname = rhs.lastname; friends = rhs.friends; } return *this; } }; //     ,  SOCI   namespace soci { template<> struct type_conversion<user_info> { typedef values base_type; static void from_base(values const& v, indicator ind, user_info& p) { if (ind == i_null) return; try { p.id = v.get<int>("id", 0); p.birthday = v.get<std::tm>("birthday", {}); p.firstname = v.get<std::string>("firstname", {}); p.lastname = v.get<std::string>("lastname", {}); std::string arr_str = v.get<std::string>("friends", {}); extract_integers(arr_str, p.friends); } catch (std::exception const & e) { std::cerr << e.what() << std::endl; } } static void to_base(const user_info& p, values& v, indicator& ind) { try { v.set("id", p.id); v.set("birthday", p.birthday); v.set("firstname", p.firstname); v.set("lastname", p.lastname); std::string arr_str; split_integers(arr_str, p.friends); v.set("friends", arr_str); ind = i_ok; return; } catch (std::exception const & e) { std::cerr << e.what() << std::endl; } ind = i_null; } }; } #endif 

Testando nosso código


test.cxx
 #ifndef test_cxx #define test_cxx #include "user_info.hpp" // g++ -std=c++11 test.cxx -o test -lsoci_core -lsoci_postgresql -lsoci_mysql && ./test int main() { db_pool db; /// \note  "postgresql"   ,         if (db.connect("postgresql://host='localhost' dbname='test' user='test' password='test'")) { try { soci::session sql(*db.get_pool()); //     std::string query_str = "CREATE TABLE IF NOT EXISTS users(id"; //     ,    -   id if (sql.get_backend_name() == "postgresql") query_str += " SERIAL "; else if (sql.get_backend_name() == "mysql") query_str += " INT AUTO_INCREMENT "; else query_str += " INT "; query_str += "NOT NULL PRIMARY KEY, birthday TIMESTAMP DEFAULT CURRENT_TIMESTAMP, firstname TEXT DEFAULT NULL, lastname TEXT DEFAULT NULL, friends TEXT DEFAULT NULL)"; //   sql << query_str; //   user_info info; std::time_t t = std::time(nullptr); info.birthday = *std::localtime(&t); info.firstname = "Dmitrij"; info.lastname = "Volin"; info.friends = {1,2,3,4,5,6,7,8,9}; sql << "INSERT INTO users(birthday, firstname, lastname, friends) VALUES(:birthday, :firstname, :lastname, :friends)", soci::use(info); t = std::time(nullptr); info.birthday = *std::localtime(&t); info.firstname = "Vasy"; info.lastname = "Pupkin"; info.friends = {11,22,33,44,55,66,77,88,99}; //       sql << "INSERT INTO users(birthday, firstname, lastname, friends) VALUES(:birthday, :firstname, :lastname, :friends)", soci::use(info); //   ,  : soci::i_ok, soci::i_null soci::indicator ind; //  MySQL  id   ,  AUTO_INCREMENT: // sql.get_backend()->get_last_insert_id(sql, "users", reinterpret_cast<long&>(id)); // //  PostgreSQL   id  ,    : // sql << "INSERT INTO users(birthday, firstname, lastname, friends) VALUES(:birthday, :firstname, :lastname, :friends) RETURNING id", soci::use(info), soci::into(id, ind); //      info.clear(); //       ,   `lastname' sql << "SELECT * FROM users WHERE lastname = :label LIMIT 1", soci::use(std::string("Volin"), "label"), soci::into(info, ind); if (ind == soci::i_null) std::cout << "      ..." << std::endl; else info.print(); std::cout << "++++++++++++++++++++++++++++++++++++++" << std::endl; //     soci::rowset<user_info> rs = (sql.prepare << "SELECT * FROM users"); for (auto it = rs.begin(); it != rs.end(); it++) { user_info& i = *it; i.print(); } //   sql << "DROP TABLE IF EXISTS users"; } catch (std::exception const & e) { std::cerr << e.what() << std::endl; } } return 0; } #endif 

Conclusão


Neste artigo, examinamos os principais recursos da biblioteca.


No próximo artigo (se os leitores estiverem interessados), escreverei sobre como trabalhar com o tipo BLOB - para armazenar arquivos e imagens no banco de dados (no postgresql, são campos OID), bem como sobre transações e solicitações preparadas.


Referências


SOCI no github
Página inicial SOCI

Source: https://habr.com/ru/post/pt422881/


All Articles