共通ドキュメント

データアクセス

概要

  • 基本概念
  • Modelクラス
  • Connecitonクラス
  • Commandクラス
  • DataSourceクラス
  • Criteriaクラス
  • データ接続設定
  • データソース設定
  • 例外処理

基本概念

クラス図

  • Modelオブジェクトはデータの操作方法を提供する。
  • DataSourceオブジェクトは特定のデータ構造(Entityの構造)に関する操作を提供する。
  • データはConnectionを通して保存され、読み出される。
  • Connectionの操作はCommandオブジェクトを通して行う。
  • Modelオブジェクトは以下のいずれかの方法でデータを操作する。
    • Commandオブジェクトを作成し、データ接続への直接操作(query,update)を実行する
    • DataSourceオブジェクトを作成してEntityオブジェクトに対する操作(save,find)を実行する。
    • DataSourceでの検索はCriteriaオブジェクトを使用する

Modelクラス

業務オブジェクトを表現する。

業務オブジェクト とは : 業務ロジックの中である程度のまとまりを持つ「機能の単位」 (としておく...)

作成要件

  • 業務オブジェクトごとに一つのModelクラスを定義し、業務オブジェクトに対する操作を提供できるように実装する。
  • トランザクションの管理はConnectionクラスを使用してModelクラスが行う。
  • 表示要素は排除し、する。(HTMLタグなどを出力に埋め込まない、HTMLタグからの要素の取り出しなどをこのクラスに直接書かない)
  • コードの見通しを良くするために特定ロジックのメソッドを「コマンド」クラス(コマンドパターンのコマンド、Connection操作用のCommandとは別物)としてまとめても良い。

構成要素

以下のものを使用して作成する。

Connectionデータを保存|読み込みするための接続先。トランザクション管理も行う
DataSource特定のデータ構造に対する処理を行うデータソース
CriteriaDataSourceに選択条件を与える、またはCommand作成時の選択条件を作成する
CommandConnectionに対して直接操作を行うためのCommand

Connectionクラス

基本

データベースなどに接続してデータの保存、読み出しを行う。定義は抽象化されていて、データベースの実体で共通化した操作のみを公開する。

Connectionの実体(インスタンス)は直接生成せず、ConnectionFactory#getConnection()を使用して取得する。

公開メソッド

connect接続を開く
disconnect接続を閉じる
beginTransactionトランザクションを開始する
commitTransactionトランザクションを確定する
rollbackTransactionトランザクションを破棄する
query選択クエリーを実行する。
queryBatch複数の選択クエリーを実行する。
update更新クエリーを実行する。
updateBatch複数の更新クエリーを実行する。
quote接続先データベースに応じた文字列引用処理(シングルクォーテーションを二重化する、など)
systemDate接続先データベースの現在時刻を取得する
describeTableテーブル情報をEntityPropertyクラスとして取得する。
describeStoredProcedureストアドプロシージャをEntityPropertyクラスとして取得する。

接続先の決定

接続先の設定に名前をつけ、その名前を指定することで接続先を決定する。>>設定

デフォルトの接続先を使う

//接続取得
$connection = CFW_Data_ConnectionFactory::getConnection(array("default"));
//以下、$connection を使った処理...

system,functionごとに接続先を定義している場合

//接続取得
$connection = CFW_Data_ConnectionFactory::getConnection(array("system","funciton"));
//以下、$connection を使った処理...

データの操作

1.Commandオブジェクトを作成しCommandを実行する

//接続取得
$connection = CFW_Data_ConnectionFactory::getConnection(array("system","funciton"));
try{
	$connection->connect();
	
	//コマンド準備
	$command = new CFW_Data_Command("select * from users");
	$command->setConnection($connection);
	//実行して結果を得る
	$result = $command->executeQuery();
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}
//後始末
$connection->disconect();

2.またはあらかじめ作成した複数のCommandオブジェクトを実行する

