使用C++模板封装SQLite(完整版)
2008-11-24 18:36
627 查看
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>, bool t_Managed = true>
class CDBConnection
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
public:
CDBConnection():m_pStatement(NULL)
{
}
~CDBConnection()
{
if (m_pStatement != NULL)
{
delete m_pStatement;
}
if (t_Managed)
{
bool bRet = Close();
assert(bRet);
}
}
public:
bool Open(TCHAR const* pszFileName)
{
return TDBTraits::Open(pszFileName);
}
bool Close()
{
return TDBTraits::Close();
}
CDBStatement<T, TDBTraits >* FetchStatement()
{
if (m_pStatement == NULL)
{
m_pStatement = new CDBStatement<T, TDBTraits >;
}
assert(m_pStatement != NULL);
return m_pStatement;
}
private:
CDBStatement<T, TDBTraits >* m_pStatement;
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBStatement
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBRowSet<T, TDBTraits > CResultSet;
public:
CDBStatement():m_pHandle(NULL),m_pResult(NULL)
{
}
~CDBStatement()
{
if (m_pResult != NULL)
{
delete m_pResult;
}
}
public:
bool Execute(TCHAR const* pszSQLStatement)
{
Reset();
bool bRet = TDBTraits::Execute(pszSQLStatement);
if (!bRet)
{
return false;
}
m_pHandle = TDBTraits::GetStatementHandle();
if (TDBTraits::HasReturnValue())
{
m_pResult = new CResultSet();
assert(m_pResult != NULL);
}
return true;
}
template<class TValue>
bool BindValue(int iIndex, TValue Value, int iSize)
{
return TDBTraits::BindVariant(iIndex, Value, iSize);
}
CResultSet* FetchResult() const
{
return m_pResult;
}
/*implicit*/operator SQLStatementHandle() const
{
return m_pHandle
}
private:
void Reset()
{
// it is OK to delete NULL
if (m_pResult != NULL)
{
delete m_pResult;
m_pResult = NULL;
}
}
private:
CResultSet* m_pResult;
SQLStatementHandle m_pHandle;
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBIterator
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBRow<T, TDBTraits> RowType;
public:
CDBIterator():m_bIsDone(false)
{
}
~CDBIterator(){}
public:
//++ operator
//forward increment
CDBIterator& operator++()
{
m_bIsDone = !(TDBTraits::Forward() );
return *this;
}
//backwards increment
CDBIterator operator++(int)
{
CDBIterator tmp(*this);
m_bIsDone = !(TDBTraits::Forward() );
return tmp;
}
//-- operator
//no backwards operation
public:
RowType& operator*()
{
return m_CurrentRow;
}
RowType* operator&()
{
return &m_CurrentRow;
}
RowType* operator->()
{
return &m_CurrentRow;
}
public:
bool IsDone() const
{
return m_bIsDone;
}
private:
bool m_bIsDone;
RowType m_CurrentRow;
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBRow
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
public:
CDBRow(){}
~CDBRow(){}
public:
template< class TValue >
bool FetchValue(int iColumn, TValue pRetValue)
{
SQLStatementHandle pStmt = TDBTraits::GetStatementHandle();
return TDBTraits::FetchValue(pStmt, iColumn, pRetValue);
}
int FetchBytes(int iColumn)
{
SQLStatementHandle pStmt = TDBTraits::GetStatementHandle();
return TDBTraits::FetchColumnBytes(pStmt, iColumn);
}
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBRowSet
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBIterator<T, TDBTraits> iterator;
public:
CDBRowSet()
:m_pIterator(NULL)
{
}
~CDBRowSet()
{
if (m_pIterator != NULL)
{
delete m_pIterator;
}
}
public:
bool IsNULL() const
{
return TDBTraits::HasReturnValue();
}
//for iterator
iterator& CreateIterator()
{
if (m_pIterator == NULL)
{
m_pIterator = new iterator;
}
return *m_pIterator;
}
private:
iterator* m_pIterator;
};
下面是将上面3个组件黏合起来的代码,风格类似WTL
//空类,留着扩展
template< class TBase >
class CDatabaseImplRoot : public TBase
{
public:
CDatabaseImplRoot(){}
virtual ~CDatabaseImplRoot(){}
public:
//interface
private:
//implementations
};
template< class T, class TBase = CDatabase,class TDBTraits = DBTraits<T>, bool t_Managed = true >
class CDatabaseImpl : public CDatabaseImplRoot<TBase>
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBIterator<T, TDBTraits> iterator;
typedef CDBStatement<T, TDBTraits>::CResultSet ResultSet;
public:
CDatabaseImpl(){}
virtual ~CDatabaseImpl(){}
protected:
CDBConnection< T, TDBTraits >* GetConnection()
{
return &m_Connection;
}
private:
CDBConnection< T, TDBTraits, t_Managed> m_Connection;
CDBStatement< T, TDBTraits > m_pStatement;
};
好了,最终的CDatabaseImpl就是所有数据库类的共同基类,每种数据库具体实现全在DBTraits里,CDatabaseImpl是数据库无关的.
最终的CSQLiteDatabase是这样的:
class CSQLiteDatabase : public CDatabaseImpl<CSQLiteDatabase, CDatabase>
{
public:
typedef CDatabaseImpl<CSQLiteDatabase, CDatabase> Base;
typedef Base::iterator iterator;
typedef Base::ResultSet ResultSet;
public:
CSQLiteDatabase();
virtual ~CSQLiteDatabase();
public:
virtual bool Open(TCHAR const* pszFileName);
virtual bool Close();
virtual bool Execute(TCHAR const* pszSQLStatement);
public:
template<class T>
bool BindValue(int iIndex, T Value, int iSize)
{
return GetConnection()->FetchStatement()->BindValue(iIndex,Value, iSize);
}
ResultSet* GetResult();
private:
};
CSQLiteDatabase::CSQLiteDatabase()
{
}
CSQLiteDatabase::~CSQLiteDatabase()
{
}
bool CSQLiteDatabase::Open(TCHAR const* pszFileName)
{
return GetConnection()->Open(pszFileName);
}
bool CSQLiteDatabase::Close()
{
return GetConnection()->Close();
}
bool CSQLiteDatabase::Execute(TCHAR const* pszSQLStatement)
{
return GetConnection()->FetchStatement()->Execute(pszSQLStatement);
}
CSQLiteDatabase::ResultSet* CSQLiteDatabase::GetResult()
{
return GetConnection()->FetchStatement()->FetchResult();
}
CSQLiteDatabase实现了CDatabase定义的接口,现在可以使用了
现在有两个数据表(我不太懂数据库设计,可能有问题)
tbl_student: id, name存储学生的学号和姓名
tbl_score: id,chinese,math,english存储学生的学号和语文,数学,英语成绩.
现在假设查询王小明的成绩:
CSQLiteDatabase* pDatabase = GetDatabase();
bool bRet = pDatabase->Execute(_T("select a.id, b.chinese, b.math, b.english from tbl_student a, tbl_score b where a.name = 王小明 and a. id ==b.id"));
if (bRet)
{
CSQLiteDatabase::ResultSet* pSet = pDatabase->GetResult();
//如果不是查询语句,则pSet == NULL
if (pSet != NULL)
{
for (CSQLiteDatabase::iterator i = pSet->CreateIterator(); !i.IsDone() ; ++i)
{
int id;
tchar strChinese[MAX];
tchar strMath[MAX];
tchar strEnglish[MAX];
int iIndex = 0;
bRet = (*i).FetchValue(iIndex++,id);
bRet = (*i).FetchValue(iIndex++,strChinese);
bRet = (*i).FetchValue(iIndex++,strMath);
bRet = (*i).FetchValue(iIndex++,strEnglish);
}
}
}
怎么样很简单吧.
附 DBTraits 源代码:
template< class TDatabase >
struct DBTraits;
template<>
struct DBTraits<CSQLiteDatabase>
{
typedef sqlite3* DBHandle;
typedef sqlite3_stmt* SQLStatementHandle;
// typedef struct
static bool Open(TCHAR const* pszFileName)
{
if (!Close())
{
return false;
}
assert(m_pDatabase == NULL);
int iRet = SQLite_APITraits<TCHAR>::Open(pszFileName,&m_pDatabase);
if (iRet != SQLITE_OK)
{
return false;
}
assert(m_pDatabase != NULL);
return true;
}
static bool Close()
{
if (m_pStatement != NULL)
{
int iRet = SQLite_APITraits<TCHAR>::Finalize(m_pStatement);
if (iRet != SQLITE_OK)
{
return false;
}
m_pStatement = NULL;
}
if (m_pDatabase != NULL)
{
int iRet = SQLite_APITraits<TCHAR>::Close(m_pDatabase);
if (iRet == SQLITE_BUSY)
{
return false;
}
m_pDatabase = NULL;
}
return true;
}
static bool Execute(TCHAR const* pszSQLStatement)
{
if (m_pDatabase == NULL/* || m_pStatement == NULL*/)
{
return false;
}
ResetStatement();
int iRet = SQLite_APITraits<TCHAR>::Prepare(m_pDatabase, pszSQLStatement, -1, &m_pStatement, NULL);
if (iRet != SQLITE_OK)
{
return false;
}
assert(m_pStatement != NULL);
//BindValue(sqlite3_stmt *pStmt, int iIndex, T Value, int iSize = 0);
for (std::list<DBVariant<TCHAR> >::iterator i = m_listBindValue.begin(); i != m_listBindValue.end(); ++i)
{
DBVariant<TCHAR> Value= (*i);
if (Value.m_iType == DBVariant<TCHAR>::VALUE_INT)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_iIntValue, Value.m_iSize);
}
else if (Value.m_iType == DBVariant<TCHAR>::VALUE_DOUBLE)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_fDoubleValue, Value.m_iSize);
}
else if (Value.m_iType == DBVariant<TCHAR>::VALUE_TEXT)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_pTextValue, Value.m_iSize);
}
else if (Value.m_iType == DBVariant<TCHAR>::VALUE_BLOB)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_pBlobValue, Value.m_iSize);
}
else
{
assert(false);
}
}
m_listBindValue.clear();
iRet = SQLite_APITraits<TCHAR>::Step(m_pStatement);
if (iRet != SQLITE_DONE && iRet != SQLITE_ROW)
{
return false;
}
if (iRet == SQLITE_ROW)
{
m_bHasReturnValue = true;
}
// iRet = SQLite_APITraits<TCHAR>::Finalize(m_pStatement);
// if (iRet != SQLITE_OK)
// {
// return false;
// }
return true;
}
static DBHandle& GetDBHandle()
{
return m_pDatabase;
}
static SQLStatementHandle& GetStatementHandle()
{
return m_pStatement;
}
static void ResetStatement()
{
if (m_pStatement != NULL)
{
int iRet = SQLite_APITraits<TCHAR>::Finalize(m_pStatement);
if (iRet != SQLITE_OK)
{
assert(false);
}
m_pStatement = NULL;
}
}
static bool HasReturnValue()
{
return m_bHasReturnValue;
}
static bool Forward()
{
int iRet = SQLite_APITraits<TCHAR>::Step(m_pStatement);
if (iRet == SQLITE_DONE)
{
ResetStatement();
}
m_bHasReturnValue = (iRet == SQLITE_ROW);
return m_bHasReturnValue;
}
static int FetchColumnCount(SQLStatementHandle pStmt)
{
return SQLite_APITraits<TCHAR>::ColumnCount(pStmt);
}
static int FetchColumnType(SQLStatementHandle pStmt, int iColumn)
{
return SQLite_APITraits<TCHAR>::ColumnType(pStmt, iColumn);
}
static wchar_t const* FetchColumnName(SQLStatementHandle pStmt, int iColumn)
{
return SQLite_APITraits<TCHAR>::ColumnName(pStmt, iColumn);
}
static int FetchColumnBytes(SQLStatementHandle pStmt, int iColumn)
{
return SQLite_APITraits<TCHAR>::ColumnBytes(pStmt, iColumn);
}
template< class T >
static bool FetchValue(SQLStatementHandle pStmt, int iColumn, T pRetValue)
{
return SQLite_APITraits<TCHAR>::FetchColumnValue(pStmt, iColumn, pRetValue);
}
// template< class T >
// static int BindValue(SQLStatementHandle pStmt, int iIndex, T Value, int iSize)
// {
// return SQLite_APITraits<TCHAR>::BindValue(pStmt, iColumn, Value, iSize);
// }
template< class T >
static bool BindVariant(int iIndex, T Value, int iSize/* = 0*/)
{
return false;
}
template<>
static bool BindVariant<int>(int iIndex, int Value, int iSize/* = 0*/)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_INT;
Var.m_iIndex = iIndex;
Var.m_iIntValue = Value;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
template<>
static bool BindVariant<double>(int iIndex, double Value, int iSize/* = 0*/)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_DOUBLE;
Var.m_iIndex = iIndex;
Var.m_fDoubleValue = Value;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
template<>
static bool BindVariant<TCHAR const*>(int iIndex, TCHAR const* Value, int iSize)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_TEXT;
Var.m_iIndex = iIndex;
Var.m_pTextValue = Value;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
template<>
static bool BindVariant<void const*>(int iIndex, void const* pData, int iSize)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_BLOB;
Var.m_iIndex = iIndex;
Var.m_pBlobValue = pData;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
private:
static DBHandle m_pDatabase;
static SQLStatementHandle m_pStatement;
static bool m_bHasReturnValue;
static std::list<DBVariant<TCHAR> > m_listBindValue;
};
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>, bool t_Managed = true>
class CDBConnection
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
public:
CDBConnection():m_pStatement(NULL)
{
}
~CDBConnection()
{
if (m_pStatement != NULL)
{
delete m_pStatement;
}
if (t_Managed)
{
bool bRet = Close();
assert(bRet);
}
}
public:
bool Open(TCHAR const* pszFileName)
{
return TDBTraits::Open(pszFileName);
}
bool Close()
{
return TDBTraits::Close();
}
CDBStatement<T, TDBTraits >* FetchStatement()
{
if (m_pStatement == NULL)
{
m_pStatement = new CDBStatement<T, TDBTraits >;
}
assert(m_pStatement != NULL);
return m_pStatement;
}
private:
CDBStatement<T, TDBTraits >* m_pStatement;
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBStatement
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBRowSet<T, TDBTraits > CResultSet;
public:
CDBStatement():m_pHandle(NULL),m_pResult(NULL)
{
}
~CDBStatement()
{
if (m_pResult != NULL)
{
delete m_pResult;
}
}
public:
bool Execute(TCHAR const* pszSQLStatement)
{
Reset();
bool bRet = TDBTraits::Execute(pszSQLStatement);
if (!bRet)
{
return false;
}
m_pHandle = TDBTraits::GetStatementHandle();
if (TDBTraits::HasReturnValue())
{
m_pResult = new CResultSet();
assert(m_pResult != NULL);
}
return true;
}
template<class TValue>
bool BindValue(int iIndex, TValue Value, int iSize)
{
return TDBTraits::BindVariant(iIndex, Value, iSize);
}
CResultSet* FetchResult() const
{
return m_pResult;
}
/*implicit*/operator SQLStatementHandle() const
{
return m_pHandle
}
private:
void Reset()
{
// it is OK to delete NULL
if (m_pResult != NULL)
{
delete m_pResult;
m_pResult = NULL;
}
}
private:
CResultSet* m_pResult;
SQLStatementHandle m_pHandle;
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBIterator
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBRow<T, TDBTraits> RowType;
public:
CDBIterator():m_bIsDone(false)
{
}
~CDBIterator(){}
public:
//++ operator
//forward increment
CDBIterator& operator++()
{
m_bIsDone = !(TDBTraits::Forward() );
return *this;
}
//backwards increment
CDBIterator operator++(int)
{
CDBIterator tmp(*this);
m_bIsDone = !(TDBTraits::Forward() );
return tmp;
}
//-- operator
//no backwards operation
public:
RowType& operator*()
{
return m_CurrentRow;
}
RowType* operator&()
{
return &m_CurrentRow;
}
RowType* operator->()
{
return &m_CurrentRow;
}
public:
bool IsDone() const
{
return m_bIsDone;
}
private:
bool m_bIsDone;
RowType m_CurrentRow;
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBRow
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
public:
CDBRow(){}
~CDBRow(){}
public:
template< class TValue >
bool FetchValue(int iColumn, TValue pRetValue)
{
SQLStatementHandle pStmt = TDBTraits::GetStatementHandle();
return TDBTraits::FetchValue(pStmt, iColumn, pRetValue);
}
int FetchBytes(int iColumn)
{
SQLStatementHandle pStmt = TDBTraits::GetStatementHandle();
return TDBTraits::FetchColumnBytes(pStmt, iColumn);
}
};
template<class T, class TDBTraits = DBTraits<T> >
class CDBRowSet
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBIterator<T, TDBTraits> iterator;
public:
CDBRowSet()
:m_pIterator(NULL)
{
}
~CDBRowSet()
{
if (m_pIterator != NULL)
{
delete m_pIterator;
}
}
public:
bool IsNULL() const
{
return TDBTraits::HasReturnValue();
}
//for iterator
iterator& CreateIterator()
{
if (m_pIterator == NULL)
{
m_pIterator = new iterator;
}
return *m_pIterator;
}
private:
iterator* m_pIterator;
};
下面是将上面3个组件黏合起来的代码,风格类似WTL
//空类,留着扩展
template< class TBase >
class CDatabaseImplRoot : public TBase
{
public:
CDatabaseImplRoot(){}
virtual ~CDatabaseImplRoot(){}
public:
//interface
private:
//implementations
};
template< class T, class TBase = CDatabase,class TDBTraits = DBTraits<T>, bool t_Managed = true >
class CDatabaseImpl : public CDatabaseImplRoot<TBase>
{
public:
typedef TDBTraits::DBHandle DBHandle;
typedef TDBTraits::SQLStatementHandle SQLStatementHandle;
typedef CDBIterator<T, TDBTraits> iterator;
typedef CDBStatement<T, TDBTraits>::CResultSet ResultSet;
public:
CDatabaseImpl(){}
virtual ~CDatabaseImpl(){}
protected:
CDBConnection< T, TDBTraits >* GetConnection()
{
return &m_Connection;
}
private:
CDBConnection< T, TDBTraits, t_Managed> m_Connection;
CDBStatement< T, TDBTraits > m_pStatement;
};
好了,最终的CDatabaseImpl就是所有数据库类的共同基类,每种数据库具体实现全在DBTraits里,CDatabaseImpl是数据库无关的.
最终的CSQLiteDatabase是这样的:
class CSQLiteDatabase : public CDatabaseImpl<CSQLiteDatabase, CDatabase>
{
public:
typedef CDatabaseImpl<CSQLiteDatabase, CDatabase> Base;
typedef Base::iterator iterator;
typedef Base::ResultSet ResultSet;
public:
CSQLiteDatabase();
virtual ~CSQLiteDatabase();
public:
virtual bool Open(TCHAR const* pszFileName);
virtual bool Close();
virtual bool Execute(TCHAR const* pszSQLStatement);
public:
template<class T>
bool BindValue(int iIndex, T Value, int iSize)
{
return GetConnection()->FetchStatement()->BindValue(iIndex,Value, iSize);
}
ResultSet* GetResult();
private:
};
CSQLiteDatabase::CSQLiteDatabase()
{
}
CSQLiteDatabase::~CSQLiteDatabase()
{
}
bool CSQLiteDatabase::Open(TCHAR const* pszFileName)
{
return GetConnection()->Open(pszFileName);
}
bool CSQLiteDatabase::Close()
{
return GetConnection()->Close();
}
bool CSQLiteDatabase::Execute(TCHAR const* pszSQLStatement)
{
return GetConnection()->FetchStatement()->Execute(pszSQLStatement);
}
CSQLiteDatabase::ResultSet* CSQLiteDatabase::GetResult()
{
return GetConnection()->FetchStatement()->FetchResult();
}
CSQLiteDatabase实现了CDatabase定义的接口,现在可以使用了
现在有两个数据表(我不太懂数据库设计,可能有问题)
tbl_student: id, name存储学生的学号和姓名
tbl_score: id,chinese,math,english存储学生的学号和语文,数学,英语成绩.
现在假设查询王小明的成绩:
CSQLiteDatabase* pDatabase = GetDatabase();
bool bRet = pDatabase->Execute(_T("select a.id, b.chinese, b.math, b.english from tbl_student a, tbl_score b where a.name = 王小明 and a. id ==b.id"));
if (bRet)
{
CSQLiteDatabase::ResultSet* pSet = pDatabase->GetResult();
//如果不是查询语句,则pSet == NULL
if (pSet != NULL)
{
for (CSQLiteDatabase::iterator i = pSet->CreateIterator(); !i.IsDone() ; ++i)
{
int id;
tchar strChinese[MAX];
tchar strMath[MAX];
tchar strEnglish[MAX];
int iIndex = 0;
bRet = (*i).FetchValue(iIndex++,id);
bRet = (*i).FetchValue(iIndex++,strChinese);
bRet = (*i).FetchValue(iIndex++,strMath);
bRet = (*i).FetchValue(iIndex++,strEnglish);
}
}
}
怎么样很简单吧.
附 DBTraits 源代码:
template< class TDatabase >
struct DBTraits;
template<>
struct DBTraits<CSQLiteDatabase>
{
typedef sqlite3* DBHandle;
typedef sqlite3_stmt* SQLStatementHandle;
// typedef struct
static bool Open(TCHAR const* pszFileName)
{
if (!Close())
{
return false;
}
assert(m_pDatabase == NULL);
int iRet = SQLite_APITraits<TCHAR>::Open(pszFileName,&m_pDatabase);
if (iRet != SQLITE_OK)
{
return false;
}
assert(m_pDatabase != NULL);
return true;
}
static bool Close()
{
if (m_pStatement != NULL)
{
int iRet = SQLite_APITraits<TCHAR>::Finalize(m_pStatement);
if (iRet != SQLITE_OK)
{
return false;
}
m_pStatement = NULL;
}
if (m_pDatabase != NULL)
{
int iRet = SQLite_APITraits<TCHAR>::Close(m_pDatabase);
if (iRet == SQLITE_BUSY)
{
return false;
}
m_pDatabase = NULL;
}
return true;
}
static bool Execute(TCHAR const* pszSQLStatement)
{
if (m_pDatabase == NULL/* || m_pStatement == NULL*/)
{
return false;
}
ResetStatement();
int iRet = SQLite_APITraits<TCHAR>::Prepare(m_pDatabase, pszSQLStatement, -1, &m_pStatement, NULL);
if (iRet != SQLITE_OK)
{
return false;
}
assert(m_pStatement != NULL);
//BindValue(sqlite3_stmt *pStmt, int iIndex, T Value, int iSize = 0);
for (std::list<DBVariant<TCHAR> >::iterator i = m_listBindValue.begin(); i != m_listBindValue.end(); ++i)
{
DBVariant<TCHAR> Value= (*i);
if (Value.m_iType == DBVariant<TCHAR>::VALUE_INT)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_iIntValue, Value.m_iSize);
}
else if (Value.m_iType == DBVariant<TCHAR>::VALUE_DOUBLE)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_fDoubleValue, Value.m_iSize);
}
else if (Value.m_iType == DBVariant<TCHAR>::VALUE_TEXT)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_pTextValue, Value.m_iSize);
}
else if (Value.m_iType == DBVariant<TCHAR>::VALUE_BLOB)
{
SQLite_APITraits<TCHAR>::BindValue(m_pStatement, Value.m_iIndex, Value.m_pBlobValue, Value.m_iSize);
}
else
{
assert(false);
}
}
m_listBindValue.clear();
iRet = SQLite_APITraits<TCHAR>::Step(m_pStatement);
if (iRet != SQLITE_DONE && iRet != SQLITE_ROW)
{
return false;
}
if (iRet == SQLITE_ROW)
{
m_bHasReturnValue = true;
}
// iRet = SQLite_APITraits<TCHAR>::Finalize(m_pStatement);
// if (iRet != SQLITE_OK)
// {
// return false;
// }
return true;
}
static DBHandle& GetDBHandle()
{
return m_pDatabase;
}
static SQLStatementHandle& GetStatementHandle()
{
return m_pStatement;
}
static void ResetStatement()
{
if (m_pStatement != NULL)
{
int iRet = SQLite_APITraits<TCHAR>::Finalize(m_pStatement);
if (iRet != SQLITE_OK)
{
assert(false);
}
m_pStatement = NULL;
}
}
static bool HasReturnValue()
{
return m_bHasReturnValue;
}
static bool Forward()
{
int iRet = SQLite_APITraits<TCHAR>::Step(m_pStatement);
if (iRet == SQLITE_DONE)
{
ResetStatement();
}
m_bHasReturnValue = (iRet == SQLITE_ROW);
return m_bHasReturnValue;
}
static int FetchColumnCount(SQLStatementHandle pStmt)
{
return SQLite_APITraits<TCHAR>::ColumnCount(pStmt);
}
static int FetchColumnType(SQLStatementHandle pStmt, int iColumn)
{
return SQLite_APITraits<TCHAR>::ColumnType(pStmt, iColumn);
}
static wchar_t const* FetchColumnName(SQLStatementHandle pStmt, int iColumn)
{
return SQLite_APITraits<TCHAR>::ColumnName(pStmt, iColumn);
}
static int FetchColumnBytes(SQLStatementHandle pStmt, int iColumn)
{
return SQLite_APITraits<TCHAR>::ColumnBytes(pStmt, iColumn);
}
template< class T >
static bool FetchValue(SQLStatementHandle pStmt, int iColumn, T pRetValue)
{
return SQLite_APITraits<TCHAR>::FetchColumnValue(pStmt, iColumn, pRetValue);
}
// template< class T >
// static int BindValue(SQLStatementHandle pStmt, int iIndex, T Value, int iSize)
// {
// return SQLite_APITraits<TCHAR>::BindValue(pStmt, iColumn, Value, iSize);
// }
template< class T >
static bool BindVariant(int iIndex, T Value, int iSize/* = 0*/)
{
return false;
}
template<>
static bool BindVariant<int>(int iIndex, int Value, int iSize/* = 0*/)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_INT;
Var.m_iIndex = iIndex;
Var.m_iIntValue = Value;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
template<>
static bool BindVariant<double>(int iIndex, double Value, int iSize/* = 0*/)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_DOUBLE;
Var.m_iIndex = iIndex;
Var.m_fDoubleValue = Value;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
template<>
static bool BindVariant<TCHAR const*>(int iIndex, TCHAR const* Value, int iSize)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_TEXT;
Var.m_iIndex = iIndex;
Var.m_pTextValue = Value;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
template<>
static bool BindVariant<void const*>(int iIndex, void const* pData, int iSize)
{
DBVariant<TCHAR> Var;
Var.m_iType = DBVariant<TCHAR>::VALUE_BLOB;
Var.m_iIndex = iIndex;
Var.m_pBlobValue = pData;
Var.m_iSize = iSize;
m_listBindValue.push_back(Var);
return true;
}
private:
static DBHandle m_pDatabase;
static SQLStatementHandle m_pStatement;
static bool m_bHasReturnValue;
static std::list<DBVariant<TCHAR> > m_listBindValue;
};
相关文章推荐
- C++使用Sqlite3,使用CppSQLite3的封装
- 将sqlite3编译成给WINCE使用的DLL,并封装成C++的步骤(使用VS2005)
- 将sqlite3编译成给WINCE使用的DLL,并封装成C++的步骤(使用VS2005)
- C++ 使用SQLite
- [C++模板]Clang3.9使用变参模拟实现CheckerFn和Checker
- SQLiteOpenHelper的使用,对数据库操作的封装
- C++模板使用介绍
- C++动态绑定和模板的简单配合使用(virtual关键字)
- C++互斥锁与条件变量的封装使用
- 利用 C++ Interop 封装 ISO C++ 对象, 供其他 .Net 语言使用
- C++中 模板Template的使用
- 使用C++模板实现栈的求最小值功能
- 在C++中使用sqlite的示例(从下载、编译、工程建立、配置、测试代码编写全方位介绍)
- C++ 使用 hiredis 封装redis 的数据获取接口
- C++set模板的使用
- DSP 2812: 使用C++封装GPIO
- C++中 模板Template的使用
- C++ 模板使用
- Sqlite3 C++ 使用方法