CppSQLite - C++ SQLite3封装类

1、Rob Groves原版

CppSQLite封装类下载(CppSQLite 3.2 , source and sqlite3.dll for SQLite 3.4.0 ):
http://download.csdn.net/download/qing666888/10114989
 

2、github上其他版本

neosmart版本:https://github.com/neosmart/CppSQLite

SQLite3下载地址:http://www.sqlite.org/2017/sqlite-amalgamation-3210000.zip

 

谷歌翻译的。

介绍

 

本文介绍CppSQLite了一个围绕公共领域SQLite数据库库的非常简单的C ++包装器。

提供了如何将应用程序与SQLite关联的说明,然后介绍了一个使用示例程序CppSQLite,最后对这些CppSQLite类进行了说明。

要设置场景,这里是来自SQLite作者的引用...

SQLite是一个C库,它实现了一个可嵌入的SQL数据库引擎。与SQLite库链接的程序可以进行SQL数据库访问,而无需运行单独的RDBMS进程。这个发行版带有一个独立的命令行访问程序(SQLite),可以用来管理SQLite数据库,并作为如何使用SQLite库的一个例子。

SQLite不是用于连接到大型数据库服务器的客户端库。SQLite是服务器。SQLite库直接读取和写入磁盘上的数据库文件。

背景

我一直在寻找简单而强大的软件开发工具和想法,SQLite绝对属于这一类。事实上,“Lite”这个名字有点让人误解,因为它实现了SQL标准的一个很大的子集,包括事务处理,而当像PHP这样的项目开始把它作为标准而不是MySQL来捆绑时,你必须看一看。

我认为在C接口上编写一个简单的包装以使其对C ++友好是很有趣的。SQLite网站上已经列出了一些C ++封装器,但其中一个是商业的,另外一个看起来有点复杂,而另外一个则是针对wxWidgets框架的。毕竟,SQLite的作者似乎已经努力保持简单,所以我认为它的C ++包装应该保持简单。

使用SQLite

SQLite在Windows平台上以2个包的形式提供,作为编译的DLL提供,也以源代码形式提供。即使你只是想使用DLL,你仍然需要获得源代码,因为这包含所需的头文件。

如果需要,可以将SQLite源代码编译到一个库(.lib)文件中,以便静态链接到您的应用程序,但这不在本文中讨论。编译指令可以在SQLite网站上找到。

动态链接仍然需要建立一个.lib文件来连接你的应用程序。这可以使用微软的LIB命令来完成。在我的系统上,它位于D:\ Program Files \ Microsoft Visual Studio \ VC98 \ Bin \ lib.exe。

解压包含sqlite.dll和sqlite.def的sqlite.zip,并执行以下命令生成lib文件。

隐藏   复制代码

c:\>lib /def:sqlite.def

在编译时,sqlite.h需要在应用程序中可见,sqlite.lib也是如此。

sqlite.dll需要在运行时可用于您的应用程序。

CppSQLite演示代码

以下代码演示了如何通过CppSQLite内联注释使用SQLite的主要功能。

隐藏   缩小    复制代码

#include "CppSQLite.h"
#include <ctime>
#include <iostream>

using namespace std;

const char* gszFile = "C:\\test.db";

