sqlite本身的事务并不支持嵌套,而savepoint方法虽然支持嵌套,但是,使用起来比较复杂。下面,提供一种针对线程数据库连接的事务嵌套模式,如果是在单线程中操作数据库,则可以进一步简化实现 (采用静态变量保存事务计数即可)。
在代码中,使用了windows下的线程槽的概念,其是针对每个线程保留单独的一份数据。
#include "sqlite3.h" /** * @class LockHelp * @brief a lock class */ class CLockHelp { public: CLockHelp() { ::InitializeCriticalSection(&m_CriticalSection); } ~CLockHelp() { ::DeleteCriticalSection(&m_CriticalSection); } /// lock void lock() { ::EnterCriticalSection(&m_CriticalSection); } /// unlock void unlock() { ::LeaveCriticalSection(&m_CriticalSection); } private: CLockHelp(const NLock&); CLockHelp& operator=(const NLock&); private: CRITICAL_SECTION m_CriticalSection; }; /** * @class CTransaction * @brief 启动、回滚、提交事务操作 */ class CTransaction { public: CTransaction(sqlite *pConnection); ~CTransaction(void); public: /** * @brief 获取m_dwTLSRollbackTrans,表明数据库操作出错 * @retval TLS中的值 */ void SetRollbackCounter(); private: /** * @brief 获取TLS中的值 * @param dwIdx: TLS索引 * @retval TLS中的值 */ INT64 GetCounter(DWORD dwIdx); /** * @brief 设置TLS中的值 * @param dwIdx: TLS索引 * @param nCount: 待设定的值 */ void SetCounter(DWORD dwIdx,INT64 nCount); private: /// 线程局部存储变量 /// 是否调用BeginTransaction() static DWORD m_dwTLSBeginTransIdx; /// 是否调用RollbackTransaction() static DWORD m_dwTLSRollbackTransIdx; /// 线程锁 static CLockHelp m_CriticalLock; /// CTableHandler对象 sqlite3 *m_pConnection; }; #include "Transaction.h" DWORD CTransaction::m_dwTLSBeginTransIdx = ::TlsAlloc(); DWORD CTransaction::m_dwTLSRollbackTransIdx = ::TlsAlloc(); CLockHelp CTransaction::m_CriticalLock; CTransaction::CTransaction(CTableHandler *pTableHandler) : m_pTableHandler(pTableHandler) { // 加锁 m_CriticalLock.lock(); INT64 nBeginTrans(GetCounter(m_dwTLSBeginTransIdx)); if (nBeginTrans == 0) { if (!m_pTableHandler->BeginTransaction()) { // 设置为0 SetCounter(m_dwTLSBeginTransIdx,nBeginTrans); return; } } SetCounter(m_dwTLSBeginTransIdx,nBeginTrans + 1); } CTransaction::~CTransaction(void) { INT64 nEndTrans(GetCounter(m_dwTLSBeginTransIdx)); if (nEndTrans == 0) { // 启动事务失败 INT64 nRollbackTrans(GetCounter(m_dwTLSRollbackTransIdx)); if (nRollbackTrans == 1) { SetCounter(m_dwTLSRollbackTransIdx,0); } // 解锁 m_CriticalLock.unlock(); return; } if (nEndTrans == 1) { INT64 nRollbackTrans(GetCounter(m_dwTLSRollbackTransIdx)); if (nRollbackTrans == 1) { m_pTableHandler->RollbackTransaction(); SetCounter(m_dwTLSRollbackTransIdx,0); } else { m_pTableHandler->CommitTransaction(); } } SetCounter(m_dwTLSBeginTransIdx,nEndTrans - 1); // 解锁 m_CriticalLock.unlock(); } void CTransaction::SetRollbackCounter() { SetCounter(m_dwTLSRollbackTransIdx,1); } INT64 CTransaction::GetCounter(DWORD dwIdx) { return reinterpret_cast<INT64>(::TlsGetValue(dwIdx)); } void CTransaction::SetCounter(DWORD dwIdx,INT64 nCount) { ::TlsSetValue(dwIdx,reinterpret_cast<void *>(nCount)); }