OLEDB事务

学过数据的人一般都知道事务的重要性,事务是一种对数据源的一系列更新进行分组或者批处理以便当所有更新都成功时同时提交更新,或者任意一个更新失败时进行回滚将数据库中的数据回滚到执行批处理中的所有操作之前的一种方法。使用事务保证了数据的完整性。这里不展开详细的说事务,只是谈谈OLEDB在事务上的支持

ITransactionLocal接口

OLEDB中支持事务的接口是ITransactionLocal接口,该接口是一个可选接口,OLEDB并不强制要求所有数据库都支持该接口,所以在使用之前需要先判断是否支持,好在现在常见的几种数据库都支持。

  1. 该接口属于回话对象,因此要得到该接口只需要根据一个回话对象调用QueryInterface即可
  2. 调用接口的StartTransaction方法开始一个事务 该函数的原型如下
HRESULT StartTransaction (
   ISOLEVEL                isoLevel,
   ULONG                   isoFlags,
   ITransactionOptions    *pOtherOptions,
   ULONG                  *pulTransactionLevel);

第一个参数是事务并发的隔离级别,一般最常用的是ISOLATIONLEVEL_CURSORSTABILITY,表示只有最终提交之后才能查询对应数据库表的数据 第二个参数是一个标志,目前它的值必须为0 第3个参数是一个指针,它可以为空,或者是调用ITransactionLocal::GetOptionsObject函数返回的一个指针 第4个参数是调用该函数创建一个事务后,该事务的并发隔离级别

隔离级别是针对不同的线程或者进程的,比如有多个客户端同时在操作数据库时,如果我们设置为ISOLATIONLEVEL_CURSORSTABILITY,那么在同一事务中只有当其中一个客户端提交了事务更新后,另外一个客户端才能正常的进行查询等操作,可以简单的将这个标识视为它在数据库中上了锁,只有当它完成事务后其他客户端才可以正常使用数据库

  1. 开始一个事务后正常的进行相关的数据库操作
  2. 当所有步骤都正常完成后调用ITransaction::Commit方法提交事务所做的所有修改
  3. 或者当其中有一步或者几步失败时调用ITransaction::Abort方法回滚所有的操作

演示例子