int main(int argc, char** argv)
{
    try
    {
        int i, fld;
        time_t tmStart, tmEnd;
        CppSQLiteDB db;

        cout << "SQLite Version: " << db.SQLiteVersion() << endl;

        remove(gszFile);
        db.open(gszFile);

        cout << endl << "Creating emp table" << endl;
        db.execDML("create table emp(empno int, empname char(20));");
        ///
        // Execute some DML, and print number of rows affected by each one
        ///
        cout << endl << "DML tests" << endl;
        int nRows = db.execDML("insert into emp values (7, 'David Beckham');");
        cout << nRows << " rows inserted" << endl;

        nRows = db.execDML(
         "update emp set empname = 'Christiano Ronaldo' where empno = 7;");
        cout << nRows << " rows updated" << endl;

        nRows = db.execDML("delete from emp where empno = 7;");
        cout << nRows << " rows deleted" << endl;

        /////
        // Transaction Demo
        // The transaction could just as easily have been rolled back
        /////
        int nRowsToCreate(50000);
        cout << endl << "Transaction test, creating " << nRowsToCreate;
        cout << " rows please wait..." << endl;
        tmStart = time(0);
        db.execDML("begin transaction;");

        for (i = 0; i < nRowsToCreate; i++)
        {
            char buf[128];
            sprintf(buf, "insert into emp values (%d, 'Empname%06d');", i, i);
            db.execDML(buf);
        }

        db.execDML("commit transaction;");
        tmEnd = time(0);

        ////
        // Demonstrate CppSQLiteDB::execScalar()
        ////
        cout << db.execScalar("select count(*) from emp;") 
               << " rows in emp table in ";
        cout << tmEnd-tmStart << " seconds (that was fast!)" << endl;

        ////
        // Re-create emp table with auto-increment field
        ////
        cout << endl << "Auto increment test" << endl;
        db.execDML("drop table emp;");
        db.execDML(
         "create table emp(empno integer primary key, empname char(20));");
        cout << nRows << " rows deleted" << endl;

        for (i = 0; i < 5; i++)
        {
            char buf[128];
            sprintf(buf, 
       "insert into emp (empname) values ('Empname%06d');", i+1);
            db.execDML(buf);
            cout << " primary key: " << db.lastRowId() << endl;
        }

     ///
     // Query data and also show results of inserts into auto-increment field
     //////
        cout << endl << "Select statement test" << endl;
        CppSQLiteQuery q = db.execQuery("select * from emp order by 1;");

        for (fld = 0; fld < q.numFields(); fld++)
        {
            cout << q.fieldName(fld) << "(" << q.fieldType(fld) << ")|";
        }
        cout << endl;

        while (!q.eof())
        {
            cout << q.fieldValue(0) << "|";
            cout << q.fieldValue(1) << "|" << endl;
            q.nextRow();
        }

        ///
        // SQLite's printf() functionality. Handles embedded quotes and NULLs
        ////
        cout << endl << "SQLite sprintf test" << endl;
        CppSQLiteBuffer bufSQL;
        bufSQL.format("insert into emp (empname) values (%Q);", "He's bad");
        cout << (const char*)bufSQL << endl;
        db.execDML(bufSQL);

        bufSQL.format("insert into emp (empname) values (%Q);", NULL);
        cout << (const char*)bufSQL << endl;
        db.execDML(bufSQL);

        ////
        // Fetch table at once, and also show how to 
        // use CppSQLiteTable::setRow() method
        //////
        cout << endl << "getTable() test" << endl;
        CppSQLiteTable t = db.getTable("select * from emp order by 1;");

        for (fld = 0; fld < t.numFields(); fld++)
        {
            cout << t.fieldName(fld) << "|";
        }
        cout << endl;
        for (int row = 0; row < t.numRows(); row++)
        {
            t.setRow(row);
            for (int fld = 0; fld < t.numFields(); fld++)
            {
                if (!t.fieldIsNull(fld))
                    cout << t.fieldValue(fld) << "|";
                else
                    cout << "NULL" << "|";
            }
            cout << endl;
        }

        ////
        // Test CppSQLiteBinary by storing/retrieving some binary data, checking
        // it afterwards to make sure it is the same
        //////
        cout << endl << "Binary data test" << endl;
        db.execDML("create table bindata(desc char(10), data blob);");
        
        unsigned char bin[256];
        CppSQLiteBinary blob;

        for (i = 0; i < sizeof bin; i++)
        {
            bin[i] = i;
        }

        blob.setBinary(bin, sizeof bin);

        bufSQL.format("insert into bindata values ('testing', %Q);", 
                      blob.getEncoded());
        db.execDML(bufSQL);
        cout << "Stored binary Length: " << sizeof bin << endl;

        q = db.execQuery("select data from bindata where desc = 'testing';");

        if (!q.eof())
        {
            blob.setEncoded((unsigned char*)q.fieldValue("data"));
            cout << "Retrieved binary Length: " 
       << blob.getBinaryLength() << endl;
        }

        const unsigned char* pbin = blob.getBinary();
        for (i = 0; i < sizeof bin; i++)
        {
            if (pbin[i] != i)
            {
                cout << "Problem: i: ," << i << " bin[i]: " 
             << pbin[i] << endl;
            }
        }

        /////
        // Pre-compiled Statements Demo
        /////
        cout << endl << "Transaction test, creating " << nRowsToCreate;
        cout << " rows please wait..." << endl;
        db.execDML("drop table emp;");
        db.execDML("create table emp(empno int, empname char(20));");
        tmStart = time(0);
        db.execDML("begin transaction;");

        CppSQLiteStatement stmt = db.compileStatement(
            "insert into emp values (?, ?);");
        for (i = 0; i < nRowsToCreate; i++)
        {
            char buf[16];
            sprintf(buf, "EmpName%06d", i);
            stmt.bind(1, i);
            stmt.bind(2, buf);
            stmt.execDML();
            stmt.reset();
        }

        db.execDML("commit transaction;");
        tmEnd = time(0);

        cout << db.execScalar("select count(*) from emp;") 
           << " rows in emp table in ";
        cout << tmEnd-tmStart << " seconds (that was even faster!)" << endl;
        cout << endl << "End of tests" << endl;
    }
    catch (CppSQLiteException& e)
    {
        cerr << e.errorCode() << ":" << e.errorMessage() << endl;
    }

    ////
    // Loop until user enters q or Q
    ///
    char c(' ');

    while (c != 'q' && c != 'Q')
    {
        cout << "Press q then enter to quit: ";
        cin >> c;
    }
    return 0;
} "CppSQLite.h"
#include <ctime>
#include <iostream>

using namespace std;

const char* gszFile = "C:\\test.db";

