行セットでの複数アクセサーの使用
複数のアクセサーを使う必要がある場合は、3 つの基本的なシナリオがあります。
複数の読み取り/書き込み行セット。 このシナリオでは、主キーを設定したテーブルがあります。 主キーを含め、行のすべての列を読み取る必要があります。 また、(主キー列に書き込めないので) 主キーを除くすべての列にデータを書き込む必要があります。 この場合、2 つのアクセサーを設定します。
アクセサー 0 にはすべての列が含まれています。
アクセサー 1 には主キーを除くすべての列が含まれています。
パフォーマンス。 このシナリオでは、1 つ以上の列に大量のデータ (グラフィック、サウンド、ビデオ ファイルなど) が格納されています。 行に移動するたびに大きなデータ ファイルを保持する列を取得すると、アプリケーションのパフォーマンスが低するため、避けたいところです。
この場合、アクセサーを分けて設定することができます。1 つ目のアクセサーには、大きなデータを保持する列を除くすべての列を含め、これらの列から自動的にデータを取得するようにします。1 つ目のアクセサーは自動アクセサーです。 2 つ目のアクセサーでは、大きなデータを保持する列のみを取得します。ただし、この列からはデータを自動的に取得しません。 他の方法を使って、大きなデータをオンデマンドで更新またはフェッチすることもできます。
アクセサー 0 は自動アクセサーです。大きなデータを保持する列を除くすべての列を取得します。
アクセサー 1 は自動アクセサーではなく、大きなデータを保持する列を取得します。
auto 引数を使って、自動アクセサーかどうかを指定します。
複数の ISequentialStream 列。 このシナリオでは、
ISequentialStream
データを保持する列が複数あります。 ただし、各アクセサーは 1 つのISequentialStream
データ ストリームに制限されています。 この問題を解決するには、複数のアクセサーを設定し、それぞれが 1 つのISequentialStream
ポインターを設定します。
通常、アクセサーの作成には BEGIN_ACCESSOR と END_ACCESSOR の各マクロを使います。 db_accessor 属性を使うこともできます (アクセサーの詳細については、 ユーザー レコード.)マクロまたは属性は、アクセサーが自動アクセサーか非自動アクセサーかを指定します。
自動アクセサーの場合、
MoveFirst
、MoveLast
、MoveNext
、MovePrev
などの move メソッドを使って、指定したすべての列のデータを自動的に取得します。 アクセサー 0 は自動アクセサーにするようにします。非自動アクセサーの場合、
Update
、Insert
、Fetch
、Delete
などのメソッドを明示的に呼び出すまで、取得は行われません。 前述のシナリオでは、移動ごとにすべての列を取得したくない場合もあります。 次に示すように、1 つ以上の列を別のアクセサーに配置し、それを非自動アクセサーにすることができます。
次の例では、複数のアクセサーを使って、SQL Server pubs データベースの jobs テーブルの読み取りと書き込みを行っています。 この例は、複数のアクセサーの最も一般的な使用方法です。前述の「複数の読み取り/書き込み行セット」シナリオを参照してください。
ユーザー レコード クラスは次のとおりです。 2 つのアクセサーを設定します。アクセサー 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()
};
main コードは次のとおりです。 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;
}