Seasar DI Container with AOP

S2Dao.PHP5リファレンス

作成すべきファイル

S2Dao機能を使用するにあたり、Entity、Dao、diconファイル、SQLファイル(.sql)の作成が必要となります。
より詳細なドキュメントはJava版S2Daoをご覧になってください。

Entityはテーブル、DaoはEntity、diconファイルはDao、SQLファイルはDaoとそれぞれ関連しています。
各ファイルの実装・設定方法の詳細は次のようになります。

Entity(Bean)

Entityはテーブルとの関連付けに使用します。 Entityにテーブルの関連付けを行うには、以下の定数宣言とメソッドの実装が必要になります。

Entityの構成と説明内で使用するテーブルは以下の通りです。

テーブル:EMP

テーブル:DEPT

TABLEアノテーション

テーブルとの関連付けはTABLEアノテーションを使用します。 TABLEアノテーションは以下の形式で定数を宣言します。

const TABLE = "テーブル名";

EMPテーブルの場合以下のようになります。

const TABLE = "EMP";

コメントアノテーションを行う場合は以下のように記述します。

@Bean(table = テーブル名)

EMPテーブルの場合以下のようになります。

/**
 * @Bean(table = EMP)
 */
class Employee {
    :
    :
}

クラス名からパッケージ名を除いた名前がテーブル名と一致する場合は、TABLEアノテーションを定義する必要はありません。

COLUMNアノテーション

テーブルのカラムとの関連付けはCOLUMNアノテーションを使用します。
COLUMNアノテーションは以下の形式で定数を宣言します。

const プロパティ名_COLUMN = "カラム名";

employeeNoというプロパティにEMPNOカラムを関連付けする場合以下のようになります。

const employeeNo_COLUMN = "EMPNO";

コメントアノテーションを行う場合は以下のように記述します。

@Column("カラム名")

employeeNoというプロパティにEMPNOカラムを関連付けする場合以下のようになります。

/**
 * @Column("EMPNO")
 */
private $employeeNo;

プロパティ名とカラム名が一致する場合は、COLUMNアノテーションを定義する必要はありません。
テーブルに存在しないプロパティは、自動的に無視されるので、特に何か定義する必要はありません。

N:1マッピング

N:1マッピングとは、複数の従業員の行に1つの部署の行が関連付けられるような場合のマッピングです。
N:1マッピングを使用するには、RELNO定数とRELKEYS定数をそれぞれ宣言する必要があります。
RELNO定数は以下の形式になります。

const プロパティ名_RELNO = 数値;

RELNO定数は、N:1マッピングの連番です。
例えば、AAAのテーブルにBBB,CCCのテーブルがN:1マッピングされるとするとBBBのRELNOは0、CCCのRELNOは1になります。
RELNOは結果セットに含まれているカラムが、どのテーブルに所属しているのかを判定することに使われます。
例えば、SELECT ..., BBB.HOGE AS HOGE_0, ... FROM AAA, BBB ...のようなSELECT文があった場合、 HOGE_0はBBBテーブルに含まれているHOGEカラムであると認識されます。
RELKEYS定数は以下の形式になります。

const プロパティ名_RELKEYS = "N側のテーブルのカラム名: 1側のテーブルのカラム名";

N:1マッピングのキーはRELKEYS定数で指定します。
キーが複数ある場合には、カンマ( , )で区切ります。例えば、mykey1:yourkey1, mykey2:yourkey2のようにします。
EMPテーブルのDEPTNUMカラムとDEPTテーブルのDEPTNOを関連付ける場合は以下のようになります。

const department_RELNO = 0;
const department_RELKEYS = "DEPTNUM:DEPTNO";

上記の場合のコメントアノテーションを行う場合は以下のように記述します。

/**
 * @Relation(relationNo = 0, relationKey = "DEPTNUM:DEPTNO")
 */
private $department;

1側のテーブルのカラム名がN側のテーブルのカラム名に等しい場合は、1側のテーブルのカラム名を省略することができます。
その場合以下のように定義することが出来ます。

const department_RELKEYS = "DEPTNO";

また1側のテーブルのカラム名とN側のテーブルのカラム名に等しく、1側のテーブルのカラム名がプライマリーキーの場合、RELKEYS定数を省略することができます。

IDの自動生成

ID(プライマリーキー)をRDBMSに自動生成させて、自動生成された値をBeanに自動的に設定することが出来ます。
そのために使うのが、IDアノテーションです。
IDアノテーションは、プロパティ名_ID = "identity"のように指定します。

const id_ID = "identity";

SEQUENCEを使うことも出来ます。myseqの部分は、実際のSEQUENCEに置き換えてください。

const id_ID = "sequence, sequenceName=myseq";

手動でIDを設定する場合は、何も指定する必要がありません。
テーブルのプライマリーキーだという情報は、テーブルの定義(PDOのメタデータ)より自動的に取得されます。 また、明示的にassignedを指定することもできます。

