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

    E:\developtools\apache ab\httpd-2.4.25-x86-vc14-r1\Apache24\bin>ab -n 15000 -c 600 "http://1 ...

  2. SDUT OJ 数据结构实验之排序三:bucket sort

    数据结构实验之排序三:bucket sort Time Limit: 250 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem D ...

  3. 实现bootstrap的dropdown-menu(下拉菜单)点击后不关闭的方法 (转)

    实现bootstrap的dropdown-menu(下拉菜单)点击后不关闭的方法 问题描述,在下拉菜单中,添加其他元素,例如,原文作者所述的<a>和我自己实际用到的<input> ...

  4. shell-003:用for循环统计内存使用量

    shell-100主要是用于练习! #!/bin/bash # 统计内存的使用量(这里用ps统计) # 第一步:不打印第一行,这里的sed ‘1d’ 去掉 for n in `ps aux |sed ...

  5. 【Leetcode】Divide Two Integers

    Divide two integers without using multiplication, division and mod operator. class Solution { public ...

  6. Android 生成xml文件及xml的解析

    1.生成xml文件的两种方式 (1)采用拼接的方式生成xml(不推荐使用) (2)利用XmlSerializer类生成xml文件 package com.example.lucky.test52xml ...

  7. 洛谷 P3267 [JLOI2016/SHOI2016]侦察守卫(树形dp)

    题面 luogu 题解 树形\(dp\) \(f[x][y]表示x的y层以下的所有点都已经覆盖完,还需要覆盖上面的y层的最小代价.\) \(g[x][y]表示x子树中所有点都已经覆盖完,并且x还能向上 ...

  8. 洛谷 P4036 [JSOI2008]火星人(splay+字符串hash)

    题面 洛谷 题解 首先,我们知道求最长公共前缀可以用二分答案+hash来求 因为有修改操作, 考虑将整个字符串的hash值放入splay中 接着就是splay的基本操作了 Code #include& ...

  9. bzoj1076 奖励关 期望dp

    题目传送门 题目大意:总共有k次弹出宝物的机会,宝物共有n种,弹出不同的宝物的概率相同的,是每个宝物都有价值,和选择这个宝物的限制(必须具有特定的宝物),问最后的最优期望是多少. 思路:“正向推概率, ...

  10. vue项目构建过程

    # template 模版项目 > A Vue.js project* 构建过程* 安装过程* 差异点* 打包优化 ## 构建过程```bashbogon:vue-cli caoke$ vue ...