//接続取得
$connection = CFW_Data_ConnectionFactory::getConnection(array("system","funciton"));
try{
	$connection->connect();
	
	//コマンド準備
	$command1 = new CFW_Data_Command("select * from users where user_id = ?");
	$command1->addParameter(new CFW_Data_Parameter(":user_id",1);
	$command2 = new CFW_Data_Command("select * from auth_users_roles where user_id = ?");
	$command2->addParameter(new CFW_Data_Parameter(":user_id",1);
	//実行して結果を得る
	$result = $connection->queryBatch(array($command1,$command2));
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}
	
//後始末
$connection->disconect();

トランザクションの処理

beginTransaction(),commitTransaction(),rollbackTransaction()を使用する

//接続取得
$connection = CFW_Data_ConnectionFactory::getConnection(array("system","funciton"));
try{
	$connection->connect();
	//トランザクション開始
	$connection->begeinTransaction();
	
	//コマンド準備
	$command1 = new CFW_Data_Command("insert into users values(?,?)");
	$command1->addParameter(new CFW_Data_Parameter(":user_id",1);
	$command1->addParameter(new CFW_Data_Parameter(":user_name","sample user");
	
	//コマンド実行先設定
	$command->setConnection($connection);
	
	//実行して結果を得る
	$result = $command1->executeUpdate();
	
	//トランザクション確定
	$connection->commitTransaction();
}
catch(Exception $ex){
	//トランザクション破棄
	$connection->rollbackTransaction();
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}

//後始末
$connection->disconect();

実装クラス

接続先データベースごとに異なる実装クラスが存在する。

  • SQLServer用接続
  • Oracle用接続
  • SQLite用接続
  • MySQL用接続
  • etc...

使用上の注意

  • 画面のコードからconnect,disconnect以外を呼び出さない。
  • 開いたら閉じる。
  • (C#)staticな領域にConnectionオブジェクトを保持しない。
  • Connectionオブジェクトを使用している間は例外を捕捉し、開かれた接続が確実に閉じられるようにする。
  • 例外(Exception)を捨てない。異常な状態は異常な状態として扱う。

Commandクラス

基本

Connectionのデータに対する操作を行う。

公開メソッド

setConnection既に開かれている接続を設定する
addParamterパラメータを追加する
executeQuery選択クエリーを実行する
executeUpdate更新クエリーを実行する
executeQueryAsObject選択クエリーを実行し、指定したクラスのインスタンスのリストを作成する。

クエリーの実行

クエリーテキストとパラメータからCommnadオブジェクトを作成し、executeQuery()を実行する。

//接続取得
$connection = CFW_Data_ConnectionFactory::getConnection(array("system","funciton"));
try{
	$connection->connect();
	
	//コマンド準備
	$command1 = new CFW_Data_Command("select * from users where user_id = ?");
	$command1->addParameter(new CFW_Data_Parameter(":user_id",1);
	//接続先を設定する
	$command->setConnection($connection);
	//実行して結果を得る
	$result = $command->executeQuery());
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}
	
//後始末
$connection->disconect();

使用上の注意

  • コマンド文字列内に動的パラメータを含めない
  • parametersコレクションにアクセスできるとしても、このコレクションに直接パラメータオブジェクトを追加したりしない。($command->parameters[] = $p1;とかしない)

DataSourceクラス

基本

特定のデータ構造のデータに対する操作を行う。データ構造はEntityPropertyに設定する。

公開メソッド

connect接続先への接続を開く
disconnect接続先への接続を閉じる
setConnection既に開かれている接続を設定する
setPropertyEntityProperty(データ構造)を設定する
save渡されたエンティティの状態(isNew,isModified,isDeleted)に基づき追加、更新、削除を実行する
update指定選択条件の行を指定データで更新する。
insert指定データを追加。
delete指定選択条件の行を削除する。
find指定選択条件の行を取得する。
findByPk主キーを指定して行を取得する。

データを追加する

save()を呼び出した時エンティティがisNew 且つ isModified の場合データを追加する。

//使用するデータの準備
$entity = new Sample_Models_SampleEntity();
$entity->id = "new";
$entity->name = "new name";
$entity->description = "新しいデータ";
//新規データ、且つ変更済みを設定
$entity->isNew = true;
$entity->isModified = true;

//データ構造の準備
$property = new Sample_Models_SampleProperty();

//データソースインスタンス生成、接続
$datasource = CFW_Data_DataSourceFactory::getDataSource(array("name"));
try{
	//接続する
	$datasource->connect();
	
	//データソースが扱うデータ構造を設定
	$datasource->setProperty($property);
	
	//保存実行
	$result = $datasource->save($entity);
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}
//後始末
$datasource->disconect();

データを更新する

save()を呼び出した時エンティティがnot isNew 且つ isModified の場合データを更新する。(対象が存在すれば、では無い)

//使用するデータの準備
$entity = new Sample_Models_SampleEntity();
$entity->id = "old";
$entity->name = "modify name";
$entity->description = "新しいデータを変更";
//Newではない
$entity->isNew = false;
//変更済み
$entity->isModified = true;

//データ構造の準備
$property = new Sample_Models_SampleProperty();

//データソースインスタンス生成、接続
$datasource = CFW_Data_DataSourceFactory::getDataSource(array("name"));
try{
	$datasource->connect();
	
	//データソースが扱うデータ構造を設定
	$datasource->setProperty($property);
	
	//保存実行
	$result = $datasource->save($entity);
}
catch(Exception $ex){
}
//後始末
$datasource->disconect();

登録日、更新日

EntityPropertyクラスのcreatedAtField,modifiedAtFieldに指定された列についてはDataSourceオブジェクト内部でデータを設定して更新する。アプリケーションサイドで特別なことをやる必要は無い。ただし、doUpdate == falseに設定されている場合は更新しない。

既定ではそれぞれ"created_at","modified_at"だが、列名を指定することで他の列も設定可能

//使用するデータの準備
				
$entity = new Sample_Models_SampleEntity();
$entity->id = "old";
$entity->name = "modify name";
$entity->description = "新しいデータを変更";
$entity->createdAt = new Date("2001/2/3"); //無視されて現在日付が設定される
$entity->modifiedAt = new Date("2001/2/3"); //無視されて現在日付が設定される
$entity->isNew = true;
$entity->isModified = true;

//データ構造の準備
$property = new Sample_Models_SampleProperty();

//データソースインスタンス生成、接続
$datasource = CFW_Data_DataSourceFactory::getDataSource(array("name"));
try{
	$datasource->connect();
	
	//データソースが扱うデータ構造を設定
	$datasource->setProperty($property);
	
	//保存実行
	$result = $datasource->save($entity);
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}
//後始末
$datasource->disconect();

複数件のデータを準備し、追加または更新する

saveメソッドに渡したデータが配列(あるいはリスト)の場合、全てのデータについてisNew,isModifiedを確認して挿入|更新を実行する。

//使用するデータの準備
$entities = array();
//1件目
$entity = new Sample_Models_SampleEntity();
$entity->id = "new";
$entity->name = "new name";
$entity->description = "新しいデータ";
$entity->isNew = true; //新規データ
$entity->isModified = true;
$entities[] = $entity;
//2件目
$entity = new Sample_Models_SampleEntity();
$entity->id = "new2";
$entity->name = "modified";
$entity->description = "データ変更";
$entity->isNew = false; //既存データ
$entity->isModified = true;
$entities[] = $entity;
//...以下略

//データ構造の準備
$property = new Sample_Models_SampleProperty();

//データソースインスタンス生成、接続
$datasource = CFW_Data_DataSourceFactory::getDataSource(array("name"));
try{
	$datasource->connect();
	
	//データソースが扱うデータ構造を設定
	$datasource->setProperty($property);
	
	//保存実行
	$result = $datasource->save($entities);
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}

//後始末
$datasource->disconect();

データの削除

isDeletedを設定したデータを削除する。

//使用するデータの準備
$entity = new Sample_Models_SampleEntity();
//この場合設定するのはキー情報だけでよい。
//通常はあらかじめfind()などで読み込んだデータを使う
$entity->id = "old";
$entity->isDeleted = true;

//データ構造の準備
$property = new Sample_Models_SampleProperty();

//データソースインスタンス生成、接続
$datasource = CFW_Data_DataSourceFactory::getDataSource(array("name"));
$datasource->connect();

//データソースが扱うデータ構造を設定
$datasource->setProperty($property);

//保存実行
$result = $datasource->save($entity);

//後始末
$datasource->disconect();

データの選択

Criteriaオブジェクトを作成して選択条件とする。

table1.column1 = 'aaa' AND table1.column1 = 'bbb' を選択する。

//検索条件の設定
$criteria = new CFW_Data_Criteria();
$criteria->addWhere(new CFW_Data_Criteria_ColumnValueCondition( "table1.column1","aaa" ));
$criteria->addWhere(new CFW_Data_Criteria_ColumnValueCondition( "table1.column2,"bbb" ));
//ソート
$criteria->addOrderBy("table1.column2");
$criteria->addOrderBy("table1.column1");

//データ構造の準備
$property = new Sample_Models_SampleProperty();

//データソースインスタンス生成、接続
$datasource = CFW_Data_DataSourceFactory::getDataSource(array("name"));
try{
	$datasource->connect();
	
	//データソースが扱うデータ構造を設定
	$datasource->setProperty($property);
	
	//保存実行
	$result = $datasource->find($criteria);
}
catch(Exception $ex){
	$logger->error(__METHOD__,$ex->getMessage());
	$logger->error(__METHOD__,$ex->getStack());
}
//後始末
$datasource->disconect();

使用上の注意

  • 行選択条件はCriteriaオブジェクトを使用する。
  • save()の時、エンティティの状態を必ず設定する。
  • setPropertyを使わない場合、データ構造はConnection#describeTableを使用してデータベースから直接構築する。パフォーマンスが気になるときには要注意
  • トランザクションの管理はConnectionオブジェクトを直接制御する。

Criteriaクラス

基本

データ選択条件を設定する

公開メソッド

addWhereWHERE条件を追加する
addOrderByORDER BY条件を追加する
addHavingHAVING条件を追加する
addGroupByGROUP BY条件を追加する
setLimit取得件数を設定する
setOffset取得開始位置を設定する
assemble追加された各条件をクエリーに使用できる形にする

列=値のような条件を設定する

ColumnValueConditionを使う

//検索条件の設定
$criteria = new CFW_Data_Criteria();
//table1.column1 ='aaa' 
$criteria->addWhere(new CFW_Data_Criteria_ColumnValueCondition( "table1.column1","aaa" ));
//table1.column2 in (1,10,100) 
$criteria->addWhere(new CFW_Data_Criteria_ColumnValueCondition( "table1.column2,array(1,10,100),CFW_Data_Criteria::OPERATOR_IN));

複合条件

ComplexConditionを使う

//検索条件の設定
// table1.column = 'xxx' or (table1.column1 = 'aaa' and table1.column2 in (1,10,100) ) を生成
$criteria = new CFW_Data_Criteria();
$complex = new CFW_Data_Criteria_ComplexCondition();
$complex->add(new CFW_Data_Criteria_ColumnValueCondition( "table1.column1","aaa" ))
$complex->add(new CFW_Data_Criteria_ColumnValueCondition( "table1.column2",array(1,10,100),CFW_Data_Criteria::OPERATOR_IN))
$criteria->addWhere(new CFW_Data_Criteria_ColumnValueCondition( "table1.column1","xxx" ));
$criteria->addWhereOr($complex);

テーブル単体の操作

DataSourceオブジェクトを作成してEntityオブジェクトに対する操作(save,find)を実行する。

複雑なテーブル操作

Commandクラス(cfw.data.DataCommand (as),CFW_Data_Command(PHP)など)を使用する。パラメータはParameterオブジェクトを生成して追加する。*クエリー内に直接パラメータを埋め込まない

結合テーブルからの選択(A)

クエリー記述時、選択列に 「テーブル別名 + "__"(アンダースコア2個) + 元の列名」からなる別名をつける。TableRowオブジェクトまたはエンティティへの展開はEntityクラスのmap()メソッドを使用する。

サンプル(PHP)
//クエリーの組み立て
$query = "select 
table1.column1 as table1__column1,
table1.column2 as table1__column2,
table1.column3 as table1__column3,
table1.column4 as table1__column4,
table2.column1 as table2__column1,
table2.column2 as table2__column2,
table2.column3 as table2__column3,
table2.column4 as table2__column4,
from table1 inner join table2
on table1.column1 = table2.column1
where table1.column1 = ?";
$command = new CFW_Data_Command($query);
$command->addParameter(new CFW_Data_Parameter(":column1",10));

//データ接続インスタンス生成
$connection = CFW_Data_ConnectionFactory::getConnection(array("name"));
$connection->connect();
//コマンドに接続を設定
$command->setConnection($connection);
//コマンド実行
$result = $command->executeQuery();
$connection->close();

//エンティティへの展開
$table1Entities = array();
$table2Entities = array();
foreach($result as $row){
	//table1の処理
	$table1Row = new Sample_Models_Table1Entity();
	$table1Row->map($row,"table1");
	$table1Entities[] = $table1Row;

	//table2の処理
	$table2Row = new Sample_Models_Table2Entity();
	$table2Row->map($row,"table2");
	$table2Entities[] = $table2Row;
}

データ接続設定

設定項目

以下の要素を1個または1個以上定義する。設定出来る項目は環境、データベース、使用言語等により異なる

名称(name)個々の接続先に名前をつける。階層構造は"/"あるいは"_"で区切ることで設定する。
クラス(class)実際に使用するConnecitonの実装クラスの名称。ex: SqlServer用、DbWebService用、SQLite用 etc...
接続先(url | location)データベース、Webサービスなどの接続先のurlまたはファイル名。
データベース名(database | dbname)接続するデータベースのインスタンス名、データベース名、etc...
ユーザー(user)データベース等を使用するためのユーザー名。
パスワード(password)データベース等を使用するためのユーザー名。
quoteStyle文字列引用符処理の方法。シングルクォーテーションを二重化する(0,デフォルト)、その他
コマンドタイムアウト(commandTimeout)1個のコマンドを実行する際のタイムアウト時間(秒)。
接続タイムアウト(connectionTimeout)接続を開く際のタイムアウト時間(秒)。
オプション(option)その他のオプション。実装クラスが定義する。

設定には必ずdefault接続先を設定しなければならない。

設定例

C#のapp.config|web.config
<!-- データアクセス設定 -->
<dataAccess defaultConnectionTimeout="15" defaultCommandTimeout="45">
<!-- 個々の接続 (default) "dbserver"上のSqlServerデータベースsampledbに接続する -->				
    <add name="default" className="CFW.Database.SqlServer.SqlServerConnection,CFW.Database" 
      url="dbserver" database="sampledb" 
      user="sampleuser" 
      password="samplepassword" 
      quoteStyle="0" 
      commandTimeout="15" 
      connectionTimeout="15" 
      option=""/>
<!-- 個々の接続 (サブシステム/機能単位) agサブシステムのデフォルト -->				
    <add name="ag/default" className="CFW.Database.SqlServer.SqlServerConnection,CFW.Database" 
      url="dbserver" database="sampledb" 
      user="sampleuser" 
      password="samplepassword" 
      quoteStyle="0" 
      commandTimeout="15" 
      connectionTimeout="15" 
      encryptUser="false" 
      option=""/>
<!-- 個々の接続 (サブシステム/機能単位) agサブシステムが使うmediaexデータベースへの接続 -->
    <add name="ag/mediaex" className="CFW.Database.SqlServer.SqlServerConnection,CFW.Database" 
      url="dbserver" database="mediaex" 
      user="mediaex" 
      password="mediaex" 
      quoteStyle="0" 
      commandTimeout="15" 
      connectionTimeout="15" 
      encryptUser="false" 
      option=""/>
</dataAccess>
				
PHP connection.ini

PHPの場合、名称は connection.name などのように設定要素に組み込まれている

//default接続先
connection.default.adapter = Sqlsrv //SQLサーバーに接続する
connection.default.params.host = dbserver
connection.default.params.username = sampleuser
connection.default.params.password = samplepassword
connection.default.params.dbname = sampledb
connection.default.params.driver_options.CharacterSet = utf-8 //sqlserverドライバに与えるオプション
//hotel/stay機能のための接続先
connection.hotel_stay.adapter = Sqlsrv //SQLサーバーに接続する
connection.hotel_stay.params.host = dbserver
connection.hotel_stay.params.username = sampleuser
connection.hotel_stay.params.password = samplepassword
connection.hotel_stay.params.dbname = hotel_stay
connection.hotel_stay.params.driver_options.CharacterSet = utf-8 //sqlserverドライバに与えるオプション
//hotel/credit機能のための接続先
connection.hotel_credit.adapter = Sqlsrv //SQLサーバーに接続する
connection.hotel_credit.params.host = dbserver
connection.hotel_credit.params.username = sampleuser
connection.hotel_credit.params.password = samplepassword
connection.hotel_credit.params.dbname = hotel_credit
connection.hotel_credit.params.driver_options.CharacterSet = utf-8 //sqlserverドライバに与えるオプション

				

データソース設定

設定項目

以下の要素を1個または1個以上定義する。設定出来る項目は環境、データベース、使用言語等により異なる

名称(name)個々のデータソースに名前をつける。階層構造は"/"あるいは"_"で区切ることで設定する。
クラス(class)実際に使用するDataSource実装クラスの名称。ex: SqlServer用、DbWebService用、SQLite用 etc...
接続先(connection)接続先設定に記述したname。ここでで指定する接続先以外に接続する場合は

設定には必ずdefaultを設定しなければならない。

phpのdatasource.ini

PHPの場合、名称は datasource.name などのように設定要素に組み込まれている

//default接続先
datasource.default.class = CFW_Data_DataSource
datasource.default.connection = default
				

例外処理

基本

例外処理が必要な場所に必要な処理を記述する。

詳細

  • 接続を開いたオブジェクトが自身の管理の下接続を閉じる。
  • 例外が発生した場合、確実に開いた接続を閉じるようにする。
  • 例外を捨ててはならない。そのオブジェクトを使用するクラスに例外を通知してはならない場合があるとしても、最低限例外をログに記録する事は必要
  • 例外が起こった事をメソッドの戻り値で返さない
  • 例外を戻り値の代わりにしない