SQLite3簡單C++包裝類原始碼示例
阿新 • • 發佈:2019-01-01
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
定義是相符合的。
上面是兩種完全不同的處理方法。
目前我通過實踐,摸透了兩個例子
這個例子我比較欣賞,準備在它的基礎上編寫一個簡單的SQLite C++ wrapper類。參見下面的程式碼:
下面是原始檔// // #ifndef __SQLITE3_WRAPPER_H__ #define __SQLITE3_WRAPPER_H__ #include <map> #include <string> #include <sstream> #include <vector> #include <memory> #include <stdlib.h> #include <stdio.h> #include <sqlite3.h> class CException: std::exception { public: CException(int _rc): rc(_rc) { stringstream ss; ss << rc; msg = ss.str(); } CException(const char* _msg): msg(_msg) {} ~CException() throw(){} int getCode() const { return rc; } const char* getMessage() const { return msg.c_str(); } protected: int rc; string msg; }; class CSQLite3; class CQuery{ public: CQuery(CSQLite3& _db, const char* sql); ~CQuery(); CSQLite3& getDB() const { return db; } size_t getPos() const { return pos; } size_t getNumCols() const { return cols.size(); } //最核心的函式, 會被反覆呼叫, 每次從結果集中讀取出一行(ie.一條記錄), 存放在vals向量中 bool read_one_row(); int getColIdx(const char* col) const; const char* getVal(size_t idx) const; const char* getVal(const char* col) const; const char* operator[](int idx) const; const char* operator[](const char* col) const; int getVals(map<string, string>& pair) const; private: CQuery(const CQuery& src); CSQLite3& db; //database connection object sqlite3_stmt* stmt; //prepared statement object size_t pos; //用於區分出列名而非記錄 vector<const char*> cols; //列名組成的向量 vector<const char*> vals; //每次只儲存一條記錄(也就是一行)的資料, 下次處理時會首先清空再存放下一條記錄 map<string, int> colIdxs; //列名和索引對應的記錄 }; class CSQLite3{ public: CSQLite3(const char* path, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, const char* zVfs = NULL); ~CSQLite3(); sqlite3* getConn() const { return conn; } typedef int (*Callback)(void*, int, char**, char**); void exec(const char* sql, Callback cb = NULL, void* arg = NULL); string execStr(const char* sql); long long execInt(const char* sql); unsigned long long execUInt(const char* sql); std::auto_ptr<CQuery> execCQuery(const char* sql); int execOne(const char* sql, map<string, string>& pair); private: sqlite3* conn; }; #endif
下面是測試檔案#include "sqlite3_wrapper.h" CQuery::CQuery(CSQLite3& _db, const char* sql): db(_db), stmt(NULL), pos(0) { int rc = sqlite3_prepare_v2(db.getConn(), sql, -1, &stmt, NULL); if(rc) throw CException(rc); } CQuery::~CQuery(){ if(stmt) sqlite3_finalize(stmt); } //該函式會反覆呼叫,每次只儲存一行的值 bool CQuery::read_one_row() { int rc = sqlite3_step(stmt); if(rc != SQLITE_ROW && rc != SQLITE_DONE) throw CException(rc); //僅在開始獲取列名向量, 以後直接跳過 if(pos++ < 1) { size_t numCols = sqlite3_column_count(stmt); cols.reserve(numCols); vals.reserve(numCols); for(size_t i = 0; i < numCols; i++) { const char* col = sqlite3_column_name(stmt, i); cols.push_back(col); colIdxs[std::string(col)] = i; } } //如果沒有下一條記錄就返回, 這說明我們已經讀完了 if(rc == SQLITE_DONE) return false; //還有下一條記錄可以讀, 先清空存放該記錄值的向量 vals.clear(); for(size_t i = 0; i < getNumCols(); i++) { const char* val = (const char*)sqlite3_column_text(stmt, i); vals.push_back(val); } return true; } //找出列名在向量中對應的索引值 int CQuery::getColIdx(const char* col) const { std::map<std::string, int>::const_iterator i = colIdxs.find(std::string(col)); if(i != colIdxs.end()) return (*i).second; return -1; } const char* CQuery::getVal(size_t idx) const { if(idx < 0 || idx > getNumCols()) return NULL; return vals[idx]; } const char* CQuery::getVal(const char* col) const { return getVal(getColIdx(col)); } const char* CQuery::operator[](int idx) const { return getVal(idx); } const char* CQuery::operator[](const char* col) const { return getVal(col); } int CQuery::getVals(map<string, string>& pair) const { for(size_t i = 0; i < getNumCols(); i++) pair[std::string(cols[i])] = getVal(i); return 0; } CSQLite3::CSQLite3(const char* path, int flags, const char* zVfs): conn(NULL) { int rc = sqlite3_open_v2(path, &conn, flags, zVfs); if(rc) throw CException(rc); } CSQLite3::~CSQLite3(){ if(conn) sqlite3_close(conn); } void CSQLite3::exec(const char* sql, Callback cb, void* arg) { char* error = NULL; int rc = sqlite3_exec(conn, sql, cb, arg, &error); if(error) { CException ex(error); sqlite3_free(error); throw ex; } if(rc) throw CException(rc); } string CSQLite3::execStr(const char* sql) { std::string ret; CQuery c(*this, sql); if(c.read_one_row() && c.getNumCols() > 0) ret = c[0]; return ret; } long long CSQLite3::execInt(const char* sql) { CQuery c(*this, sql); if(!c.read_one_row() || c.getNumCols() < 1) return -1; return strtoll(c[0], NULL, 10); } unsigned long long CSQLite3::execUInt(const char* sql) { CQuery c(*this, sql); if(!c.read_one_row() || c.getNumCols() < 1) return -1; return strtoull(c[0], NULL, 10); } std::auto_ptr<CQuery> CSQLite3::execCQuery(const char* sql) { return std::auto_ptr<CQuery>(new CQuery(*this, sql)); } //成功返回0, 失敗返回1 int CSQLite3::execOne(const char* sql, map<string, string>& pair) { CQuery c(*this, sql); if(!c.read_one_row()) { return 1; } return c.getVals(pair); }
注意://g++ -g test_sqlite3.cpp sqlite3_wrapper.cpp -o test_sqlite3 -lsqlite3 // #include <iostream> #include <unistd.h> #include "sqlite3_wrapper.h" int main() { try { unlink("test.db"); CSQLite3 db("test.db"); db.exec("create table test(id int primary key, value int);"); for(int i = 0; i < 100; i++) { std::stringstream sql; sql << "insert into test values(" << i << ", " << i * 2 << ");"; db.exec(sql.str().c_str()); } std::cout << "Max: " << db.execInt("select max(value) from test;") << std::endl; CQuery query(db, "select * from test order by value desc;"); while(query.read_one_row()) std::cout << query["id"] << " = " << query["value"] << std::endl; //some arbitrary op to show that you can easily get the sqlite3 object and do whatever to it directly sqlite3_interrupt(db.getConn()); } catch(CException ex) { std::cout << "SQLite exception: " << ex.getMessage() << std::endl; } return 0; }