int main(int argc, char** argv)
{
    try
    {
        int i, fld;
        time_t tmStart, tmEnd;
        CppSQLiteDB db;

        cout << "SQLite Version: " << db.SQLiteVersion() << endl;

        remove(gszFile);
        db.open(gszFile);

        cout << endl << "Creating emp table" << endl;
        db.execDML("create table emp(empno int, empname char(20));");
        ///
        // Execute some DML, and print number of rows affected by each one
        ///
        cout << endl << "DML tests" << endl;
        int nRows = db.execDML("insert into emp values (7, 'David Beckham');");
        cout << nRows << " rows inserted" << endl;

        nRows = db.execDML(
         "update emp set empname = 'Christiano Ronaldo' where empno = 7;");
        cout << nRows << " rows updated" << endl;

        nRows = db.execDML("delete from emp where empno = 7;");
        cout << nRows << " rows deleted" << endl;

        /////
        // Transaction Demo
        // The transaction could just as easily have been rolled back
        /////
        int nRowsToCreate(50000);
        cout << endl << "Transaction test, creating " << nRowsToCreate;
        cout << " rows please wait..." << endl;
        tmStart = time(0);
        db.execDML("begin transaction;");

        for (i = 0; i < nRowsToCreate; i++)
        {
            char buf[128];
            sprintf(buf, "insert into emp values (%d, 'Empname%06d');", i, i);
            db.execDML(buf);
        }

        db.execDML("commit transaction;");
        tmEnd = time(0);

        ////
        // Demonstrate CppSQLiteDB::execScalar()
        ////
        cout << db.execScalar("select count(*) from emp;") 
               << " rows in emp table in ";
        cout << tmEnd-tmStart << " seconds (that was fast!)" << endl;

        ////
        // Re-create emp table with auto-increment field
        ////
        cout << endl << "Auto increment test" << endl;
        db.execDML("drop table emp;");
        db.execDML(
         "create table emp(empno integer primary key, empname char(20));");
        cout << nRows << " rows deleted" << endl;

        for (i = 0; i < 5; i++)
        {
            char buf[128];
            sprintf(buf, 
       "insert into emp (empname) values ('Empname%06d');", i+1);
            db.execDML(buf);
            cout << " primary key: " << db.lastRowId() << endl;
        }

     ///
     // Query data and also show results of inserts into auto-increment field
     //////
        cout << endl << "Select statement test" << endl;
        CppSQLiteQuery q = db.execQuery("select * from emp order by 1;");

        for (fld = 0; fld < q.numFields(); fld++)
        {
            cout << q.fieldName(fld) << "(" << q.fieldType(fld) << ")|";
        }
        cout << endl;

        while (!q.eof())
        {
            cout << q.fieldValue(0) << "|";
            cout << q.fieldValue(1) << "|" << endl;
            q.nextRow();
        }

        ///
        // SQLite's printf() functionality. Handles embedded quotes and NULLs
        ////
        cout << endl << "SQLite sprintf test" << endl;
        CppSQLiteBuffer bufSQL;
        bufSQL.format("insert into emp (empname) values (%Q);", "He's bad");
        cout << (const char*)bufSQL << endl;
        db.execDML(bufSQL);

        bufSQL.format("insert into emp (empname) values (%Q);", NULL);
        cout << (const char*)bufSQL << endl;
        db.execDML(bufSQL);

        ////
        // Fetch table at once, and also show how to 
        // use CppSQLiteTable::setRow() method
        //////
        cout << endl << "getTable() test" << endl;
        CppSQLiteTable t = db.getTable("select * from emp order by 1;");

        for (fld = 0; fld < t.numFields(); fld++)
        {
            cout << t.fieldName(fld) << "|";
        }
        cout << endl;
        for (int row = 0; row < t.numRows(); row++)
        {
            t.setRow(row);
            for (int fld = 0; fld < t.numFields(); fld++)
            {
                if (!t.fieldIsNull(fld))
                    cout << t.fieldValue(fld) << "|";
                else
                    cout << "NULL" << "|";
            }
            cout << endl;
        }

        ////
        // Test CppSQLiteBinary by storing/retrieving some binary data, checking
        // it afterwards to make sure it is the same
        //////
        cout << endl << "Binary data test" << endl;
        db.execDML("create table bindata(desc char(10), data blob);");
        
        unsigned char bin[256];
        CppSQLiteBinary blob;

        for (i = 0; i < sizeof bin; i++)
        {
            bin[i] = i;
        }

        blob.setBinary(bin, sizeof bin);

        bufSQL.format("insert into bindata values ('testing', %Q);", 
                      blob.getEncoded());
        db.execDML(bufSQL);
        cout << "Stored binary Length: " << sizeof bin << endl;

        q = db.execQuery("select data from bindata where desc = 'testing';");

        if (!q.eof())
        {
            blob.setEncoded((unsigned char*)q.fieldValue("data"));
            cout << "Retrieved binary Length: " 
       << blob.getBinaryLength() << endl;
        }

        const unsigned char* pbin = blob.getBinary();
        for (i = 0; i < sizeof bin; i++)
        {
            if (pbin[i] != i)
            {
                cout << "Problem: i: ," << i << " bin[i]: " 
             << pbin[i] << endl;
            }
        }

        /////
        // Pre-compiled Statements Demo
        /////
        cout << endl << "Transaction test, creating " << nRowsToCreate;
        cout << " rows please wait..." << endl;
        db.execDML("drop table emp;");
        db.execDML("create table emp(empno int, empname char(20));");
        tmStart = time(0);
        db.execDML("begin transaction;");

        CppSQLiteStatement stmt = db.compileStatement(
            "insert into emp values (?, ?);");
        for (i = 0; i < nRowsToCreate; i++)
        {
            char buf[16];
            sprintf(buf, "EmpName%06d", i);
            stmt.bind(1, i);
            stmt.bind(2, buf);
            stmt.execDML();
            stmt.reset();
        }

        db.execDML("commit transaction;");
        tmEnd = time(0);

        cout << db.execScalar("select count(*) from emp;") 
           << " rows in emp table in ";
        cout << tmEnd-tmStart << " seconds (that was even faster!)" << endl;
        cout << endl << "End of tests" << endl;
    }
    catch (CppSQLiteException& e)
    {
        cerr << e.errorCode() << ":" << e.errorMessage() << endl;
    }

    ////
    // Loop until user enters q or Q
    ///
    char c(' ');

    while (c != 'q' && c != 'Q')
    {
        cout << "Press q then enter to quit: ";
        cin >> c;
    }
    return 0;
}

CppSQLite类

定义了下面的简单类来封装SQLite的功能。

所有的CppSQLite类都包含在2个文件CppSQLite.h和CppSQLite.cpp中,这些文件将需要添加到您的应用程序中。

