行セットでの複数アクセサーの使用
多重アクセサーを使用する必要があるのは、基本的に次の 3 つの場合です。
**複数の読み取り/書き込み行セット。**この場合には、主キーを含むテーブルがあるとします。 主キーも含めて行のすべての列を読み取る必要があります。 また、主キー列への書き込みはできないため、主キーを除くすべての列にデータを書き込むことができるようにします。 この場合は、2 つのアクセサーを設定します。
アクセサー 0 はすべての列を含みます。
アクセサー 1 は主キーを除くすべての列を含みます。
**パフォーマンス。**この場合には、1 つ以上の列に大容量のデータが含まれるとします。たとえば、グラフィックス、サウンド、ビデオのファイルなどです。 行に移動するたびに大容量のデータ ファイルを含む列を取得することは、アプリケーションのパフォーマンスが低下するため望ましくありません。
この場合は、個別にアクセサーを設定できます。最初のアクセサーは大容量のデータを含む列以外のすべての列を含み、それらの列から自動的にデータを取得します。このアクセサーは、自動アクセサーと呼ばれます。 2 番目のアクセサーは、大容量のデータを含む列だけを取得しますが、この列のデータを自動的には取得しません。 その他のメソッドを使用し、要求に応じて大容量のデータを更新またはフェッチできます。
アクセサー 0 は自動アクセサーで、大容量のデータを含む列以外の列をすべて取得します。
アクセサー 1 は自動アクセサーではなく、大容量のデータを含む列を取得します。
自動アクセサーにするかどうかを指定するときは auto 引数を使用します。
**複数の ISequentialStream 列。**この場合には、ISequentialStream データを含む複数の列があるとします。 ただし、各アクセサーは 1 つの ISequentialStream データ ストリームに制限されます。 この問題を解決するには、それぞれが 1 つの ISequentialStream ポインターを含む、複数のアクセサーを設定します。
通常、アクセサーは BEGIN_ACCESSOR マクロと END_ACCESSOR マクロを使用して作成します。 また、db_accessor 属性を使用することもできます。アクセサーの詳細については、「ユーザー レコード」を参照してください。アクセサーを自動アクセサーにするかどうかは、マクロまたは属性によって指定します。
自動アクセサーでは、MoveFirst、MoveLast、MoveNext、MovePrev などの移動メソッドによって、指定したすべての列のデータが自動的に取得されます。 アクセサー 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()
};
メイン コードは次のとおりです。 MoveNext を呼び出すと、アクセサー 0 を使用して、主キー列である ID のデータが自動的に取得されます。 コード例の終わりの方にある Insert メソッドでは、主キー列に書き込まないようにアクセサー 1 が使用されます。
int main(int argc, char* argv[])
{
// Initalize 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;
}