除了之前介绍的接口,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. HDU6300-2018ACM暑假多校联合训练1003-Triangle Partition

    题意是给3n个点,其中不可能存在任意三点共线的情况,让你在其中建n个三角形,点不能重复使用,三角形不能相互覆盖 做法是给每个点排序,按照先y轴排,再x轴排的顺序,三个三个一组从下往上输出,有人说是凸包 ...

  2. VS2017+DLib_19.17详细配置教程

      最近学校布置了一个关于图像融合的作业,于是想利用Learn OpenCV 网站上的Face Morph 教程来设计一个人脸融合的Gif图,但是程序中需要用到DLib库,光是配置这个库就花费了我半天 ...

  3. Configure MongoDB Replica Set

    Table of Contents Introduction Requirements Create Replica Set Add Secondary Members Add an Arbiter ...

  4. Linux 使用echo向文件末尾追加命令

    //echo后边用单引号包围要添加的内容 echo 'add content'>>/home/data/test.sh 注意>>表示在原来的文件末尾上进行追加,如果使用的是&g ...

  5. springboot配置文件的所有属性

    转载:https://blog.csdn.net/qq_28929589/article/details/79439795 # spring boot application.properties配置 ...

  6. 【算法笔记】B1038 统计同成绩学生

    1038 统计同成绩学生 (20 分) 本题要求读入 N 名学生的成绩,将获得某一给定分数的学生人数输出. 输入格式: 输入在第 1 行给出不超过 10​5​​ 的正整数 N,即学生总人数.随后一行给 ...

  7. P2801 教主的魔法

    传送门 $N$ 太大了主席树过不了 考虑分块 对每个块内的元素排序,询问就对大块二分查找,对两边小的部分暴力枚举 修改时维护 $add[i]$ 标记,维护当前块内整块已经加的数 那么整块的就直接修改 ...

  8. 字典序的第K小数字

    今天zyb参加一场面试,面试官听说zyb是ACMer之后立马抛出了一道算法题给zyb:有一个序列,是1到n的一种排列,排列的顺序是字典序小的在前,那么第k个数字是什么?例如n=15,k=7, 排列顺序 ...

  9. SiteServer CMS简介

    SiteServer CMS 是中国在.NET平台下.强大的企业站开源CMS内容管理系统和网站群系统. 能够最低的成本.最少的人力投入在最短的时间内架设好一个功能齐全.性能优异.规模庞大并易于维护的网 ...

  10. 小a的计算器

    链接:https://ac.nowcoder.com/acm/contest/317/A来源:牛客网 小a的数学基础实在太差了,以至于他只会用计算器算数.他的计算器比较特殊,只有+,−,×,/+,−, ...