CppSQLiteException

封装SQLite错误代码和消息。这里没什么复杂的,如果需要的话,这个类可以很容易的被合并到一个现有的异常层次结构中

SQLite返回的错误消息需要sqlite_freemem()由程序员来完成,而这个类承担了这个责任。请注意,对于由此生成的错误消息CppSQLite,我们不想释放内存,所以有一个可选的结尾参数指示是否CppSQLiteException释放内存。

隐藏   复制代码

class CppSQLiteException
{
public:

    CppSQLiteException(const int nErrCode,
                    char* szErrMess,
                    bool bDeleteMsg=true);

    CppSQLiteException(const CppSQLiteException&  e);

    virtual ~CppSQLiteException();

    const int errorCode() { return mnErrCode; }

    const char* errorMessage() { return mpszErrMess; }

    static const char* errorCodeAsString(int nErrCode);

private:

    int mnErrCode;
    char* mpszErrMess;
}; CppSQLiteException
{
public:

    CppSQLiteException(const int nErrCode,
                    char* szErrMess,
                    bool bDeleteMsg=true);

    CppSQLiteException(const CppSQLiteException&  e);

    virtual ~CppSQLiteException();

    const int errorCode() { return mnErrCode; }

    const char* errorMessage() { return mpszErrMess; }

    static const char* errorCodeAsString(int nErrCode);

private:

    int mnErrCode;
    char* mpszErrMess;
};

CppSQLiteDB

封装一个SQLite数据库文件。

隐藏   缩小    复制代码

class CppSQLiteDB
{
public:

    enum CppSQLiteDBOpenMode
    {
        openExisting,
        createNew,
        openOrCreate
    };

    CppSQLiteDB();

    virtual ~CppSQLiteDB();

    void open(const char* szFile);

    void close();

    int execDML(const char* szSQL);

    CppSQLiteQuery execQuery(const char* szSQL);

    int execScalar(const char* szSQL);

    CppSQLiteTable getTable(const char* szSQL);

    CppSQLiteStatement compileStatement(const char* szSQL);

    int lastRowId();

    void interrupt() { sqlite_interrupt(mpDB); }

    void setBusyTimeout(int nMillisecs);

    static const char* SQLiteVersion() { return SQLITE_VERSION; }

private:

    CppSQLiteDB(const CppSQLiteDB& db);
    CppSQLiteDB& operator=(const CppSQLiteDB& db);

    sqlite_vm* compile(const char* szSQL);

    void checkDB();

    sqlite* mpDB;
    int mnBusyTimeoutMs;
}; CppSQLiteDB
{
public:

    enum CppSQLiteDBOpenMode
    {
        openExisting,
        createNew,
        openOrCreate
    };

    CppSQLiteDB();

    virtual ~CppSQLiteDB();

    void open(const char* szFile);

    void close();

    int execDML(const char* szSQL);

    CppSQLiteQuery execQuery(const char* szSQL);

    int execScalar(const char* szSQL);

    CppSQLiteTable getTable(const char* szSQL);

    CppSQLiteStatement compileStatement(const char* szSQL);

    int lastRowId();

    void interrupt() { sqlite_interrupt(mpDB); }

    void setBusyTimeout(int nMillisecs);

    static const char* SQLiteVersion() { return SQLITE_VERSION; }

private:

    CppSQLiteDB(const CppSQLiteDB& db);
    CppSQLiteDB& operator=(const CppSQLiteDB& db);

    sqlite_vm* compile(const char* szSQL);

    void checkDB();

    sqlite* mpDB;
    int mnBusyTimeoutMs;
};

open()close()方法是自我解释。SQLite确实提供了一个mode参数,sqlite_open()但这被记录为没有效果,所以不提供CppSQLite

execDML()用于执行数据操纵语言(DML)命令,如createdropinsertupdatedelete语句。它返回受影响的行数。以分号分隔的多个SQL语句可以一次提交和执行。注意: CppSQLite返回受影响的行数有一个潜在的问题。如果还有其他未完成的()操作正在进行,受影响的行数将是累积的,并包括前面的语句。所以,如果这个功能对你很重要,你必须确保任何CppSQLiteQueryCppSQLiteStatement尚未破坏的对象已经finalize()要求他们在你面前execDML()

execQuery()用于执行查询。该CppSQLiteQuery对象是由值返回,因为这释放了程序员不必删除它。

execScalar()是我从ADO.NET获得的一个想法。当需要运行一个简单的聚合函数时,这是一个捷径,例如“ select count(*) from emp”或“ select max(empno) from emp”。它返回查询结果第一行中第一个字段的值。其他列和行被忽略。

getTable()允许SQLite功能,可以在一个单一的操作中获取整个表,而不必像查询一样一次获取一行。实际上,可以通过用一个where子句指定一个查询来获取表行子集,但是整个结果集是一次返回的。同样,CppSQLiteTable为了方便起见,该对象被返回值。

compileStatement()允许实验性的SQLite预编译的SQL功能。见CppSQLiteStatement下文。

SQLite是无类型的,这意味着所有的字段被存储为字符串。一个例外是INTEGER PRIMARY KEY类型,它允许一个自动增量字段,很像SQL Server的标识列。该lastRowId()函数用于确定插入的最后一行的主键值。

interrupt() 在多线程中是有用的,并且允许一个线程中断正在进行的另一个线程上的操作。

setBusyTimeout()在多线程时也是有用的,并且允许程序员在返回之前等待SQLite等待多长时间,SQLITE_BUSY如果另一个线程锁定了数据库。默认值是60秒,在打开数据库时设置。