const id_ID = "assigned";

コメントアノテーションを行う場合は以下のように記述します。

/**
 * @Id(identity)
 */
private $id;
永続化されないカラム

カラムが永続化の対象かどうかという情報は、テーブルの定義(PDOのメタデータ)より自動的に取得されます。 また、明示的にNO_PERSISTENT_PROPSを使って永続化したくないカラムを指定することもできます。 NO_PERSISTENT_PROPSに空文字を指定するとPDOのメタデータのメタデータを使わずにすべてのプロパティを永続化の対象とみなします。

const NO_PERSISTENT_PROPS = "dummy1, dummy2";

またコメントアノテーションで指定する場合は以下の書式で記述します

/**
 * @Bean(table = EMP)
 * @NoPersistentProperty(dummy1, dummy2)
 */
class Employee {
    private $dummy1;
    private $dummy2;
      :
      :
}
VERSION_NO_PROPERTYアノテーション

versionNoによる排他制御用のプロパティ名をデフォルトのversionNoから変えるときに使うのがVERSION_NO_PROPERTYアノテーションです。 次のように使います。

const VERSION_NO_PROPERTY = "myVersionNo";

またコメントアノテーションで指定する場合は以下の書式で記述します

/**
 * @Bean(table = EMP)
 * @VersionNoProperty(myVersionNo)
 */
class Employee {
    private $myVersionNo;
      :
      :
}
TIMESTAMP_PROPERTYアノテーション

timestampによる排他制御用のプロパティ名をデフォルトのtimestampから変えるときに使うのがTIMESTAMP_PROPERTYアノテーションです。次のように使います。

const TIMESTAMP_PROPERTY = "myTimestamp";

またコメントアノテーションで指定する場合は以下の書式で記述します

/**
 * @Bean(table = EMP)
 * @TimestampProperty(myTimestamp)
 */
class Employee {
    private $myTimestamp;
      :
      :
}
カラムに対応するプロパティの宣言

テーブルのカラムに対応した変数を宣言します。

EMPNOというカラムの場合、以下のように宣言することができます。

private $empno;
getter/setterメソッド

テーブルのカラムに対応した各プロパティのgetter/setterメソッドを実装します。メソッドの命名規則は以下の形式になります。

getterメソッド

public function getプロパティ名()

setterメソッド

public function setプロパティ名(引数)

private $empno;というプロパティの場合は以下のようになります。

private $empno;

public function getEmpno() {
    return $this->empno;
}

public function setEmpno($empno) {
    $this->empno = $empno;
}

以上の設定を行ったEMPテーブルに関連付くEntityは次のようになります。

class Employee {

    const TABLE = "EMP";

    private $empno;

    private $ename;

    private $deptnum;

    private $department;

    public function Employee() {
    }

    public function getDepartment() {
        return $this->department;
    }

    public function setDepartment(Department $department) {
        $this->department = $department;
    }

    public function getDeptnum() {
        return $this->deptnum;
    }

    public function setDeptnum($deptnum) {
        $this->deptnum = $deptnum;
    }

    public function getEmpno() {
        return $this->empno;
    }

    public function setEmpno($empno) {
        $this->empno = $empno;
    }

    public function getEname() {
        return $this->ename;
    }