//注意使用ISOLATIONLEVEL_CURSORSTABILITY表示最终Commint以后,才能读取这两个表的数据
    hr = pITransaction->StartTransaction(ISOLATIONLEVEL_CURSORSTABILITY,0,NULL,NULL);

    //获取主表主键的最大值
    pRetData = RunSqlGetValue(pIOpenRowset,_T("Select Max(PID) As PMax From T_Primary"));
    if(NULL == pRetData)
    {
        goto CLEAR_UP;
    }
    iPID = *(int*)((BYTE*)pRetData + sizeof(DBSTATUS) + sizeof(ULONG));

    //最大值总是加1,这样即使取得的是空值,起始值也是正常的1
    ++iPID;

    TableID.eKind           = DBKIND_NAME;
    TableID.uName.pwszName  = (LPOLESTR)pszPrimaryTable;

    hr = pIOpenRowset->OpenRowset(NULL,&TableID
        ,NULL,IID_IRowsetChange,1,PropSet,(IUnknown**)&pIRowsetChange);
    COM_COM_CHECK(hr,_T("打开表对象'%s'失败,错误码:0x%08X\n"),pszPrimaryTable,hr);

    ulChangeOffset = CreateAccessor(pIRowsetChange,pIAccessor,hChangeAccessor,pChangeBindings,ulRealCols);

    if(0 == ulChangeOffset
        || NULL == hChangeAccessor
        || NULL == pIAccessor
        || NULL == pChangeBindings
        || 0 == ulRealCols)
    {
        goto CLEAR_UP;
    }
    //分配一个新行数据 设置数据后 插入
    pbNewData = (BYTE*)COM_CALLOC(ulChangeOffset);

    //设置第一个字段 K_PID
    *(DBLENGTH *)((BYTE *)pbNewData + pChangeBindings[0].obLength) = sizeof(int);
    *(int*) (pbNewData + pChangeBindings[0].obValue) = iPID;

    //设置第二个字段 F_MValue
    *(DBLENGTH *)((BYTE *)pbNewData + pChangeBindings[1].obLength) = 8;
    StringCchCopy((WCHAR*) (pbNewData + pChangeBindings[1].obValue)
        ,pChangeBindings[1].cbMaxLen/sizeof(WCHAR),_T("主表数据"));

    //插入新数据
    hr = pIRowsetChange->InsertRow(NULL,hChangeAccessor,pbNewData,NULL);
    COM_COM_CHECK(hr,_T("调用InsertRow插入新行失败,错误码:0x%08X\n"),hr);

    hr = pIRowsetChange->QueryInterface(IID_IRowsetUpdate,(void**)&pIRowsetUpdate);
    COM_COM_CHECK(hr,_T("获取IRowsetUpdate接口失败,错误码:0x%08X\n"),hr);

    hr = pIRowsetUpdate->Update(NULL,0,NULL,NULL,NULL,NULL);
    COM_COM_CHECK(hr,_T("调用Update提交更新失败,错误码:0x%08X\n"),hr);

    COM_SAFEFREE(pChangeBindings);
    COM_SAFEFREE(pRetData);
    COM_SAFEFREE(pbNewData);
    if(NULL != hChangeAccessor && NULL != pIAccessor)
    {
        pIAccessor->ReleaseAccessor(hChangeAccessor,NULL);
        hChangeAccessor = NULL;
    }
    COM_SAFERELEASE(pIAccessor);
    COM_SAFERELEASE(pIRowsetChange);
    COM_SAFERELEASE(pIRowsetUpdate);

    //插入第二个也就是从表的数据
    TableID.eKind           = DBKIND_NAME;
    TableID.uName.pwszName  = (LPOLESTR)pszMinorTable;

    hr = pIOpenRowset->OpenRowset(NULL,&TableID
        ,NULL,IID_IRowsetChange,1,PropSet,(IUnknown**)&pIRowsetChange);
    COM_COM_CHECK(hr,_T("打开表对象'%s'失败,错误码:0x%08X\n"),pszMinorTable,hr);

    ulChangeOffset = CreateAccessor(pIRowsetChange,pIAccessor,hChangeAccessor,pChangeBindings,ulRealCols);

    if(0 == ulChangeOffset
        || NULL == hChangeAccessor
        || NULL == pIAccessor
        || NULL == pChangeBindings
        || 0 == ulRealCols)
    {
        goto CLEAR_UP;
    }

    //分配一个新行数据 设置数据后 插入
    pbNewData = (BYTE*)COM_CALLOC(ulChangeOffset);

    //设置第一个字段 K_MID
    *(DBLENGTH *)((BYTE *)pbNewData + pChangeBindings[0].obLength) = sizeof(int);
    //设置第二个字段 K_PID
    *(DBLENGTH *)((BYTE *)pbNewData + pChangeBindings[1].obLength) = sizeof(int);
    *(int*) (pbNewData + pChangeBindings[1].obValue) = iPID;

    //设置第二个字段
    *(DBLENGTH *)((BYTE *)pbNewData + pChangeBindings[2].obLength) = 8;
    StringCchCopy((WCHAR*) (pbNewData + pChangeBindings[2].obValue)
        ,pChangeBindings[2].cbMaxLen/sizeof(WCHAR),_T("从表数据"));

    for(int i = iMIDS; i <= iMIDMax; i++)
    {//循环插入新数据
        //设置第一个字段 K_MID
        *(int*) (pbNewData + pChangeBindings[0].obValue) = i;

        hr = pIRowsetChange->InsertRow(NULL,hChangeAccessor,pbNewData,NULL);
        COM_COM_CHECK(hr,_T("调用InsertRow插入新行失败,错误码:0x%08X\n"),hr);
    }

    hr = pIRowsetChange->QueryInterface(IID_IRowsetUpdate,(void**)&pIRowsetUpdate);
    COM_COM_CHECK(hr,_T("获取IRowsetUpdate接口失败,错误码:0x%08X\n"),hr);

    hr = pIRowsetUpdate->Update(NULL,0,NULL,NULL,NULL,NULL);
    COM_COM_CHECK(hr,_T("调用Update提交更新失败,错误码:0x%08X\n"),hr);

    //所有操作都成功了,提交事务释放资源
    hr = pITransaction->Commit(FALSE, XACTTC_SYNC, 0);
    COM_COM_CHECK(hr,_T("事务提交失败,错误码:0x%08X\n"),hr);

