除了之前介绍的接口,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. #首行输入数n,接下来输入n行数,以空格隔开

    #首行输入数n,接下来输入n行数,以空格隔开 n = int(raw_input())# print nL = []for i in range(n): L.append([int(x) for x ...

  2. jeecms学习笔记

    jeecms学习笔记 内容管理系统 1.栏目列表标签 作用:获取栏目的列表 [@cms_channel_list] [#list tag_list as b] <a href="${b ...

  3. (转)win7英文目录和中文目录,文件夹的别名

    win7英文目录和中文目录,文件夹的别名 在使用win7的很多目录例如我的文档.我的音乐等目录,你会发现文件夹是中文名的,路径也是中文的.但这个不是真的路径.点击一下地址栏,就可以看到真实路径了. 这 ...

  4. 安装jdk1.8,编写环境变量

    好记性不如烂笔头!!!(我这是把jdk放在的/usr/local下,且命令为jdk1.8...复制的时候别搞错位置了) JAVA_HOME=/usr/local/jdk1./ JAVA_BIN=/us ...

  5. jeesite 的提示消息图标

    jeesite  的提示消息图标 jeesite 框架的提示信息 保存数据时 总是显示一个叉子图标 不符合要求 原因: 不加成功两字:如下 后来大神说 保存数据提示语句必须加“”“成功” 才会出现正确 ...

  6. 在 Domoticz 中添加插座开关

    前言 继上一篇的折腾,将 WiFi 插座刷为 ESPEasy,这次介绍一下在 Domoticz 中启用该插座的方法 步骤 在 Domoticz 控制台,设置 → 硬件中添加 Dummy,名称为&quo ...

  7. asp.net boilerplate 配置文件加载

  8. linux安装PHP7以及扩展

    Linux下安装PHP7 事先升级gcc4.8,然后安装PHP7,安装步骤参考:CentOS安装PHP7 1.Linux下编译的php没有php.ini 解决办法:从源代码目录中复制php.ini-d ...

  9. Nginx 安装--图片服务器搭建

    1.  nginx 需要依赖以下模块: gzip模块需要 zlib 库 rewrite模块需要 pcre 库 ssl 功能需要openssl库 1.1.安装pcre 1.         获取pcre ...

  10. python数组(列表、元组及字典)

    python数组的使用 2010-07-28 17:17 1.Python的数组分三种类型: (1) list 普通的链表,初始化后可以通过特定方法动态增加元素. 定义方式:arr = [元素] (2 ...