事务对象和命令对象

上次说到数据源对象,这次接着说事务对象和命令对象。
事务是一种对数据源的一系列更新进行分组或批处理以便当所有更新都成功时同时提交这些更新,或者如果任何一个更新失败则不提交任何更新并且回滚整个事务的方法.
命令对象一般是用来执行sql语句并生成结果集的对象

会话对象

在OLEDB中通过以下3中方式支持事务:

  1. ITransactionLocal::StartTransaction
  2. ITransaction::commit
  3. ITransaction::Abort

OLEDB中定义事务和回话对象的接口如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CoType TSession {
[mandatory] interface IGetDataSource;
[mandatory] interface IOpenRowset;
[mandatory] interface ISessionProperties;
[optional] interface IAlterIndex;
[optional] interface IAlterTable;
[optional] interface IBindResource;
[optional] interface ICreateRow;
[optional] interface IDBCreateCommand;
[optional] interface IDBSchemaRowset;
[optional] interface IIndexDefinition;
[optional] interface ISupportErrorInfo;
[optional] interface ITableCreation;
[optional] interface ITableDefinition;
[optional] interface ITableDefinitionWithConstraints;
[optional] interface ITransaction;
[optional] interface ITransactionJoin;
[optional] interface ITransactionLocal;
[optional] interface ITransactionObject;
}

在创建了数据库连接之后使用QueryInterface 查询出IDBCreateSeesion对象,然后调用IDBCreateSession的CreateSession方法创建一个回话对象。
需要注意的是,一个数据源连接可以创建多个回话对象,这里只能通过这种方式创建回话对象,而不能直接通过CoCreateInstance 来创建。一般来说应用中至少创建一个会话对象

Command对象

Command对象的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CoType TCommand {
[mandatory] interface IAccessor;
[mandatory] interface IColumnsInfo;
[mandatory] interface ICommand;
[mandatory] interface ICommandProperties;
[mandatory] interface ICommandText;
[mandatory] interface IConvertType;
[optional] interface IColumnsRowset;
[optional] interface ICommandPersist;
[optional] interface ICommandPrepare;
[optional] interface ICommandWithParameters;
[optional] interface ISupportErrorInfo;
[optional] interface ICommandStream;
}