    public function setEname($ename) {
        $this->ename = $ename;
    }

?>

Dao(Data Access Object)

Daoはインターフェースとして作成します。永続化されるデータとロジックを分離して、Dao本来の目的であるBeanの永続化を行います。 Entityとは1:1の関係にあるので、一つのEntityに対して一つのDaoを作成することになります。 Daoのメソッドを呼ぶことにより、メソッドに対応したSQLファイルに記述されているSQLが実行されます。 Daoを作成するには、以下の点が必要になります。

またメソッドの引数をSQL文で参照したり、WHERE句、ORDER句を追加、更新のSQLに含めない、あるいは含めるプロパティを指定する場合には、以下のアノテーションを使います。

BEANアノテーション

DaoがどのEntity(エンティティ)に関連付けられているのかはBEANアノテーションで指定します。
BEANアノテーションは以下の形式で定数を宣言します。

const BEAN = "Bean(Entity)名";

EmployeeDaoクラスがEmployeeエンティティに関連付けられる場合は次のように定義します。

const BEAN = "Employee";

コメントアノテーションを行う場合は以下のように記述します。

@Dao(bean = Bean(Entity)名)

EmployeeDaoクラスがEmployeeエンティティに関連付けられる場合は次のように定義します。

/**
 * @Dao(bean = Employee)
 */
interface EmployeeDao {
    :
    :
}
ARGSアノテーション

メソッドの引数をSQL文で参照できるように、ARGSアノテーションを使用し、メソッドの引数名を指定することもできます。
ARGSアノテーションは以下の形式で定数を宣言します。

const メソッド名_ARGS = "引数名";

public function getEmployeeList($empno)というメソッドがDaoに定義されていた場合の引数名は次のように定義します。

const getEmployeeList_ARGS = "empno";

コメントアノテーションを行うには以下のように記述します。

/**
 * @Arguments(empno)
 */
public function getEmployee($empno);

また、ARGSアノテーションは省略することができます。
省略した場合は、メソッドの引数名がそのまま使われます。

例えば以下のようなメソッドの場合

public function methodA($id, $name, $age);

もし、ARGSアノテーションを省略した場合、 id, name, age が利用されます。
よって上のメソッドの場合以下のARGSアノテーションが省略可能です。

//const methodA_ARGS = "id, name, age";
QUERYアノテーション

自動的に生成されるSELECT文にWHERE句やORDER BY句を追加するには、QUERYアノテーションを使用します。
QUERYアノテーションは以下の形式で定数を宣言します。

const メソッド名_QUERY = "WHERE句ORDER BY句";

引数で給与の上限と下限を指定し、その間に含まれる従業員を抽出する場合、次のようにします。

const getEmployeesBySalList_QUERY = "sal BETWEEN ? AND ? ORDER BY empno";
public function getEmployeesBySalList($minSal, $maxSal);

コメントアノテーションを行う場合には以下のように記述します。

/**
 * @Query("sal BETWEEN ? AND ? ORDER BY empno")
 */
public function getEmployeesBySal($minSal, $maxSal);

上記例の“?”をバインド変数と言います。 バインド変数をQUERYアノテーションに記述することにより、メソッドの引数の値が順に”?”の部分に代入されます。 ARGSアノテーションは、必要ありません。ORDER BY句だけを記述するときは、ORDER BYで始めてください。SQLコメントも記述することが出来ます。 SQLコメントを使用したサンプルは以下のとおりです。

const getEmployeesList_QUERY =
        "job = /*job*/'CLERK'/*IF deptno != null*/ AND deptno = /*deptno*/20/*END*/";

上記サンプルは、引数deptnoがnullでない場合、deptnoが引数の値と一致するという条件を追加します。SQLコメントについての詳しい説明は、SQLコメントの項目を参照して下さい。

メソッドの定義

Daoに定義したメソッドを呼ぶことにより、対応するSQLファイルに記述されているSQLが実行されますが、 更新(INSERT, UPDATE, DELETE)、検索処理ごとにメソッドの命名規則があります。S2Daoではメソッドの命名規則よりSQL文の中身を自動的に決定しています。 また、定義するメソッドのオーバーロードはサポートしていません。

  • INSERT処理
  • INSERT処理を行なうメソッドの名前が、insert, add, createではじまる必要があります。
    更新した行数が戻り値となります。引数の型はエンティティの型と一致させます。
    メソッドの定義例は以下のようになります。

    public function insert(Department $department);
    public function addDept(Department $department);
    public function createDept(Department $department);
  • UPDATE処理
  • UPDATE処理を行うメソッドの名前が、update,modify,storeではじまる必要があります。
    更新した行数が戻り値となります。引数の型はエンティティの型と一致させます。
    メソッドの定義例は以下のようになります。

    public function update(Department $department);
    public function modifyDept(Department $department);
    public function storeDept(Department $department);
  • DELETE処理
  • DELETE処理を行うメソッドの名前が、delete,removeではじまる必要があります。
    更新した行数が戻り値となります。引数の型はエンティティの型と一致させます。
    メソッドの定義例は以下のようになります。

    public function delete(Department $department);
    public function removeDept(Department $department);
  • 検索(SELECT)処理

    検索処理を行ないたい場合は、メソッド名の最後がListまたは、Arrayで終わっている必要があります。
    もしListまたは、Arrayで終わっていない場合は単一のEntity(Bean)を返します。
    また、List、Arrayなどは大文字小文字を判別しません。

    • List形式の戻り値

      Listで終わるメソッド名の場合は、SELECT文でエンティティのリスト(S2Dao_ArrayList形式)を返します。
      (S2Dao_ArrayListはS2Dao.PHP5の組み込みクラスです。JavaのArrayListと同じような機能を提供します。)
      以下のように記述することができます。

      public function getEmpList();

      コメントアノテーションを行う場合は以下のように記述します。

      /**
       * @return list
       */
      public function getEmp();
    • Array形式の戻り値

      Arrayで終わるメソッド名の場合は、エンティティの連想配列(array())を返します。
      以下のように記述することができます。

      public function getEmpArray();

      コメントアノテーションを行う場合は以下のように記述します。

      /**
       * @return array
       */
      public function getEmp();
    • YAML形式の戻り値

      Yamlで終わるメソッド名の場合は、YAML形式の戻り値を返します。
      以下のように記述することができます。

      public function getEmpYaml();

