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

    mysql表复制 1.复制表结构 create table student like user; 2.复制表内容 insert into t3 select * from t1; mysql索引 1. ...

  2. vue 路由导航白话全解析

    这里先放上官网的教程和说明:点击这里,vue导航守卫官方文档 路由守卫 路由守卫说白了就是路由拦截,在地址栏跳转之前 之后 跳转的瞬间 干什么事 全局守卫 全局守卫顾名思义,就是全局的,整个项目所有路 ...

  3. java10:基于时间的版本控制

    功能发布 从Java 10开始,采用了一种新的严格的基于时间的发布模式. 在这个新模型中,Java平台的主要版本(现称为功能版本)将每6个月(3月和9月)发布一次. 功能版本将包含语言功能,JVM功能 ...

  4. HTTP上下文表单内容转为实体对象

    using ServiceStack.Web; using System; using System.Collections.Generic; using System.Linq; using Sys ...

  5. TX2 自制底板不识别USB

    目的:解决自制的底板无法识别USB,使能3个UART接口,使能3个SPI接口. Jetpack版本:Jetpack-3.1 虚拟机:ubuntu14.04 使用dtb文件夹下的文件替换刷机包../64 ...

  6. XAF对Attribute的总结

    [Aggregated] 没有参数,作用于a property or a field,并且只能是持久类的引用或者XPCollection.实现两个持久类的级联删除的功能. [Association(& ...

  7. springcloud微服务架构的思考

    在网上找到一张关于微服务体系架构的图 应用组件: 首先对于整个程序的入口应该是网关,zuul部分 这个组件在springcloud中的gateway服务之后,zuul可以进行网关分配,根据想应的路劲进 ...

  8. COCO2018 全景分割

    全景分割是18年新推出的一个任务,它要求同时分割出目标和背景,也就是既有实例分割也有语义分割,用官方的话讲是朝着真实世界视觉系统的重要一步 如图所示,里面既有对天空,草地等stuff的分割,也有对目标 ...

  9. linux的目录和基本的操作命令

    目录相关操作:( ctrl+l   清空当前的屏幕中的命令  ) 一:目录说明: .   当前目录..   上一层目录-   前一个工作目录~   当前[用户]所在的家目录 蓝色的文件: 都是目录 白 ...

  10. C++_代码重用1-总览

    C++的主要目的是促进代码重用. 公有继承是实现这一目标的机制之一: 本身是另一个类的成员,这种方法称为包含.组合.层次化. 另一种方法是使用私有.保护继承. 通常包含.私有继承和保护继承用于实现ha ...