CLEAR_UP:
    //操作失败,回滚事务先,然后释放资源
    hr = pITransaction->Abort(NULL, FALSE, FALSE);

在上述代码中首先创建一个事务对象,然后在进行相关的数据库操作,这里主要是在更新和插入新数据,当所有操作成功后调用commit函数提交,当其中有错误时会跳转到CLEAR_UP标签下,调用Abort进行回滚

最后实例的完整代码: Trancation


本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linyb极客之路

MySQL数据库“十宗罪”(十大经典错误案例)

Too many connections(连接数过多,导致连接不上数据库,业务无法正常进行)

17620
来自专栏LIN_ZONE

MySQL视图更新

昨天在写美团2019秋招笔试题的时候遇到了关于视图是否能更新的问题,突然感觉这个问题之前复习的时候重点关注过,但是却又想不全。今天特地搜了一些资料总结一下。本文...

20030
来自专栏数据和云

Oracle数据库的初始化与跟踪学习方法

编辑说明:《Oracle性能优化与诊断案例精选》出版以来,收到很多读者的来信和评论,我们会通过连载的形式将书中内容公布出来,希望书中内容能够帮助到更多的读者朋友...

40490
来自专栏乐沙弥的世界

RAC 环境下修改归档模式

    RAC环境下的归档模式切换与单实例稍有不同,主要是共享存储所产生的差异。在这种情况下,我们可以将RAC数据库切换到非集群状态下,仅仅在一个实例上来实施归...

10920
来自专栏青青天空树

springboot整合mybatis(xml+注解)

​ 刚毕业的第一份工作是java开发,项目中需要用到mybatis,特此记录学习过程,这只是一个简单demo,mybatis用法很多不可能全部写出来,有更复杂的...

12620
来自专栏我的博客

Sqlite使用说明

安装apt-get install slqite .databases List names and files of attached databases(列...

37440
来自专栏乐沙弥的世界

基于同一主机配置Oracle 11g Data Guard(logical standby)

      Oracle Data Guard逻辑备库是利用主库的一个备份首先建立一个物理备库,然后再将其转换为逻辑备库。这之后主库将日志传递到备库,备库利用l...

10110
来自专栏杨建荣的学习笔记

系统级alias vs Oracle ADR功能(r5笔记第35天)

Oracle在11g中推出的新特性ADR,即Automatic Diagnostic Repository 个人理解这个工具就是能够高效的把一些日志文件轻松管理...

37180
来自专栏技术点滴

黑客常用WinAPI函数整理

黑客常用WinAPI函数整理 之前的博客写了很多关于Windows编程的内容,在Windows环境下的黑客必须熟练掌握底层API编程。为了使读者对黑客常用的Wi...

19860
来自专栏乐沙弥的世界

MySQL read_log_event(): 'Found invalid event in binary log'

    MySQL以简单易用著称,在同一个服务器上可以安装N个不同的版本,方便测试,迁移等等。此外,对于大多数Linux系统,集成了mysql,缺省会被安装。因...

9920

扫码关注云+社区

领取腾讯云代金券