      コメントアノテーションを行う場合は以下のように記述します。

      /**
       * @return yaml
       */
      public function getEmp();

      YAML形式の戻り値を取得するためにはSpyc(http://spyc.sourceforge.net/)を読み込んでおく必要があります。

      require_once "spyc.php";
      :
      :
      
    • JSON形式の戻り値

      Jsonで終わるメソッド名の場合は、JSON形式の戻り値を返します。
      以下のように記述することができます。

      public function getEmpJson();

      コメントアノテーションを行う場合は以下のように記述します。

      /**
       * @return json
       */
      public function getEmp();

      JSON形式の戻り値を取得するためにはphp-jsonのPHP拡張(http://www.aurore.net/projects/php-json/)を使用します。
      使用するにはphp.iniなどにjson.so(php_json.dll)を読み込んでおいてください。

      extension=json.so(extension=php_json.dll)
      
NO_PERSISTENT_PROPSアノテーション

更新するときに、このプロパティは、SQLに含めて欲しくないという場合もあります。 そのような場合は、NO_PERSISTENT_PROPSアノテーションを使います。

const insert_NO_PERSISTENT_PROPS = "sal, comm";

上記のように指定すると、insertメソッドで、salとcommプロパティは永続化の対象になりません。

コメントアノテーションを行う場合は以下のように記述します。

/**
 * @NoPersistentProperty(sal, comm)
 */
public function insert(Department $department);
PERSISTENT_PROPSアノテーション

更新するときに、このプロパティだけをSQLに含めたいという場合もあります。 そのような場合は、PERSISTENT_PROPSアノテーションを使います。

const update_PERSISTENT_PROPS = "deptno";

上記のように指定すると、updateメソッドで、プライマリーキー、versionNo、timestampのプロパティに加えて、 PERSISTENT_PROPSアノテーションで指定したプロパティが永続化の対象になります。

コメントアノテーションを行う場合は以下のように記述します。

/**
 * @PersistentProperty("deptno")
 */
public function update(Department $department);
SQLアノテーション

SQLアノテーションを使用することが可能です。
機能はSQLファイルと同様で、アノテーションにてSQL及びSQLコメントを使用することが可能です。

SQLアノテーションに命名規則があります。

  • SQLファイルとDaoに定義したメソッドの関連付け
  • SQLアノテーションとDaoに定義したメソッドの関連付けをするには、SQLアノテーションを以下の形式にする必要があります。

    const メソッド名_SQL

    EmployeeDao::getAllEmployeesList()に対応するSQLアノテーションは以下のようになります。

    const getAllEmployeesList_SQL = "SELECT
             emp.*, dept.dname dname_0, dept.loc loc_0 FROM emp, dept
                 WHERE emp.deptno = dept.deptno ORDER BY emp.empno;";

    コメントアノテーションを行う場合は以下のように記述します。

    /**
     * @Sql("SELECT * FROM EMP WHERE EMPNO > 1")
     */
    public function getAllEmployees();
    
  • 複数DBMS対応
  • DBMSごとに使用するSQLアノテーションを指定することができます。
    どのDBMSを使っているのかは PDO::ATTR_DRIVER_NAME の値に応じて、S2Dao が自動的に判断しています。
    S2Dao.PHP5でDBMSごとにサフィックスを決めているので、SQLアノテーションにサフィックスを追加します。
    例えばMySQLの場合、サフィックスはmysqlなので、「getAllEmployeesList_mysql_SQL」というSQLアノテーションになります。

    DBMSとサフィックスの関係は以下の通りです。

    DBMS サフィックス
    Oracle oracle
    Firebird firebird
    MySQL mysql
    PostgreSQL pgsql
    SQLite sqlite
    Sybase sybase
PROCEDUREアノテーション

バージョンRC3より、PROCEDUREアノテーションを使用することによりStoredProcedureやStoredFunctionを実行することができます。 PROCEDUREアノテーションは以下の形式のうちいずれかを指定します。

const メソッド名_PROCEDURE = "カタログ名.スキーマ名.プロシジャ名";

const メソッド名_PROCEDURE = "スキーマ名.プロシジャ名";

const メソッド名_PROCEDURE = "プロシジャ名";

  • サポートされるStoredProcedureの範囲について
  • PROCEDUREアノテーションでは、戻り値のあるStoredProcedureおよびINパラメータを複数持つプロシジャーをサポートしています。 ただし、OUTやINOUTパラメータは現在のところ対応していません。

    また、DBMSやPDOのバージョンによっては、利用できない場合があります。

    DBMS 制限事項
    Oracle 現在利用不可です。
    MySQL INパラメータをもつStoredFunctionおよびStoredProcedureをサポートしています。
    PostgreSQL INパラメータをもつStoredFunctionおよびStoredProcedureをサポートしています。
    SQLite PDO::sqliteCreateFunctionに対応し、INパラメータにおいてサポートしています。
    Firebird 現在利用不可です。
    Sybase 現在利用不可です。

diconファイル

diconファイルはDaoをコンテナにコンポーネント登録します。 Dao機能を使用するには、登録したDaoに対して、AOPを適用する必要があります。 diconファイルはどこに配置してもいいのですが、通常Daoと同じ場所に配置します。 diconファイルの詳しい設定方法については、DIContainerを参照して下さい。

S2DaoInterceptorの適用

Dao機能を使用するには "org.seasar.dao.interceptors.S2DaoInterceptor"を登録したDaoに対してAOPを適用します。
AOPについては、AOPのページを参照して下さい。
以下はDao(EmployeeDao)をコンポーネント登録するサンプル

EmployeeDao.dicon

<components>
    <include path="dao.dicon"/>
    <component class="EmployeeDao">
        <aspect>dao.interceptor</aspect>
    </component>
</components>

dao.dicon

<components namespace="dao">
    <include path="%PDO_DICON%" />
    <component class="S2Dao_FieldAnnotationReaderFactory" />
    <component class="S2Dao_DaoMetaDataFactoryImpl" />
    <component name="interceptor" class="S2DaoInterceptor" />
</components>

pdo.dicon

<components namespace="pdo">
    <component name="dataSource" class="S2Container_PDODataSource">
        <property name="dsn">"mysql:host=localhost; dbname=s2con"</property>
        <property name="user">"root"</property>
        <property name="password">"pass"</property>
    </component>
</components>

<component class="S2Dao_DaoMetaDataFactoryImpl"/>はS2DaoInterceptorのコンストラクタの引数となるので、 S2Dao_DaoMetaDataFactoryImplも記述しておきます。

SQLファイル

検索、更新処理等を行うSQL文を記述します。 Daoに定義したメソッドを呼び出すと、対応するSQLファイルに記述されているSQL文が発行されます。 作成したSQLファイルはDaoと同じ場所に配置してください。
S2DaoにはSQLを自動で生成する機能が用意されているので、SQLファイルがない場合、S2Dao.PHP5がSQL文を自動生成します。

SQLファイル名

S2DaoにはSQLのファイル名にも命名規則があります。

  • SQLファイルとDaoに定義したメソッドの関連付け
  • 作成したSQLファイルとDaoに定義したメソッドの関連付けをするには、SQLファイルのファイル名を以下の形式にする必要があります。

    Daoのクラス名_メソッド名.sql

    examples.dao.EmployeeDao::getAllEmployeesList()に対応するSQLファイルは以下のようになります。

    examples/dao/EmployeeDao_getAllEmployeesList.sql
  • 複数DBMS対応
  • DBMSごとに使用するSQLファイルを指定することができます。
    どのDBMSを使っているのかは PDO::ATTR_DRIVER_NAME の値に応じて、S2Dao が自動的に判断しています。
    S2Dao.PHP5でDBMSごとにサフィックスを決めているので、SQLファイル名にサフィックスを追加します。
    例えば MySQL の場合、サフィックスは mysql なので、「EmployeeDao_getAllEmployeesList_mysql.sql」というファイル名になります。

    DBMSとサフィックスの関係は以下の通りです。

    DBMS サフィックス
    Oracle oracle
    Firebird firebird
    MySQL mysql
    PostgreSQL pgsql
    SQLite sqlite
    Sybase sybase
SQL文の記述

SQLファイルには、”SELECT * FROM EMP”, “DELETE FROM EMP WHERE EMPNO = 7788”といった、普通のSQL文を記述することが可能です。
また、WHERE句の条件の値などを動的に変化させることも可能です。 詳しくは、SQLコメントを参照して下さい。

SQLコメント

S2Dao.PHP5では、メソッドの引数とSQL文のバインド変数の対応付けを/**/や--などのコメントを使って行います。 コメントなので、対応付けをした後でも、SQL*PlusなどのSQLのツールでそのまま実行することができます。 最初、SQLのツールでSQL文を実行して思い通りの結果を出力するようになったら、 それに対して、コメントを埋め込んでいくと良いでしょう。

また、SQL文に対しての説明の意味でのコメントを使用したい場合は、/*の後にスペースを入れることにより、 普通のコメントを使用することが出来ます。例として、/* hoge*/となります。/*の後にスペースが入っているので、実行時には無視されます。

バインド変数コメント

Daoに定義したメソッドの引数の値をSQL文で使用する場合は、SQL文にバインド変数コメントを記述します。 バインド変数コメントの右側のリテラルに引数の値が自動的に置換され実行されます。 バインド変数コメントは、以下のように記述します。

/*引数名*/リテラル

引数がEntityの場合は以下のように記述します。

/*引数名.プロパティ名*/リテラル

public function getEmployeeList($empno);

Daoに上記のメソッドを定義した場合、SQLファイル(EmploeeDao_getEmployeeList.sql)は次のようにバインド変数を使用することが可能です。 自動的にgetEmployeeListメソッドの引数の値が設定されます。

SELECT * FROM emp WHERE empno = /*empno*/7788

IN句にバインド変数を適用したい場合は以下のようにすることができます。

IN /*引数名*/(...)

IN /*names*/('aaa', 'bbb')

引数はS2DaoArrayListや配列の引数となります。上記のIN句の場合は、以下のように引数を用意します。

$names = array("SCOTT", "SMITH", "JAMES");

配列namesが自動的にバインド変数の部分に置換されます。

LIKEを使用する場合は、次のようにします。

ename LIKE /*ename*/'hoge'

ワイルドカードを使いたい場合は、メソッドの引数の値に埋め込みます。 「"COT"を含む」という条件を指定する場合は、以下のように引数の値にワイルドカードを埋め込みます。

$employeeDao->findEmployees("%COT%");

埋め込み変数コメント

Daoに定義したメソッドの引数の値をSQL文に文字列として直接埋め込む場合は、SQL文に埋め込み変数コメントを記述します。 埋め込み変数コメントの右側のリテラルに引数の値が自動的に置換され実行されます。 埋め込み変数コメントは、以下のように記述します。

/*引数名*/リテラル

引数がEntityの場合は以下のように記述します。

/*引数名.プロパティ名*/リテラル

IFコメント

IFコメントでは、条件に応じて実行するSQL文を変えることが可能です。IFコメントは以下の形式で記述します。

/*IF 条件*/ .../*END*/

サンプルは以下のとおりです。

/*IF hoge != null*/hoge = /*hoge*/'abc'/*END*/

IFコメントは、条件が真の場合、/*IF*/と/*END*/に囲まれた部分が評価されます。 上記の場合、引数 hoge がnullでない場合にのみ、IFコメントで囲まれている部分(hoge = /*hoge*/'abc')が評価されます。
また偽の場合の処理としてELSEコメントというものが用意されています。 条件が偽となった場合は、”ELSE”の後に記述した部分が評価されます。ELSEコメントは以下のように記述します。

/*IF hoge != null*/hoge = /*hoge*/'abc'
  --ELSE hoge is null
/*END*/

条件がfalseになると--ELSEの後の部分(hoge is null)が評価されます。

/*IF hoge != null*/の判別を厳密に行いたい(型も含める)場合は /*IF hoge !== null*/を使用してください。
例えば hoge = "true" の場合以下のIFコメントでは動作が違います。

/*IF hoge == true*/hoge = 123/*END*/
/*IF hoge == "true"*/hoge = 456/*END*/
/*IF hoge === true*/hoge = 789/*END*/
/*IF hoge === "true"*/hoge = 823/*END*/

BEGINコメント

BEGINコメントは、WHERE句内のすべてのELSEを含まないIFコメントがfalseになった場合に、 WHERE句自体を出力したくない場合に使います。BEGINコメントはIFコメントと併せて使用します。
BEGINコメントは以下の形式で記述します。

/*BEGIN*/WHERE句/*END*/

サンプルは以下の通りです。

/*BEGIN*/WHERE
  /*IF job != null*/job = /*job*/'CLERK'/*END*/
  /*IF deptno != null*/AND deptno = /*deptno*/20/*END*/
/*END*/

上記の場合、job,deptnoがnullの場合は、WHERE句は出力されません。 job == null,deptno != nullの場合は、WHERE depno = ?、 job != null,deptno == nullの場合は、WHERE job = ?、 job != null,deptno != nullの場合は、WHERE job = ? AND depno = ?のようになります。動的SQLも思いのままです。

EntityManagerを使用したQueryの実行

EntityManagerを使用し、自動的に生成されるSELECT文にWHERE句やORDER BY句を追加できます。書き方は、QUERYアノテーションと同様です。 主に、動的にQueryを組み立てたいときに使用します。EntityManagerを使用するには、以下のクラスを継承します。

S2Dao_AbstractDao

Daoのインターフェース名は、必ず"Dao"で終わるようにしてください。S2Daoは、S2Dao_AbstractDaoを継承したクラスが実装しているインターフェースの中で、 クラス名が"Dao"で終わっているインターフェースをDaoインターフェースだと判断しているためです。

EntityManagerには、以下のメソッドが用意されています。

find()メソッド
戻り値をS2Dao_ArrayList(ArrayObject)で返します。引数の種類は以下の通りです。
public function find($query, $arg1 = null, $arg2 = null, $arg3 = null);
findArray()メソッド
戻り値を配列(array())で返します。引数の種類は以下の通りです。
public function findArray($query, $arg1 = null, $arg2 = null, $arg3 = null);
findBean()メソッド
戻り値をEntity(Bean)で返します。引数の種類は以下の通りです。
public function findBean($query, $arg1 = null, $arg2 = null, $arg3 = null);
findObject()メソッド
結果セットを Entity オブジェクトで返します。引数の種類は以下の通りです。
public function findObject($query, $arg1 = null, $arg2 = null, $arg3 = null);
findYaml()メソッド
結果セットを YAML 形式で返します。引数の種類は以下の通りです。
public function findYaml($query, $arg1 = null, $arg2 = null, $arg3 = null);
findJson()メソッド
結果セットを JSON 形式で返します。引数の種類は以下の通りです。
public function findJson($query, $arg1 = null, $arg2 = null, $arg3 = null);

引数は、QUERYアノテーションと同様に記述します。引数が4つ以上になるの場合は、配列(array)を使用します。

S2Dao_AbstractDaoを継承したクラスの基本的な実装方法

  1. S2Dao_AbstractDaoの継承
  2. Daoをimplementsする
    implementsするDaoのインターフェース名の最後は"Dao"で終了している必要があります。
  3. コンストラクタの実装
    S2Dao_DaoMetaDataFactoryを引数とし、parent::__construct(S2Dao_DaoMetaDataFactory)を呼び出します。
  4. Daoに定義したメソッドの実装
    EntityManagerで提供しているメソッドを使用する場合は、$this->getEntityManager()->find(...);のように、getEntityManager()メソッドを使用し、 EntityManagerを取得し呼び出すことが出来ます。

S2Dao_AbstractDaoを継承したクラスのサンプルは以下の通りです。

class Employee2DaoImpl extends S2Dao_AbstractDao implements Employee2Dao {

    public function __construct(S2Dao_DaoMetaDataFactory $daoMetaDataFactory) {
        parent::__construct($daoMetaDataFactory);
    }

    public function getEmployeesList($ename) {
        return $this->getEntityManager()->find("ename LIKE ?", "%" + $ename + "%");
    }
}

VersionNoによる排他制御

S2Dao.PHP5は排他制御も自動的に行うことができます。
設定方法は、テーブルに排他制御用のカラムを用意し、Entity(Bean)に$versionNoと定義するだけで、versionNoによる排他制御を自動的に行ってくれます。

例えば、2人のユーザがversionNo値0の同一データを取得して更新しようとした場合、 先に更新したユーザは正常に登録することができます。そのとき自動でversionNoはインクリメントされ、DBのversionNoは1となります。 次にもう1人のユーザがデータを更新しようとすると、ユーザが保持しているversionNoの値(0)と、 実際にDB格納されているversionNoの値(1)が異なることになり、UpdateFailureRuntimeExceptionが発生し更新失敗することになります。

Timestampによる排他制御

VersionNoの他にTimestampによる排他制御もS2Dao.PHP5が自動的に行うことができます。Entity(Bean)に$timestampという名前のプロパティを定義するだけで、自動的に行ってくれます。 Timestamp用のカラムの値にnullが設定されていると比較に失敗するので注意してください。

更新SQLの自動生成

更新SQL文を自動生成させるには、メソッド名を命名規則にあわせ、Entityを1つ引数に持つメソッドを定義するだけです。 SQLファイルの作成は不要です。例としてInsertの場合、命名規則に合わせ、以下のように定義します。

public function insert(Department $department);

検索SQLの自動生成

メソッドのsignatueより、S2Dao.PHP5に自動的にSELECT文を生成させることもできます。ARGSアノテーションにカラム名を指定することで、 引数の値によってWHERE句が変わるような動的なSQL文も自動生成できます。

SELECT * FROM emp
/*BEGIN*/WHERE
  /*IF job != null*/job = /*job*/'CLERK'/*END*/
  /*IF deptno != null*/AND deptno = /*deptno*/20/*END*/
/*END*/

上記SQL文に相当するSQL文を自動生成するには以下のように定義します。上記SQLの/**/などについては、SQLコメントを参照してください。

const getEmployeeByJobDeptnoList($job, $deptno);

N:1でマッピングされているカラムを指定する場合には、「カラム名_関連番号」で指定します。 N:1でマッピングされているBeanは左外部結合を使って1つのSQL文で取得されます。左外部結合をサポートしていないRDBMSはSELECT文自動生成の対象外です。 オラクルのように左外部結合が標準と異なる場合も、S2Dao.PHP5がRDBMSがオラクルであると自動的に判断して適切なSQL文を組み立てます。

引数にDTO(Data Transter Object)を指定することもできます。その場合、ARGSアノテーションを指定してはいけません。 S2Dao.PHP5は、引数が1つで、ARGSアノテーションが指定されていない場合、引数をDTOとみなし、DTOのプロパティを使って自動的にSQL文を組み立てます。 プロパティ名とカラム名が異なる場合は、COLUMNアノテーションを使ってカラム名を指定します。N:1でマッピングされているカラムを指定する場合には、カラム名_関連番号で指定します。 テーブルに存在しないプロパティ(カラム)は自動的に無視されます。プロパティの値によって、WHERE句が変わるような動的SQL文を自動生成します。
動的SQL文の自動生成とORDER BYではじまるQUERYアノテーションは併用することが出来ます。

class EmployeeSearchCondition {

    const dname_COLUMN = "dname_0";
    private $job;
    private $dname;
    ...
}
getEmployeesBySearchConditionList(EmployeeSearchCondition $dto);

また同様の指定方法で引数にEntityを使用することも出来ます。
DTOの詳しい使用方法は、自動で検索用SQL文を生成する場合のExampleを参照して下さい。

定数アノテーションとコメントアノテーション

S2Dao.PHP5では定数アノテーションの他にコメントアノテーションにも対応しています。
コメントアノテーションを利用することで、メソッド名に余計な文字列(List、Array)を付けなくて済みます。

コメントアノテーションを利用するにおいては、S2Container.PHP5-1.1.0以降を必要とします。
また、現在コメントアノテーションと定数アノテーションを同時に使用することはできません。

コメントアノテーションを使用する方法は以下のようになります。

  1. DaoおよびEntity(Bean)にコメントアノテーションを設定
  2. 作成したファイル実行する

定数 'S2DAO_PHP5_USE_COMMENT' の設定

コメントアノテーションを使用するにあたってS2DAO_PHP5_USE_COMMENTの定数に true を代入しておく必要があります。

define('S2DAO_PHP5_USE_COMMENT', true);

define('S2DAO_PHP5_USE_COMMENT', true);は必ずbooleanを指定してください

DaoおよびEntity(Bean)にコメントアノテーションを設定

各コメントアノテーションをDao及びEntity(Bean)に設定します。

Daoに設定する場合のサンプル
<?php

/**
 * @Dao(bean = CdBean)
 */
interface CdDao {

    /**
     * @NoPersistentProperty("id, content")
     */
    public function update(CdBean $cd);

    /**
     * @NoPersistentProperty("content")
     */
    public function insert(CdBean $cd);

    /**
     * @return array
     */
    public function getAll();

    /**
     * @Sql("SELECT CD.ID, CD.TITLE FROM CD WHERE ID > 1")
     * @return array
     */
    public function getCds();

    /**
     * @return list
     */
    public function getCD1($id, $title = null);

    /**
     * @Sql("SELECT COUNT(*) FROM CD")
     * @return object
    */
    public function getCdCount();
}
?>
Entity(Bean)に設定する場合のサンプル
<?php

/**
 * @Bean(table = CD)
 */
class CdBean {

   /**
    * @Column(ID)
    */
    private $id;

   /**
    * @Ccolumn(TITLE)
    */
    private $title;

   /**
    * @Column(CONTENT)
    */
    private $content;

    public function getContent() {
        return $this->content;
    }

    public function setContent($content) {
        $this->content = $content;
    }

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getTitle() {
        return $this->title;
    }

    public function setTitle($title) {
        $this->title = $title;
    }
}
?>

作成したファイル実行する

作成したファイルを読み込み、実行させます。

<?php
require_once dirname(__FILE__) . "/example.inc.php";

$container = S2ContainerFactory::create("./resource/example.dicon.xml");
$dao = $container->getComponent("CdDao");

$cd = $dao->getAll();

for($i = 0; $i < count($cd); $i++){
    echo "ID: " . $cd[$i]->getId(), PHP_EOL;
    echo "TITLE: " . $cd[$i]->getTitle(), PHP_EOL;
    echo "CONTENT: " . $cd[$i]->getContent(), PHP_EOL;
    echo "-------", PHP_EOL;
}

echo "=====", PHP_EOL;

$list = $dao->getCD1(2, 'aaa');
for($i = 0; $i < $list->size(); $i++){
    $cd = $list->get($i);
    echo "ID: " . $cd->getId(), PHP_EOL;
    echo "TITLE: " . $cd->getTitle(), PHP_EOL;
    echo "CONTENT: " . $cd->getContent(), PHP_EOL;
    echo "-------", PHP_EOL;
}
?>

S2Daoの実行

Daoを実行する基本的な方法は以下のようになります。

  1. 作成したdiconファイルのパスを引数にS2Containerを生成
  2. 生成したS2ContainerからgetComponentを呼び出し、登録したDaoを取得する
  3. 取得したDaoのメソッドを実行する

実行サンプル

<?php
define("PATH", "examples/dao/EmployeeDao.dicon");
define("PDO_DICON", "examples/pdo.dicon");

$container = S2ContainerFactory::create(PATH); /* 手順1 */
$dao = $container->getComponent("EmployeeDao"); /* 手順2 */
var_dump($dao->List_getAllEmployee(7788)); /* 手順3 */
?>