除了之前介绍的接口,OLEDB还定义了其他一些支持回调的接口,可以异步操作OLEDB对象或者得到一些重要的事件通知,从而使应用程序有机会进行一些必要的处理。其中较有用的就是结果集对象的变更通知接口。通过这个接口可以及时得到结果集被增删改数据变化的情况,并有机会进行必要的数据合法性审核。

数据变更通知的接口是IRowsetNotify,数据源对象要求的异步通知事件接口是IDBAsynchNotify。

标准COM的回调方式

为了更好的理解OLEDB的回调,先回忆一下标准COM的回调方式。

COM组件除了提供函数供应用程序主动调用这种方式外,还提供了回调这种方式,这种方式由用户实现相应的接口,然后由COM组件来调用,这样我们就可以知道COM组件的运行状态,同时能针对一些情况进行处理,比如处理内存耗尽,获取用户输入等等。

要支持事件回调的COM组件必须提供IConnectionPointContainer接口,调用者调用IConnectionPointContainer接口的FindConnectPoint接口,通过回调事件的IID找到特定的事件挂载点,然后调用接口的Advise方法将挂载点与对应的回调函数关联起来(一个事件可以对应多个回调函数)这样当事件发生时就可以调用对应的回调函数。

这个机制有点类似于QT中的信号和槽函数机制,QT中的事件是实现定义好的,可以直接使用而这里是需要通过事件ID找到具体事件,拥有事件后,与QT步骤类似,都是需要将事件与对应的回调函数绑定。

IRowsetNotify接口

对于OLEDB结果集来说,最重要的事件接口是IRowsetNotify,该接口提供三个重要的通知函数:

  • OnFieldChange:列数据发生变更
  • OnRowChange: 行发生变化,尤其是删除或者插入行
  • OnRowsetChange:修改数据被提交

通过这些事件函数具体实现时设置不同的返回值可以控制结果集对象对修改做出的响应,比如:返回S_OK表示接受这个修改,返回S_FALSE明确拒绝接受这个修改。这样就给了一个最终反悔的机制。

这些函数有两个重要的参数:

  • DBREASON: 发生变化的原因
  • DBEVENTPHASE:事件被触发的阶段

    通过对这两个参数组合的判定,可以准确的判断出结果集中数据变化的动态追踪及情况

DBREASON 参数的相关值

  • DBREASON_ROW_ASYNCHINSERT:异步插入
  • DBREASON_ROWSET_FETCHPOSITIONCHANGE:结果集的行指针发生变化,当调用类似 IRowset::GetNextRows or IRowset::RestartPosition时触发
  • DBREASON_ROWSET_RELEASE:当结果集被释放的时候触发
  • DBREASON_ROWSET_CHANGED:数据库中某些元数据发生变化时触发,这里是指描述数据库表字段的一些信息发生变化,比如表字段的大小,类型这些数据,要修改这些数据需要用户具有一定的权限,一般情况下不会触发这个原因
  • DBREASON_COLUMN_SET:当行数据被设置时触发(这里只是已存在的行数据被设置,不包括新增行),一般调用SetData时会触发
  • DBREASON_COLUMN_RECALCULATED:当列的值发生变更时触发,一般是调用SetData
  • DBREASON_ROW_ACTIVATE:当用户修改行指针导致行的状态由未激活变为激活时触发
  • DBREASON_ROW_RELEASE:当调用ReleaseRows释放某些行句柄的时候触发
  • DBREASON_ROW_DELETE:当行被删除时触发
  • DBREASON_ROW_FIRSTCHANGE:当某些行的某列被设置新值后又改变了当前行指针的指向时,它会被第一时间触发,并且它的触发会早于DBREASON_COLUMN_SET,这个事件只会在使用延迟更新的时候才会产生。
  • DBREASON_ROW_INSERT:在插入新行的时候触发
  • DBREASON_ROW_UNDOCHANGE:当调用Undo放弃修改的时候触发
  • DBREASON_ROW_UNDOINSERT:当调用Undo放弃插入新行的时候触发
  • DBREASON_ROW_UNDODELETE:当调用Undo放弃删除的时候触发
  • DBREASON_ROW_UPDATE:当调用Update进行更新的时候触发

DBEVENTPHASE

