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 条评论
登录 后参与评论

相关文章

来自专栏解Bug之路

文件解析中间件,用SQL去读取各种格式的文件! 顶

(1)完全支持MySql协议 (2)完全支持Mybatis-Generator (3)支持Schema和Table (4)支持客户端创建Schema和Ta...

1123
来自专栏从零开始的linux

mycat实现mysql的读写分离

环境 ip角色端口128.0.0.101master3306128.0.0.102slave3306128.0.0.103mycat8066 前提两台机器已经做...

3546
来自专栏乐沙弥的世界

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

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

781
来自专栏搜云库

CentOs7.3 搭建 MySQL 5.7.19 主从复制,以及复制实现细节分析

CentOs7.3 搭建 MySQL 5.7.19 主从复制,以及复制实现细节分析 概念 主从复制可以使MySQL数据库主服务器的主数据库,复制到一个或多个My...

2705
来自专栏沃趣科技

复制状态与变量记录表 | performance_schema全方位介绍

不知不觉中,performance_schema系列快要接近尾声了,今天将带领大家一起踏上系列第六篇的征程(全系共7个篇章),在这一期里,我们将为大家全面讲解p...

1673
来自专栏乐沙弥的世界

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

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

812
来自专栏SpringBoot 核心技术

第二章:使用QueryDSL与SpringDataJPA实现单表普通条件查询

2402
来自专栏青青天空树

springboot整合mybatis(xml+注解)

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

962
来自专栏岑玉海

sqoop 兼容性问题

--direct 只支持mysql 5.0 + 和postgresql 8.3+(只是import) jdbc的jar包需要放在$SQOOP_HOME/lib目...

3996
来自专栏数据和云

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

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

3809

扫码关注云+社区