一个比较好的sqlite3 C++ wrapper包装类的通常思路是这样的:
这里的重点是,首次将列字段名保存下来,再每次获取一行记录,将字段值记录到一个向量中,使用
sqlite3_column_text()将每个字段统一存为一个字符串,在需要处理时再分别转换为具体的数据类型。
当进行下一行解析时,先要将原来的数据清除,在存放当前行的数据。注意存放的是const char*,这和
const unsigned char *sqlite3_column_text(sqlite3_stmt*,int iCol);
定义是相符合的。
上面是两种完全不同的处理方法。
目前我通过实践,摸透了两个例子
https://github.com/wrmsr/SQLiteDB
这个例子我比较欣赏,准备在它的基础上编写一个简单的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,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],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,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; }
需要先安装sqlite3动态库,在Ubuntu 14.04 64bit上输入如下命令
在CentOS上面安装
yum -y install sqlite-devel
下面是运行截图
参考文献
原文链接:https://www.f2er.com/sqlite/200189.html