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));
}