在一个行集合上使用多个访问器
在三种基本方案中需要使用多个访问器:
多个读/写行集。 在此方案中,你有一个具有主键的表。 你希望能够读取行中的所有列,包括主键。 你还希望能够将数据写入除主键以外的所有列(因为无法写入主键列)。 在此情况下,设置两个访问器:
访问器 0 包含所有列。
访问器 1 包含除主键以外的所有列。
性能。 在此方案中,一个或多个列具有大量数据,例如图形、声音或视频文件。 每次移动到行时,你可能都不想检索具有大型数据文件的列,因为这样做会减慢应用程序的性能。
可以设置单独的访问器,其中第一个访问器包含除具有大型数据的列以外的所有列,会自动从这些列中检索数据;第一个访问器是自动访问器。 第二个访问器只检索包含大型数据的列,但它不会自动从此列中检索数据。 可以让其他方法按需更新或提取大型数据。
访问器 0 是自动访问器;它检索除具有大型数据的列以外的所有列。
访问器 1 不是自动访问器;它检索除具有大型数据的列。
使用自动参数指定访问器是否为自动访问器。
多个 ISequentialStream 列。 在此方案中,你具有多个包含
ISequentialStream
数据的列。 但是,每个访问器都限制到一个ISequentialStream
数据流。 若要解决此问题,请设置多个访问器,每个访问器都有一个ISequentialStream
指针。
通常使用 BEGIN_ACCESSOR 和 END_ACCESSOR 宏创建访问器。 也可以使用 db_accessor 属性。 (访问器在用户记录中进行了进一步介绍。)宏或属性指定访问器是自动访问器还是非自动访问器:
在自动访问器中,移动方法(例如
MoveFirst
、MoveLast
、MoveNext
和MovePrev
)会自动检索所有指定列的数据。 访问器 0 应该是自动访问器。在非自动访问器中,显式调用方法(如
Update
、Insert
、Fetch
或Delete
)之前不会进行检索。 在上述方案中,你可能不希望在每次移动中都检索所有列。 可以将一个或多个列放置在单独的访问器中,并使该访问器成为非自动访问器,如下所示。
以下示例使用多个访问器读取和写入SQL Server pubs 数据库的作业表。 此示例是多个访问器的最常见用法;请参阅上面的“多个读/写行集”方案。
用户记录类如下所示。 它设置两个访问器:访问器 0 仅包含主键列 (ID),访问器 1 包含其他列。
class CJobs
{
public:
enum {
sizeOfDescription = 51
};
short nID;
char szDescription[ sizeOfDescription ];
short nMinLvl;
short nMaxLvl;
DWORD dwID;
DWORD dwDescription;
DWORD dwMinLvl;
DWORD dwMaxLvl;
BEGIN_ACCESSOR_MAP(CJobs, 2)
// Accessor 0 is the automatic accessor
BEGIN_ACCESSOR(0, true)
COLUMN_ENTRY_STATUS(1, nID, dwID)
END_ACCESSOR()
// Accessor 1 is the non-automatic accessor
BEGIN_ACCESSOR(1, true)
COLUMN_ENTRY_STATUS(2, szDescription, dwDescription)
COLUMN_ENTRY_STATUS(3, nMinLvl, dwMinLvl)
COLUMN_ENTRY_STATUS(4, nMaxLvl, dwMaxLvl)
END_ACCESSOR()
END_ACCESSOR_MAP()
};
主代码如下所示。 调用 MoveNext
会使用访问器 0 自动从主键列 ID 中检索数据。 请注意末尾附近的 Insert
方法如何使用访问器 1 来避免写入主键列。
int main(int argc, char* argv[])
{
// Initialize COM
::CoInitialize(NULL);
// Create instances of the data source and session
CDataSource source;
CSession session;
HRESULT hr = S_OK;
// Set initialization properties
CDBPropSet dbinit(DBPROPSET_DBINIT);
dbinit.AddProperty(DBPROP_AUTH_USERID, OLESTR("my_user_id"));
dbinit.AddProperty(DBPROP_INIT_CATALOG, OLESTR("pubs"));
dbinit.AddProperty(DBPROP_INIT_DATASOURCE, OLESTR("(local)"));
hr = source.Open("SQLOLEDB.1", &dbinit);
if (hr == S_OK)
{
hr = session.Open(source);
if (hr == S_OK)
{
// Ready to fetch/access data
CTable<CAccessor<CJobs>> jobs;
// Set properties for making the rowset a read/write cursor
CDBPropSet dbRowset(DBPROPSET_ROWSET);
dbRowset.AddProperty(DBPROP_CANFETCHBACKWARDS, true);
dbRowset.AddProperty(DBPROP_CANSCROLLBACKWARDS, true);
dbRowset.AddProperty(DBPROP_IRowsetChange, true);
dbRowset.AddProperty(DBPROP_UPDATABILITY,
DBPROPVAL_UP_INSERT | DBPROPVAL_UP_CHANGE |
DBPROPVAL_UP_DELETE);
hr = jobs.Open(session, "jobs", &dbRowset);
if (hr == S_OK)
{
// Calling MoveNext automatically retrieves ID
// (using accessor 0)
while(jobs.MoveNext() == S_OK)
printf_s("Description = %s\n", jobs.szDescription);
hr = jobs.MoveFirst();
if (hr == S_OK)
{
jobs.nID = 25;
strcpy_s(&jobs.szDescription[0],
jobs.sizeOfDescription,
"Developer");
jobs.nMinLvl = 10;
jobs.nMaxLvl = 20;
jobs.dwDescription = DBSTATUS_S_OK;
jobs.dwID = DBSTATUS_S_OK;
jobs.dwMaxLvl = DBSTATUS_S_OK;
jobs.dwMinLvl = DBSTATUS_S_OK;
// Insert method uses accessor 1
// (to avoid writing to the primary key column)
hr = jobs.Insert(1);
}
jobs.Close();
}
session.Close();
}
source.Close();
}
// Uninitialize COM
::CoUninitialize();
return 0;
}