这个参数表示当前执行的状态,一般操作数据结果集有5个状态,分别对应这样的5个值:

  • DBEVENTPHASE_OKTODO:准备好了去做,当应用程序需要操作结果集的时候会发送一个DBEVENTPHASE_OKTODO到监听程序(在这暂时就理解为OLEDB的数据源),监听程序收到后不会立马去执行该动作,而是会返回S_OK表示它知道了这个请求,或者返回S_FALSE拒绝这个请求
  • DBEVENTPHASE_ABOUTTODO:当数据源针对 DBEVENTPHASE_OKTODO返回S_OK时,应用程序会给一个信号,告知数据源可以进行执行动作之前最后的准备工作,这部完成之后,数据源会异步的执行相关请求操作
  • DBEVENTPHASE_DIDEVENT:当数据源执行完这次的请求之后会到这个状态,此时数据库表的数据已经更新
  • DBEVENTPHASE_FAILEDTODO:当之前的某一步发生错误时会进入这个状态,此时会产生回滚,将数据还原到最开始的状态。

下面是数据状态迁移图,这个图很形象的展示了在某个操作执行过程中的各种状态变化

结果集对象事件通知接口的使用方法

  1. 定义一个派生自IRowsetNotify接口的类,并实现其接口中的所有方法
  2. 设置结果集对象属性集DBPROPSET_ROWSET中的DBPROP_IConnectionPointContainer属性为VARIANT_TRUE
  3. 获得结果集对象
  4. 调用IRowset::QueryInterface方法得到IConnectionPointContainer接口指针
  5. 调用IConnectionPointContainer::FindConnectionPoint方法得到IRowsetNotify接口对应的IConnectionPoint接口指针
  6. 实例化一个第一步中创建的类
  7. 调用IConnectionPoint::Advise并传递该对象指针
  8. 对结果集对象进行操作,此时如果事件条件成立,结果集对象会调用该对象的相应方法通知调用者触发了什么事件

详细的内容可以参考MSDN IRowsetNotify

例子

最后来看使用的具体例子

class CCOMRowsetNotify:
public IRowsetNotify
{
public:
CCOMRowsetNotify(void);
virtual ~CCOMRowsetNotify(void); protected:
virtual HRESULT FindConnectionPointContainer(IUnknown *pIUnknown, REFIID rrid, IConnectionPoint* &pIcp);
public:
virtual HRESULT Addvise(IUnknown *pIUnknown, REFIID rrid);
virtual HRESULT UnAddvise(IUnknown *pIUnknown, REFIID rrid); public:
virtual STDMETHODIMP_(ULONG) AddRef(void);
virtual STDMETHODIMP_(ULONG) Release(void);
virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); virtual STDMETHODIMP OnFieldChange (IRowset *pRowset, HROW hRow, DBORDINAL cColumns, DBORDINAL rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase,BOOL fCantDeny);
virtual STDMETHODIMP OnRowChange (IRowset *pRowset, DBCOUNTITEM cRows,const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);
virtual STDMETHODIMP OnRowsetChange (IRowset *pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny); protected:
ULONG m_uRef;
DWORD m_dwCookie;
};

使用时首先定义一个派生自IRowsetNotify的类,并实现所有的接口方法

if (!OpenTable(pIOpenRowset, pIRowsetChange))
{
COM_PRINTF(_T("打开表失败\n"));
goto __CLEAN_UP;
} RowsetNotify.Addvise(pIRowsetChange, IID_IRowsetNotify); HRESULT CCOMRowsetNotify::FindConnectionPointContainer(IUnknown *pIUnknown, REFIID rrid, IConnectionPoint* &pIcp)
{
IConnectionPointContainer* pICpc = NULL;
HRESULT hr = pIUnknown->QueryInterface(IID_IConnectionPointContainer,(void**)&pICpc);
if(FAILED(hr))
{
COM_PRINTF(_T("通过IRowset接口获取IConnectionPointContainer接口失败,错误码:0x%08X\n"),hr);
return hr;
}
hr = pICpc->FindConnectionPoint(rrid,&pIcp);
if(FAILED(hr))
{
COM_PRINTF(_T("获取IConnectionPoint接口失败,错误码:0x%08X\n"),hr);
COM_SAFE_RELEASE(pIcp);
return hr;
} return hr;
} HRESULT CCOMRowsetNotify::Addvise(IUnknown *pIUnknown, REFIID rrid)
{
IConnectionPoint *pIcp = NULL;
HRESULT hRes = FindConnectionPointContainer(pIUnknown, rrid, pIcp);
if (S_OK != hRes)
{
return hRes;
} hRes = pIcp->Advise(dynamic_cast<IRowsetNotify*>(this), &m_dwCookie);
COM_SAFE_RELEASE(pIcp);
return hRes;
}

