SQLite 是一款开源的嵌入式数据库,由于本身小巧玲珑,比较适合作为应用软件的一部分嵌入到程序中,SQLite提供了一套简单易用的C API供应用程序调用, 但由于API的使用比较繁杂,并且考虑到将来移植到不同数据库的需求,应当避免直接使用SQLite API,应对其适当的封装,增加代码的灵活性。本文就项目中的使用经验提供了一种封装的方法。
1.API层的封装
由于SQLite支持ansi && unicode,为同时支持char, wchar_t,一些API同时提供了两个版本,如sqlite3_open,sqlite3_open16,另外一些API则只有一个版本,基于这两点实现了两个简单的类别:SQLite_APIBase和SQLite_APITraits,,SQLite_APIBase包含char, wchar_t无关的API,SQLite_APITraits模板从SQLite_APIBase派生,并针对char, wchar_t提供了两个特化版本。如下:
- struct SQLite_APIBase
- {
- static int Close(sqlite3* pDb)
- {
- return sqlite3_close(pDb);
- }
- static int Prepare(sqlite3* db, wchar_t const* pszSQL, int nBytes, sqlite3_stmt **ppStmt, const void** pzTail)
- {
- return sqlite3_prepare16(db, pszSQL, nBytes, ppStmt, pzTail);
- }
- static int Step(sqlite3_stmt* pStmt)
- {
- return sqlite3_step(pStmt);
- }
- static int Finalize(sqlite3_stmt *pStmt)
- {
- return sqlite3_finalize(pStmt);
- }
- //set operations
- static int BindInt(sqlite3_stmt *pStmt, int iIndex, int iValue)
- {
- return sqlite3_bind_int(pStmt, iIndex, iValue);
- }
- static int BindDouble(sqlite3_stmt *pStmt, int iIndex, double fValue)
- {
- return sqlite3_bind_double(pStmt, iIndex, fValue);
- }
- static int BindBlob(sqlite3_stmt *pStmt, int iIndex, void const* pData, int iSize)
- {
- return sqlite3_bind_blob(pStmt, iIndex, pData, iSize, NULL);
- }
- ....
- };
- template< class TChar >
- struct SQLite_APITraits;
- template<>
- struct SQLite_APITraits<char> : public SQLite_APIBase
- {
- static int Open( char const* filename, sqlite3** ppDb)
- {
- return sqlite3_open(filename, ppDb);
- }
- static int BindText(sqlite3_stmt *pStmt, int iIndex, char const* pText, int iSize)
- {
- return sqlite3_bind_text(pStmt, iIndex, pText, iSize, NULL);
- }
- static char const* ErrorMsg(sqlite3* pDB)
- {
- return sqlite3_errmsg(pDB);
- }
- ...........
- };
- template<>
- struct SQLite_APITraits<wchar_t> : public SQLite_APIBase
- {
- static int Open( wchar_t const* filename, sqlite3** ppDb)
- {
- return sqlite3_open16(filename, ppDb);
- }
- static int BindText(sqlite3_stmt *pStmt, int iIndex, wchar_t const* pText, int iSize)
- {
- return sqlite3_bind_text16(pStmt, iIndex, pText, iSize, NULL);
- }
- static wchar_t const* ErrorMsg(sqlite3* pDB)
- {
- return sqlite3_errmsg16(pDB);
- }
- ............
- };
这样以来,使用SQLite API时方便多了,不必针对char wchar_t调用不同的函数了,现在只需这样SQLite_APITraits<TCHAR>::some_func.
2.数据库层的封装
先来看一下数据库的接口
- class CDatabase
- {
- public:
- CDatabase();
- virtual ~CDatabase();
- public:
- virtual bool Open(TCHAR const* pszFileName) = 0;
- virtual bool Close() = 0;
- virtual bool Execute(TCHAR const* pszSQLStatement) = 0;
- };
CDatabase 提供了3个接口,Open打开数据库文件,Close关闭数据库,Execute执行制定的SQL语句.
这些接口的实现主要用到了下面的一些组件CDBConnection类,CDBStatement类,CResultSet类:
其中,CDBConnection负责管理数据库连接,CDBStatement负责运行SQL语句并生成执行结果,最终通过CResultSet查询结果。
为了使得这些组件能在不同的数据库间移植,需要把不同数据库的具体实现能分离出来,这里我用DBTraits模板来分离,为每种数据库实现一个特化版本,关于Traits用法,请参考侯捷老大一篇文章Traits: 類型的else-if-then機製.
(为了不影响代码的连贯性,DBTraits模板的代码附在最后)
下面是各个组件的实现,中间用到了iterator,类似于STL的迭代器但稍稍不同,你将会看到iterator将使得结果查询变得多么简单优雅
- template<class T, class TDBTraits = DBTraits<T>,