Eintrag
Die Bibliothek selbst ist ziemlich ausgereift - die erste Veröffentlichung auf dem Github stammt aus dem Jahr 2004. Ich war überrascht, als Habr in der Suchmaschine mir keinen einzigen Link zu Artikeln gab, die diese wunderbare Bibliothek erwähnen.
Ausgesprochen als: soci , mit Schwerpunkt auf der ersten Silbe.
SOCI unterstützt ORM durch die Spezialisierung von soci :: type_conversion .
Datenbankunterstützung (DB) (Backends):
Ich werde hier keine Handbücher übersetzen oder Code aus Beispielen bereitstellen, aber ich werde versuchen, den Code aus meinem früheren Projekt (mit einer Änderung der Tabellenstruktur und anderen Vereinfachungen) anzupassen, um ihn visueller und interessanter zu gestalten.
Installation
Wir laden Rohstoffe aus dem Hauptzweig herunter , entpacken sie und führen im Verzeichnis den folgenden Befehl aus:
In Fenstern
$ mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" ../ && cmake --build. --config Release
oder anstelle des letzten Befehls können Sie das resultierende Projekt in Visual Studio öffnen und kompilieren.
( Wilk forderte auf, mit cmake in der Kommandozeile zu bauen)
Auf nix
$ mkdir build && cd build && cmake ../ && sudo make install
soci-9999.ebuildWenn Sie Eigentümer von Gentoo Linux oder Calculate Linux sind und die neueste Version von SOCI aus dem offiziellen Repository auf dem Github im System haben möchten, können Sie diese Installationsdatei im /usr/portage/dev-db/soci/
speichern, den Befehl aufrufen und ausführen ::
# ebuild soci-9999.ebuild manifest && emer -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 }
Wir schreiben einen Pool für Verbindungen zur Datenbank
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
Definieren Sie die Tabellenstruktur in der Klasse 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
Testen Sie unseren Code
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
Fazit
In diesem Artikel haben wir die Hauptfunktionen der Bibliothek untersucht.
Im nächsten Artikel (wenn die Leser interessiert sind) werde ich über die Arbeit mit dem BLOB-Typ schreiben - zum Speichern von Dateien und Bildern in der Datenbank (in postgresql sind dies OID-Felder) sowie über Transaktionen und vorbereitete Anfragen.
Referenzen
SOCI auf Github
SOCI-Homepage