上述代码先打开数据结果集,然后调用类对象的Addvise方法传入IID_IRowsetNotify接口指针,在方法Addvise中做的主要操作是首先使用传入的接口指针查找到接口IConnectionPointContainer,然后利用IConnectionPointContainer接口的FindConnectionPoint方法找到对应的挂载点,最后调用IConnectionPointContainer的Advise方法将对应的类对象挂载到挂载点上,这样在后面操作结果集时就会调用对应的On函数,完成对应事件的处理

最后放上完整代码的链接

点击这里获取完整代码


OLEDB 数据变更通知的更多相关文章

  1. OLEDB数据源和目标组件

    在SSIS工程的开发过程中,OLEDB 数据源和目标组件是最常用的数据流组件.从功能上讲,OLEDB 数据源组件用于从OLEDB 提供者(Provider)中获取数据,传递给下游组件,OLEDB提供者 ...

  2. Zookeeper Watcher 机制 -- 数据变更通知 ?

    Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务 端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通 知来实现分布式的通知功 ...

  3. Zookeeper Watcher 机制 -- 数据变更通知 ?

    Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务 端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通 知来实现分布式的通知功 ...

  4. OleDB Destination 用法

    第一部分:简介 OleDB Destination component 是将数据流load 到destination,共有5种Data Access Mode,一般的Destination compo ...

  5. 把Excel的数据导入到数据库

    将Excel作为数据源,将数据导入数据库,是SSIS的一个简单的应用,下图是示例Excel,数据列是code和name 第一部分,Excel中的数据类型是数值类型 1,使用SSDT创建一个packag ...

  6. 导出数据到Excel方法总结

    一,问题的提出 近来在网上经常有人问怎样把数据导出到Excel中?针对这个问题网上也有很多资料.大都比较的琐碎.本人当前从事的项目中,刚好涉及到这些内容.就顺便做了一些归纳整理.共享给大家.避免大家再 ...

  7. ZooKeeper 典型应用场景-数据发布与订阅

    ZooKeeper 是一个高可用的分布式数据管理与系统协调框架.基于对 Paxos 算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得 ZooKeeper 可以解决很多分 ...

  8. Pulsar 联合 TiDB 推出大数据场景数据应用分析解决方案

    方案概述 大数据时代,各类应用对消息解决方案的要求不仅仅是数据的流动,而是要在持续增长的服务和应用中传输海量数据,进行智能的处理和分析,帮助业务做出更加精准的决策. Pulsar 与 TiDB 联合解 ...

  9. 【Soul网关探秘】http数据同步-Admin通知前处理

    引言 本篇开始研究 Soul 网关 http 数据同步,将分为三篇进行分析: <Admin通知前处理> <变更通知机制> <Bootstrap处理变更通知> 希望三 ...

随机推荐

  1. js 三大家族之offset

    JS中的offset家族: 一.offsetWidth与offsetHeight: 获取的是元素的实际宽高 = width + border + padding 注意点: 1.可以获取行内及内嵌的宽高 ...

  2. Image组件

    /* Image* 用于显示图片的组件,包括网络图片,静态资源 * 常用性能: * resizeMode 图片适应模式 cover , contain , stretch * source 图片的引用 ...

  3. 数据库分库分表配置sharding-jdbc

    @Bean(name = "shardingDataSource", destroyMethod = "close") @Qualifier("sha ...

  4. sql的编写需要注意优化

    使用limit对查询结果的记录进行限定 避免select *,将需要查找的字段列出来 使用连接(join)来代替子查询 拆分大的delete或insert语句 可通过开启慢查询日志来找出较慢的SQL ...

  5. Django 中 admin 的执行流程

    Django 中 admin 的执行流程 1 循环加载执行所有已经注册的 app 中的 admin.py 文件 def autodiscover(): autodiscover_modules('ad ...

  6. 洛谷P2146 [NOI2015]软件包管理器

    https://www.luogu.org/problemnew/show/P2146 传送门 简单的树链剖分......维护下当前安装了多少个包......修改后查询下就行了......附上极其丑陋 ...

  7. 【算法笔记】B1015 德才论

    1015 德才论 (25 分) 宋代史学家司马光在<资治通鉴>中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人.凡取人之术,苟不得圣人,君 ...

  8. JAVA通过网站域名URL获取该网站的源码(2018

    import java.io.ByteArrayOutputStream;   import java.io.InputStream;   import java.net.HttpURLConnect ...

  9. Intelligent Poetry

    Readme: Creat poems. import re import random from collections import Counter def Creat_Poem(number): ...

  10. 04-树6 Complete Binary Search Tree (30 分)

    A Binary Search Tree (BST) is recursively defined as a binary tree which has the following propertie ...