OLEDB事务
学过数据的人一般都知道事务的重要性,事务是一种对数据源的一系列更新进行分组或者批处理以便当所有更新都成功时同时提交更新,或者任意一个更新失败时进行回滚将数据库中的数据回滚到执行批处理中的所有操作之前的一种方法。使用事务保证了数据的完整性。这里不展开详细的说事务,只是谈谈OLEDB在事务上的支持
ITransactionLocal接口
OLEDB中支持事务的接口是ITransactionLocal接口,该接口是一个可选接口,OLEDB并不强制要求所有数据库都支持该接口,所以在使用之前需要先判断是否支持,好在现在常见的几种数据库都支持。
- 该接口属于回话对象,因此要得到该接口只需要根据一个回话对象调用QueryInterface即可
- 调用接口的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事务的更多相关文章
- C# vs MySql
MySqlHelper类 /// <summary> ///MySql操作类 /// </summary> public abstract class MySqlHelper ...
- C#连接、访问MySQL数据库
一.准备工具 visual stuido(本示例使用visual studio 2010) MySql.Data.dll mysql_installer_community_V5.6.21.1_set ...
- DataBase——Mysql的DataHelper
源帖 https://www.cnblogs.com/youuuu/archive/2011/06/16/2082730.html 保护原帖,尊重技术,致敬工匠! using System; usin ...
- 八、.net core 通过数据库配置文件连接操作数据库
一.创建DotNetCore项目 直接创建core项目并不勾选docker支持 二.nuget进行连接MySQL程序集的下载安装 1.MySql.Data.EntityFrameworkCore方式 ...
- DB通用类:MySQL通用类
Mysql类为网络上收集的,没有测试过.. using System; using System.Collections; using System.Collections.Generic; usin ...
- MySql+Socket 完成数据库的增查Demo
需求: 利用MySql数据库结合前端技术完成用户的注册(要求不使用Web服务技术),所以 Demo采用Socket技术实现Web通信. 第一部分:数据库创建 数据库采用mysql 5.7.18, 数据 ...
- C# 基于MySQL的数据层基类(MySQLHelper)
这里介绍下比较简单的方式,引用MySql.Data.dll然后添加一个MySqlHelper类来对MySql数据库进行访问和操作. 1.将MySql.Data.dll引用到你的项目中 下载地址:MyS ...
- C#访问和操作MYSQL数据库
这里介绍下比较简单的方式,引用MySql.Data.dll然后添加一个MySqlHelper类来对MySql数据库进行访问和操作. 1.将MySql.Data.dll引用到你的项目中 下载地址:MyS ...
- MySqlDBHelper数据库连接
这里是本人在工作中用到,希望给大家帮助 public class MySqlDBHelper { //获取一个记录器 private static readonly log4net.ILog log ...
随机推荐
- Flink学习笔记:异步I/O访问外部数据
本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...
- 190221协程与IO模型
一.协程 又称微线程 协程是一种用户态的轻量级的线程 在单线程下实现的并发,例如:yield 优点: 无需线程上下文切换的开销 无需原子操作锁定及同步的开销 方便切换控制流,简化编程模型 高并发,高扩 ...
- 使用window.name 进行数据跨域传递
其中要点, Stpe1,浏览器在Iframe中加载一个异域的页面,这个页面返回 <script>window.name="任何数据"</script>,这时 ...
- c#几种数据库的大数据批量插入(SqlServer、Oracle、SQLite和MySql)
这篇文章主要介绍了c#几种数据库的大数据批量插入(SqlServer.Oracle.SQLite和MySql),需要的朋友可以了解一下. 在之前只知道SqlServer支持数据批量插入,殊不知道Ora ...
- docker记录
# docker run 运行程序 # docker stop (docker kill) 终止容器. (首先应该执行 docker stop!!) #停止所有容器 docker stop $(doc ...
- [BZOJ 4923][Lydsy1706月赛]K小值查询
传送门 势能分析平衡树,splay或treap都可以 放个指针版的就跑 #include <bits/stdc++.h> using namespace std; #define rep( ...
- MITK 手册
可怜这么有用的开源竟然没有中文手册, MITK Plugin Manuals Overview The Basic Image Processing Plugin The DataManager ...
- 安卓app上传到应用宝、360手机助手、小米应用商店、百度手机助手/安卓市场/91助手
1.小米应用商店 小米开放平台网站:https://account.xiaomi.com 注册帐号教程地址:http://dev.xiaomi.com/doc/?p=90 应用提交流程:http:// ...
- maven-eclipse-plugin downloadSources downloadJavadocs
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclip ...
- 有关tensorflow一些问题
1.python版本 采用64位的python 2.系统不支持高版本tensorflow(>1.6),运行报错如下: 问题描述如下: ImportError: DLL load failed: ...