复制构造函数operator=()是私有的,因为复制CppSQLiteDB对象是没有意义的。

最后,静态方法SQLiteVersion()返回底层SQLite DLL的版本号。

CppSQLiteQuery

封装一个SQLite查询结果集。

隐藏   缩小    复制代码

class CppSQLiteQuery
{
public:

    CppSQLiteQuery();

    CppSQLiteQuery(const CppSQLiteQuery& rQuery);

    CppSQLiteQuery(sqlite_vm* pVM,
                bool bEof,
                int nCols,
                const char** paszValues,
                const char** paszColNames,
                bool bOwnVM=true);

    CppSQLiteQuery& operator=(const CppSQLiteQuery& rQuery);

    virtual ~CppSQLiteQuery();

    int numFields();

    const char* fieldName(int nCol);

    const char* fieldType(int nCol);

    const char* fieldValue(int nField);
    const char* fieldValue(const char* szField);

    int getIntField(int nField, int nNullValue=0);
    int getIntField(const char* szField, int nNullValue=0);

    double getFloatField(int nField, double fNullValue=0.0);
    double getFloatField(const char* szField, double fNullValue=0.0);

    const char* getStringField(int nField, const char* szNullValue="");
    const char* getStringField(const char* szField, 
          const char* szNullValue="");

    bool fieldIsNull(int nField);
    bool fieldIsNull(const char* szField);

    bool eof();

    void nextRow();

    void finalize();

private:

    void checkVM();

    sqlite_vm* mpVM;
    bool mbEof;
    int mnCols;
    const char** mpaszValues;
    const char** mpaszColNames;
    bool mbOwnVM;
}; CppSQLiteQuery
{
public:

    CppSQLiteQuery();

    CppSQLiteQuery(const CppSQLiteQuery& rQuery);

    CppSQLiteQuery(sqlite_vm* pVM,
                bool bEof,
                int nCols,
                const char** paszValues,
                const char** paszColNames,
                bool bOwnVM=true);

    CppSQLiteQuery& operator=(const CppSQLiteQuery& rQuery);

    virtual ~CppSQLiteQuery();

    int numFields();

    const char* fieldName(int nCol);

    const char* fieldType(int nCol);

    const char* fieldValue(int nField);
    const char* fieldValue(const char* szField);

    int getIntField(int nField, int nNullValue=0);
    int getIntField(const char* szField, int nNullValue=0);

    double getFloatField(int nField, double fNullValue=0.0);
    double getFloatField(const char* szField, double fNullValue=0.0);

    const char* getStringField(int nField, const char* szNullValue="");
    const char* getStringField(const char* szField, 
          const char* szNullValue="");

    bool fieldIsNull(int nField);
    bool fieldIsNull(const char* szField);

    bool eof();

    void nextRow();

    void finalize();

private:

    void checkVM();

    sqlite_vm* mpVM;
    bool mbEof;
    int mnCols;
    const char** mpaszValues;
    const char** mpaszColNames;
    bool mbOwnVM;
};

nextRow()eof()允许迭代查询结果。

numFields()fieldValue()fieldName()fieldType()fieldIsNull()允许程序员确定领域,它们的名称,值类型的数量,以及是否包含SQL NULL。有重载的版本允许必要的字段由索引或名称指定。

getIntField(),getFloatField()getStringField()提供一个稍微简单的程序获取字段值的方法,永远不会返回SQL的NULL指针,NULL,并有一个默认的第二个参数,允许程序员指定返回的值。

在结果中不可能迭代。原因是这CppSQLite是一个薄包装,并不缓存任何返回的行数据。如果这是必需的,CppSQLiteDB::getTable()应该使用,或者应用程序可以继承这个类。

finalize() 释放与查询相关联的内存,但析构函数会自动调用它。

CppSQLiteTable

SQLite提供了一种方法来获取一个完整的表的内容在一块内存中,CppSQLiteTable封装了这个功能。

隐藏   缩小    复制代码

class CppSQLiteTable
{
public:

    CppSQLiteTable();

    CppSQLiteTable(const CppSQLiteTable& rTable);

    CppSQLiteTable(char** paszResults, int nRows, int nCols);

    virtual ~CppSQLiteTable();

    CppSQLiteTable& operator=(const CppSQLiteTable& rTable);

    int numFields();

    int numRows();

    const char* fieldName(int nCol);

    const char* fieldValue(int nField);
    const char* fieldValue(const char* szField);

    int getIntField(int nField, int nNullValue=0);
    int getIntField(const char* szField, int nNullValue=0);

    double getFloatField(int nField, double fNullValue=0.0);
    double getFloatField(const char* szField, double fNullValue=0.0);

    const char* getStringField(int nField, const char* szNullValue="");
    const char* getStringField(const char* szField, const char* szNullValue="");

    bool fieldIsNull(int nField);
    bool fieldIsNull(const char* szField);

    void setRow(int nRow);

    void finalize();

private:

    void checkResults();

