1. 程式人生 > >SQLite3簡單C++包裝類原始碼示例

SQLite3簡單C++包裝類原始碼示例

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;
}
注意: