学过数据的人一般都知道事务的重要性,事务是一种对数据源的一系列更新进行分组或者批处理以便当所有更新都成功时同时提交更新,或者任意一个更新失败时进行回滚将数据库中的数据回滚到执行批处理中的所有操作之前的一种方法。使用事务保证了数据的完整性。这里不展开详细的说事务,只是谈谈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,那么在同一事务中只有当其中一个客户端提交了事务更新后,另外一个客户端才能正常的进行查询等操作,可以简单的将这个标识视为它在数据库中上了锁,只有当它完成事务后其他客户端才可以正常使用数据库

3. 开始一个事务后正常的进行相关的数据库操作

4. 当所有步骤都正常完成后调用ITransaction::Commit方法提交事务所做的所有修改

5. 或者当其中有一步或者几步失败时调用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


OLEDB事务的更多相关文章

  1. C# vs MySql

    MySqlHelper类 /// <summary> ///MySql操作类 /// </summary> public abstract class MySqlHelper ...

  2. C#连接、访问MySQL数据库

    一.准备工具 visual stuido(本示例使用visual studio 2010) MySql.Data.dll mysql_installer_community_V5.6.21.1_set ...

  3. DataBase——Mysql的DataHelper

    源帖 https://www.cnblogs.com/youuuu/archive/2011/06/16/2082730.html 保护原帖,尊重技术,致敬工匠! using System; usin ...

  4. 八、.net core 通过数据库配置文件连接操作数据库

    一.创建DotNetCore项目 直接创建core项目并不勾选docker支持  二.nuget进行连接MySQL程序集的下载安装 1.MySql.Data.EntityFrameworkCore方式 ...

  5. DB通用类:MySQL通用类

    Mysql类为网络上收集的,没有测试过.. using System; using System.Collections; using System.Collections.Generic; usin ...

  6. MySql+Socket 完成数据库的增查Demo

    需求: 利用MySql数据库结合前端技术完成用户的注册(要求不使用Web服务技术),所以 Demo采用Socket技术实现Web通信. 第一部分:数据库创建 数据库采用mysql 5.7.18, 数据 ...

  7. C# 基于MySQL的数据层基类(MySQLHelper)

    这里介绍下比较简单的方式,引用MySql.Data.dll然后添加一个MySqlHelper类来对MySql数据库进行访问和操作. 1.将MySql.Data.dll引用到你的项目中 下载地址:MyS ...

  8. C#访问和操作MYSQL数据库

    这里介绍下比较简单的方式,引用MySql.Data.dll然后添加一个MySqlHelper类来对MySql数据库进行访问和操作. 1.将MySql.Data.dll引用到你的项目中 下载地址:MyS ...

  9. MySqlDBHelper数据库连接

    这里是本人在工作中用到,希望给大家帮助 public class MySqlDBHelper { //获取一个记录器 private static readonly log4net.ILog log ...

随机推荐

  1. 公司拷贝回家的工程用sts导入clean package报错java.lang.NoClassDefFoundError

    从公司拷贝工程回家加班,用相同版本的sts和jdk但是run as    maven build   clean package 总是报错java.lang.NoClassDefFoundError: ...

  2. CF138D World of Darkraft

    $ \color{#0066ff}{ 题目描述 }$ n*m的格子,每个格子有字符'L','R',X',初始可以选择所有格子. 当选了 'L'的格子时,当前格子左下右上这条线上所有点不能选; 当选了 ...

  3. Day 4 上午

    内容提要 进制转换 高精度 数论:筛法,gcd/exgcd,逆元 进制转换 10=2^3+2^0=1010(2)10=3^2+3^0=101(3) 10进制x-->k进制:短除法 k进制x--& ...

  4. Tomcat 连接数与线程池详解

    前言 在使用tomcat时,经常会遇到连接数.线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector). 在前面的文章 详解Tomcat配置文件server.xm ...

  5. apache shiro学习笔记

    一.权限概述 1.1 认证与授权 认证:系统提供的用于识别用户身份的功能,通常登录功能就是认证功能-----让系统知道你是谁?? 授权:系统授予用户可以访问哪些功能的许可(证书)----让系统知道你能 ...

  6. Go 语言 基础 【第一篇】:package fmt导入

    package  main 解释:只要你 一个可执行 程序

  7. [转] 如何批量删除Docker中已经停止的容器

    [From]https://blog.csdn.net/csdn_duomaomao/article/details/78587103 方法一: #显示所有的容器,过滤出Exited状态的容器,取出这 ...

  8. Oracle Purge和drop的区别

    转自: http://www.cnblogs.com/HondaHsu/archive/2012/09/28/2707487.html 最近发现oracle中出现了这些奇怪的表名,上网查找后发现是or ...

  9. 解决nginx文件服务器访问403

    2018-10-24 nginx配置文件目录服务器 修改/etc/nginx/conf.d/default.conf或者在/etc/nginx/conf.d/目录下添加一配置文件,如下 server ...

  10. poi excel 常用api

    http://www.cnblogs.com/huajiezh/p/5467821.html