    int mnCols;
    int mnRows;
    int mnCurrentRow;
    char** mpaszResults;
}; CppSQLiteTable
{
public:

    CppSQLiteTable();

    CppSQLiteTable(const CppSQLiteTable& rTable);

    CppSQLiteTable(char** paszResults, int nRows, int nCols);

    virtual ~CppSQLiteTable();

    CppSQLiteTable& operator=(const CppSQLiteTable& rTable);

    int numFields();

    int numRows();

    const char* fieldName(int nCol);

    const char* fieldValue(int nField);
    const char* fieldValue(const char* szField);

    int getIntField(int nField, int nNullValue=0);
    int getIntField(const char* szField, int nNullValue=0);

    double getFloatField(int nField, double fNullValue=0.0);
    double getFloatField(const char* szField, double fNullValue=0.0);

    const char* getStringField(int nField, const char* szNullValue="");
    const char* getStringField(const char* szField, const char* szNullValue="");

    bool fieldIsNull(int nField);
    bool fieldIsNull(const char* szField);

    void setRow(int nRow);

    void finalize();

private:

    void checkResults();

    int mnCols;
    int mnRows;
    int mnCurrentRow;
    char** mpaszResults;
};

setRow()为行之间的移动提供了随机访问方法,并且可以结合使用numRows()来迭代表格。这种设计决定是为了简单制作,是继同型号的CppSQLiteQuery,将需要功能bof()eof()first()last()next()prev()

numFields()fieldValue()fieldName()fieldIsNull()getIntField()getFloatField()getStringField()close(),和operator=()提供相同的功能为CppSQLiteQuery

CppSQLiteBuffer

封装SQLite“ sprintf”功能。

SQLite的提供了一个函数sqlite_mprintf(),其是像C运行sprintf()除了没有超速缓存提供,如的可能性sqlite_mprintf()使用malloc分配足够的内存。另一个好处sprintf()就是%Q标签,它的工作方式%s除了它将按照撇号,使它们不会混淆正在构建的SQL字符串,并且还将NULL指针转换为SQL NULL值。

隐藏   复制代码

class CppSQLiteBuffer
{
public:

    CppSQLiteBuffer();

    ~CppSQLiteBuffer();

    const char* format(const char* szFormat, ...);

    operator const char*() { return mpBuf; }

    void clear();

private:

    char* mpBuf;
}; CppSQLiteBuffer
{
public:

    CppSQLiteBuffer();

    ~CppSQLiteBuffer();

    const char* format(const char* szFormat, ...);

    operator const char*() { return mpBuf; }

    void clear();

private:

    char* mpBuf;
};

operator const char*()允许程序员将此对象的实例传递给定义的函数CppSQLiteDB

CppSQLiteBinary

由于SQLite将所有数据存储为已NULL终止的字符串,因此如果已嵌入二进制数据,则无法存储二进制数据NULL。SQLite的提供2个功能sqlite_encode_binary()sqlite_decode_binary()可以被用于允许二进制数据的存储和检索。CppSQLiteBinary封装了这两个功能。

这两个函数当前不是作为预编译DLL的一部分提供的,所以我已经将SQLite的encode.c文件的全部内容复制到CppSQLite.cpp文件中。如果这些函数在未来某个时间点在DLL中提供,可以很容易地从CppSQLite.cpp中删除。

隐藏   复制代码

class CppSQLiteBinary
{
public:

    CppSQLiteBinary();

    ~CppSQLiteBinary();

    void setBinary(const unsigned char* pBuf, int nLen);
    void setEncoded(const unsigned char* pBuf);

    const unsigned char* getEncoded();
    const unsigned char* getBinary();

    int getBinaryLength();

    unsigned char* allocBuffer(int nLen);

    void clear();

private:

    unsigned char* mpBuf;
    int mnBinaryLen;
    int mnBufferLen;
    int mnEncodedLen;
    bool mbEncoded;
}; CppSQLiteBinary
{
public:

    CppSQLiteBinary();

    ~CppSQLiteBinary();

    void setBinary(const unsigned char* pBuf, int nLen);
    void setEncoded(const unsigned char* pBuf);

    const unsigned char* getEncoded();
    const unsigned char* getBinary();

    int getBinaryLength();

    unsigned char* allocBuffer(int nLen);

    void clear();

private:

    unsigned char* mpBuf;
    int mnBinaryLen;
    int mnBufferLen;
    int mnEncodedLen;
    bool mbEncoded;
};

CppSQLiteBinary可以使用setEncoded()setBinary()函数接受编码或二进制形式的数据。无论使用哪种方式,总是会分配足够的内存来存储编码版本,这通常会更长,因为nulls和单引号必须转义。

使用getEncoded()getBinary()功能检索数据。根据数据当前在班级中的哪种形式,可能需要转换。

getBinaryLength() 返回存储的二进制数据的长度,如果需要,再次将保存的格式从编码转换为二进制。

allocBuffer()可以用来防止数据必须通过临时缓冲区循环,如本文开始时的示例代码。这个函数可以用在下面的例子中,其中数据从文件直接读入CppSQLiteBinary对象。

隐藏   复制代码

int f = open(gszJpgFile, O_RDONLY|O_BINARY);
int nFileLen = filelength(f);
read(f, blob.allocBuffer(nFileLen), nFileLen); f = open(gszJpgFile, O_RDONLY|O_BINARY);
int nFileLen = filelength(f);
read(f, blob.allocBuffer(nFileLen), nFileLen);

CppSQLiteStatement

SQLite为使用预编译的SQL提供了一些实验功能。当不同的值一次又一次地执行相同的SQL时,只需编译一次SQL并多次执行,每次使用不同的值,就可以显着提高性能。CppSQLiteStatement封装了这个功能。

隐藏   缩小    复制代码

class CppSQLiteStatement
{
public:

    CppSQLiteStatement();

    CppSQLiteStatement(const CppSQLiteStatement& rStatement);

    CppSQLiteStatement(sqlite* pDB, sqlite_vm* pVM);

    virtual ~CppSQLiteStatement();

    CppSQLiteStatement& operator=(const CppSQLiteStatement& rStatement);