一般创建Command对象是通过Session对象Query出来一个IDBCreateCommand接口让后调用CreateCommand方法创建。
与会话对象相似,一个会话对象可以创建多个命令对象,但是从上面会话对象的定义可以看出IDBCreateCommand接口是一个可选接口,并不是所有的数据库都支持,因此在创建命令对象的时候一定要注意判断是否支持。对于不支持的可以采用其他办法(一般采用直接打开数据库表的方式)。
下面是一个演示例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define CHECK_OLEDB_INTERFACE(I, iid)\
hRes = (I)->QueryInterface(IID_##iid, (void**)&(p##iid));\
if(FAILED(hRes))\
{\
OLEDB_PRINTF(_T("不支持接口:%s\n"), _T(#iid));\
}\
else\
{\
OLEDB_PRINTF(_T("支持接口:%s\n"), _T(#iid));\
}

BOOL CreateDBSession(IOpenRowset* &pIOpenRowset)
{
DECLARE_BUFFER();
DECLARE_OLEDB_INTERFACE(IDBPromptInitialize);
DECLARE_OLEDB_INTERFACE(IDBInitialize);
DECLARE_OLEDB_INTERFACE(IDBCreateSession);
BOOL bRet = FALSE;
HWND hDesktop = GetDesktopWindow();
HRESULT hRes = CoCreateInstance(CLSID_DataLinks, NULL, CLSCTX_INPROC_SERVER, IID_IDBPromptInitialize, (void**)&pIDBPromptInitialize);
OLEDB_SUCCESS(hRes, _T("创建接口IDBPromptInitialize失败,错误码:%08x\n"), hRes);

hRes = pIDBPromptInitialize->PromptDataSource(NULL, hDesktop, DBPROMPTOPTIONS_WIZARDSHEET, 0, NULL, NULL, IID_IDBInitialize, (IUnknown**)&pIDBInitialize);
OLEDB_SUCCESS(hRes, _T("弹出接口数据源配置对话框失败,错误码:%08x\n"), hRes);

hRes = pIDBInitialize->Initialize();
OLEDB_SUCCESS(hRes, _T("链接数据库失败,错误码:%08x\n"), hRes);

hRes = pIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pIDBCreateSession);

OLEDB_SUCCESS(hRes, _T("创建接口IDBCreateSession失败,错误码:%08x\n"), hRes);
hRes = pIDBCreateSession->CreateSession(NULL, IID_IOpenRowset, (IUnknown**)&pIOpenRowset);
OLEDB_SUCCESS(hRes, _T("创建接口IOpenRowset失败,错误码:%08x\n"), hRes);
bRet = TRUE;

__CLEAR_UP:
OLEDB_SAFE_RELEASE(pIDBPromptInitialize);
OLEDB_SAFE_RELEASE(pIDBInitialize);
OLEDB_SAFE_RELEASE(pIDBCreateSession);
return bRet;
}

int _tmain(int argc, TCHAR *argv[])
{
DECLARE_BUFFER();
DECLARE_OLEDB_INTERFACE(IOpenRowset);
DECLARE_OLEDB_INTERFACE(IDBInitialize);
DECLARE_OLEDB_INTERFACE(IDBCreateCommand);

DECLARE_OLEDB_INTERFACE(IAccessor);
DECLARE_OLEDB_INTERFACE(IColumnsInfo);
DECLARE_OLEDB_INTERFACE(ICommand);
DECLARE_OLEDB_INTERFACE(ICommandProperties);
DECLARE_OLEDB_INTERFACE(ICommandText);
DECLARE_OLEDB_INTERFACE(IConvertType);
DECLARE_OLEDB_INTERFACE(IColumnsRowset);
DECLARE_OLEDB_INTERFACE(ICommandPersist);
DECLARE_OLEDB_INTERFACE(ICommandPrepare);
DECLARE_OLEDB_INTERFACE(ICommandWithParameters);
DECLARE_OLEDB_INTERFACE(ISupportErrorInfo);
DECLARE_OLEDB_INTERFACE(ICommandStream);

CoInitialize(NULL);
if (!CreateDBSession(pIOpenRowset))
{
OLEDB_PRINTF(_T("调用函数CreateDBSession失败,程序即将推出\n"));
return 0;
}

HRESULT hRes = pIOpenRowset->QueryInterface(IID_IDBCreateCommand, (void**)&pIDBCreateCommand);
OLEDB_SUCCESS(hRes, _T("创建接口IDBCreateCommand失败,错误码:%08x\n"), hRes);

hRes = pIDBCreateCommand->CreateCommand(NULL, IID_IAccessor, (IUnknown**)&pIAccessor);
OLEDB_SUCCESS(hRes, _T("创建接口IAccessor失败,错误码:%08x\n"), hRes);

CHECK_OLEDB_INTERFACE(pIAccessor, IColumnsInfo);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommand);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommandProperties);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommandText);
CHECK_OLEDB_INTERFACE(pIAccessor, IConvertType);
CHECK_OLEDB_INTERFACE(pIAccessor, IColumnsRowset);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommandPersist);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommandPrepare);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommandWithParameters);
CHECK_OLEDB_INTERFACE(pIAccessor, ISupportErrorInfo);
CHECK_OLEDB_INTERFACE(pIAccessor, ICommandStream);


__CLEAR_UP:
OLEDB_SAFE_RELEASE(pIOpenRowset);
OLEDB_SAFE_RELEASE(pIDBInitialize);
OLEDB_SAFE_RELEASE(pIDBCreateCommand);

OLEDB_SAFE_RELEASE(pIAccessor);
OLEDB_SAFE_RELEASE(pIColumnsInfo);
OLEDB_SAFE_RELEASE(pICommand);
OLEDB_SAFE_RELEASE(pICommandProperties);
OLEDB_SAFE_RELEASE(pICommandText);
OLEDB_SAFE_RELEASE(pIConvertType);
OLEDB_SAFE_RELEASE(pIColumnsRowset);
OLEDB_SAFE_RELEASE(pICommandPersist);
OLEDB_SAFE_RELEASE(pICommandPrepare);
OLEDB_SAFE_RELEASE(pICommandWithParameters);
OLEDB_SAFE_RELEASE(pISupportErrorInfo);
OLEDB_SAFE_RELEASE(pICommandStream);
CoUninitialize();
return 0;
}

在上述的例子中,首先定义了一个宏,用来判断是否支持对应的接口。
同时定义了一个CreateDBSession方法来创建一个会话对象。在该函数中首先利用上一节的方法创建一个数据库连接,然后在数据源对象上调用QueryInterface来获取接口IDBCreateSeesion,接着利用IDBCreateSeesion接口的CreateSeesion方法创建一个会话对象,由于IDBCreateCommand接口并不是所有的数据源都支持,所以为了保证一定能创建会话对象,我们选择会话对象必须支持的接口IOpenRowset。
在得到会话对象后,尝试创建IDBCreateSession对象,如果它不支持,那么程序直接退出。接着调用IDBCreateCommand接口来创建一个命令对象并尝试query命令对象的其他接口,得出数据源支持哪些接口。
这个例子非常简单,只是为了演示如何创建会话对象和数据源对象罢了。
本节例子代码