    int execDML();

    CppSQLiteQuery execQuery();

    void bind(int nParam, const char* szValue);
    void bind(int nParam, const int nValue);
    void bind(int nParam, const double dwValue);
    void bindNull(int nParam);

    void reset();

    void finalize();

private:

    void checkDB();
    void checkVM();

    sqlite* mpDB;
    sqlite_vm* mpVM;
}; CppSQLiteStatement
{
public:

    CppSQLiteStatement();

    CppSQLiteStatement(const CppSQLiteStatement& rStatement);

    CppSQLiteStatement(sqlite* pDB, sqlite_vm* pVM);

    virtual ~CppSQLiteStatement();

    CppSQLiteStatement& operator=(const CppSQLiteStatement& rStatement);

    int execDML();

    CppSQLiteQuery execQuery();

    void bind(int nParam, const char* szValue);
    void bind(int nParam, const int nValue);
    void bind(int nParam, const double dwValue);
    void bindNull(int nParam);

    void reset();

    void finalize();

private:

    void checkDB();
    void checkVM();

    sqlite* mpDB;
    sqlite_vm* mpVM;
};

使用包含占位符的SQL语句CppSQLiteStatement调用对象CppSQLiteDB::compileStatement(),如下所示:

隐藏   复制代码

CppSQLiteStatement stmt = db.compileStatement("insert into emp values (?, ?);");
stmt.bind(1, 1);
stmt.bind(2, "Emp Name");
stmt.execDML();
stmt.reset();"insert into emp values (?, ?);");
stmt.bind(1, 1);
stmt.bind(2, "Emp Name");
stmt.execDML();
stmt.reset();

CppSQLiteStatement::bind()然后在调用execDML()execQuery()适当的方法之前,方法用于设置占位符的值。

程序员完成任一execDML()或两者的结果后execQuery()reset()可以调用该方法将语句恢复为编译状态。CppSQLiteStatement::bind()然后可以再次使用这些方法,其次是execDML()execQuery()。一个典型的用法是在CppSQLiteDemo程序中演示的循环中。

多线程

SQLite在默认情况下在Windows上被编译为线程安全的,并且CppSQLite使用一些SQLite特性来帮助多线程使用。本文附带的源代码中包含一个名为CppSQLiteDemoMT的第二个演示程序,演示了这些功能。

每个希望在同一个数据库文件上同时使用CppSQLite的线程都必须有自己的CppSQLiteDB对象和调用open()。换句话说,多个线程同时调用一个CppSQLiteDB对象是一个错误。一个例外是CppSQLiteDB::interrupt(),可以从一个线程使用中断另一个线程的工作。

CppSQLite多线程使用的另一个变化是使用sqlite_busy_timeout()导致SQLite在返回之前等待指定的毫秒数的函数SQLITE_BUSY。默认情况下,CppSQLite将其设置为60,000(60秒),但可以CppSQLiteDB::setBusyTimeout()根据需要进行更改。在CppSQLiteDemoMT程序中显示了这样做的各种示例。

SQLite功能目前没有包装

SQLite提供了一种机制,允许应用程序开发人员定义可以从SQL语句中调用的存储过程和聚合函数。这些存储过程由应用程序开发人员用C写成,并通过函数指针让SQLite知道。这就是SQLite构建函数的SQL如何实现的,但是这个功能目前还不能满足CppSQLite

SQLite在封装的函数上提供了一些其他的变体,鼓励读者学习SQLite文档。

托管C ++

可以将SQLite和CppSQLite编译为托管的C ++程序It Just Works(IJW)。您将需要设置CppSQLite.cpp文件,以便它不使用预编译头,也不使用托管扩展,即不使用/ clr。

CppSQLite下载包含一个托管C ++演示。

SQLite版本3

在撰写本文时,SQLite版本3正在测试中。有关更多详细信息,请参阅http://www.sqlite.org/。我已经创建了一个CppSQLiteSQLite版本3 的端口,下面的注释解释了这些差异。

有一组新的类的前缀CppSQLite3,例如CppSQLite3Exception。这允许程序CppSQLite与两个版本的SQLite 链接,就像SQLite本身的两个版本一样。

最初并不支持UTF-16,因为这不是我所经历的,也不知道如何测试。这可以在以后用另一组类,称为例如添加CppSQLite3Exception16等需要注意的是sqlite3的一些东西,如sqlite3_exec()sqlite3_get_table()不似乎有UTF-16版本中,还sqlite3_vmprintf(),使用CppSQLiteBuffer

错误消息现在返回sqlite3_errmsg()并不需要被释放。为了保持一致性,抛出异常的代码CppSQLiteCppSQLite3从SQLite版本3返回的消息已经被改变了,所以它传递了DONT_DELETE_MSG作为最后一个参数CppSQLite3Exception。这个异常是sqlite3_exec()和返回的消息sqlite3_get_table()

SQLite版本3现在直接支持BLOB数据,因此不需要对它进行编码或解码,而且似乎没有工作CppSQLiteBinary。但是,SQLite版本3的变化意味着处理BLOB数据的唯一方法似乎是使用准备好的语句(CppSQLiteStatement)。不是一个真正的问题,但到现在为止,CppSQLiteBinary已经允许使用的呼叫(编码)的二进制数据CppSQLiteDB::execQuery()CppSQLiteDB::execDML()并从返回的数据CppSQLiteDB::getTable()

sqlite_encode_binary()sqlite_decode_binary()仍然包含在SQLite版本3源代码发行版中,虽然不清楚这是否是错误,因为它们没有sqlite3前缀,也不是从DLL中导出。CppSQLite3复制源到这两个函数。在CppSQlite up1.3版之前的版本中,SQLite的版本是2.8.15,它们不是从DLL中导出的。CppSQLite3Binary是完全相同的副本CppSQLiteBinary,与源捆绑sqlite_encode_binary()sqlite_decode_binary()。这将允许CppSQLite和之间轻松移植CppSQLite3。希望使用sqlite3 BLOB和其减少的存储空间的程序将不需要使用CppSQLite3Binary,并且无论如何都需要重写。

SQLite版本3引入了对所使用的数据分类系统的更改。请参阅http://www.sqlite.org/datatype3.html。由于这个原因,CppSQLiteQuery::FieldType()已被2个函数替换:CppSQLiteQuery::FieldDeclType()它将字段的声明数据类型作为字符串CppSQLiteQuery::FieldDataType()返回,并且返回当前行中存储在该列中的数据的实际类型,作为SQLite版本3#定义的价值。

演示程序已经稍微改变,以演示新功能,并解释SQLite版本3的不同锁定行为。请参阅http://www.sqlite.org/lockingv3.html。请注意,SQLite版本3.0.5引入了一个编译时间选项,用于更改锁定行为,请参阅http://www.sqlite.org/changes.html以获取更多详细信息。

SQLite版本3作为本文顶部的单独下载提供。

未来的工作

我可以添加对剩下的SQLite功能的支持CppSQLite。目前,这意味着存储过程和聚合函数。

跨平台能力

自从1.2版以来CppSQLite,我一直努力不去做任何与微软相关的事情,并且已经成功编译并运行了mingw32以及Visual C ++ 的演示程序。

由于mingw32基于GCC,在Linux / Unix上应该没有大的问题,虽然多线程演示程序CppSQLiteDemoMT使用这个_beginthread()调用,这显然不起作用。这可能很容易解决,例如使用pthreads。

捐款

感谢代码项目成员的建议和修补程序CppSQLite,也感谢Mateusz Loskot担任审阅者。

结论

CppSQLite 使得SQLite更容易在C ++程序中使用,但不会比平面C接口提供更低的功耗或效率。

如果没有别的,写作CppSQLite为作者提供了对SQLite的力量和简单性的洞察。希望本文的读者也能从中受益。

CppSQLite的历史(目标SQLite 2.8.n)

  • 1.0 - 2004年3月3日 - 初始版本。
  • 1.1 - 2004年3月10日
    • 重命名CppSQLiteException::errorMess()CppSQLiteException::errorMessage()
    • 第二个构造函数CppSQLiteException()
    • 现在将错误代码解码为字符串CppSQLiteException
    • sqlite_finalize()遇到问题后立即致电以获取错误详细信息sqlite_step()
    • 新增CppSQLiteBinary课程
  • 1.2 - 2004年4月2日 - 未发布。
    • 更新文章。
    • 删除了使用微软特定的扩展(我希望)
    • 检查NULL指针
    • 更新了SQLite 2.8.13
    • 利用sqlite_busy_timeout()sqlite_interrupt()帮助多线程使用
    • 第二个多线程使用演示程序
    • 增加了预编译的SQL语句的支持
    • 添加了从中确定列类型的功能 CppSQLiteQuery
    • 添加 CppSQLiteDB::execScalar()
  • 2004年4月12日至12日
    • 更新后的文章审查
    • 在审查之后使用C ++而不是C标准头文件
  • 1.3 - 2004年5月21日
    • 添加“BSD样式”许可证通知源文件
    • 修正了bind()的bug
    • 新增getIntField()getStringField()getFloatField()
    • 增加了重载函数来按名称访问字段
    • CppSQLiteDB::ExecDML()与实施sqlite_exec()使多个语句可以一次执行。
    • 在关于潜在的回报价值问题的文章中增加注释 CppSQLiteDB::execDML()
    • 增加了托管的C ++示例程序
  • 2004年8月14日至30日
    • 升级到SQLite 2.8.15
    • 删除了sqlite_encode_binary()的源代码,sqlite_decode_binary()现在从SQLite DLL中导出
    • 添加了有关托管C ++的文章部分

CppSQLite3的历史(目标SQLite 3.nn)

  • 2004年8月3日至30日
    • 初始版本使用SQLite版本3.0.6
  • 2004年10月3.1 - 26日
    • 升级到vSQLite 3.0.8
    • 新增CppSQLiteDB3::tableExists()功能
    • 实现使用sqlite3的函数来代替,等。getXXXXFieldatoi()atof()
  • 2011年6月3.2日至24日
    • 与SQLite3版本3.4.0捆绑在一起
    • CppSQLite3DB::SQLiteHeaderVersion()CppSQLite3DB::SQLiteLibraryVersion()CppSQLite3DB::SQLiteLibraryVersionNumber()
    • 固定execScalar来处理NULL结果
    • 增加了Int64功能CppSQLite3Statement
    • 新增CppSQLite3DB::IsAutoCommitOn(),可用于测试交易是否有效
    • CppSQLite3DB::close()错误中抛出异常
    • 在以上例外陷阱 CppSQLite3DB::~CppSQLite3DB()
    • 表中较大的缓冲区大小为256
    • sqlite3_prepare 取而代之 sqlite3_prepare_v2
    • 修复CppSQLite3DB::compile()由Dave Rollins提供的
    • 按照Dave Rollins建议的名称绑定参数

 

原文地址:https://www.codeproject.com/Articles/6343/CppSQLite-C-Wrapper-for-SQLite

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值