Seasar DI Container with AOP

PDOトランザクションの自動制御

S2Txの機能を使って、普通のPHPのクラスに対して、Aspectでトランザクションの自動管理機能を組み込むことができます。 PDOが自動で管理するトランザクション管理機能をPHPのクラスに対して透明に組み込むことができるのです。組む込むことのできるトランザクション属性は次のとおりです。

トランザクション属性

S2Dao.PHP5が標準で用意しているpdo.diconには、次のトランザクション属性に対応したAdviceが定義されています。 コンポーネント名がAdviceの名前です。

属性 コンポーネント名 説明
Required pdo.requiredTx トランザクションが開始されていなければ、
自動的にトランザクションを開始します。
既にトランザクションが開始されていれば、
そのトランザクションを引き継ぎます。
RequiresNew pdo.requiresNewTx 常に新しいトランザクションを開始させます。
既存のトランザクションが開始されているなら、
既存のトランザクションを中断し、
自分自身のトランザクションの終了後、
中断したトランザクションを復帰させます。
Mandatory pdo.mandatoryTx トランザクションが既に開始されてなければエラーにします。
NotSupported pdo.notSupportedTx 既存のトランザクションが開始されているなら、
既存のトランザクションを中断します。
コンポーネントのメソッドの終了後、
中断したトランザクションを復帰させます。

Example

CdDao.class.php

<?php

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

    public function update(CdBean $cd);
    public function insert(CdBean $cd);
    public function delete(CdBean $cd);

    /**
     * @return list
     */
    public function getAll();
}
?>

CdBean.class.php

<?php

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

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

    /**
     * @Column("TITLE")
     */
    private $title;

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

    public function __construct($id = null, $title = null, $content = null){
        $this->id = $id;
        $this->title = $title;
        $this->content = $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;
    }
}
?>

CdTxManager.class.php

<?php
interface CdTxManager {
    public function requiredInsert();
    public function requiresNewInsert();
    public function mandatoryInsert();
    public function getAll();
    public function delete();
}
?>

CdTxManagerImpl.class.php

<?php

class CdTxManagerImpl implements CdTxManager {

    private $dao = null;

    public function __construct(CdDao $dao){
        $this->dao = $dao;
    }

    public function requiredInsert(){
        $this->dao->insert(new CdBean(3, "amanda", "newage"));
        $this->dao->insert(new CdBean(4, "rat race", "metal"));
    }

    public function requiresNewInsert(){
        $this->dao->insert(new CdBean(4, "Crazy Little Thing Called Love", "rock"));
        $this->dao->insert(new CdBean(3, "Staring At The Sun", "rock"));
    }

    public function mandatoryInsert(){
        $this->dao->insert(new CdBean(5, "Prophecy", "metal"));
    }

    public function getAll(){
        return $this->dao->getAll();
    }

    public function delete(){
        $this->dao->delete(new CdBean(3));
        $this->dao->delete(new CdBean(4));
        $this->dao->delete(new CdBean(5));
    }
}

?>

example.dicon.xml

<components namespace="example">

    <include path="%DAO_DICON%" />

    <component class="CdDao">
        <aspect>dao.interceptor</aspect>
    </component>

    <component class="CdTxManagerImpl">
        <aspect pointcut="requiredInsert">pdo.requiredTx</aspect>
        <aspect pointcut="requiresNewInsert">pdo.requiresNewTx</aspect>
        <aspect pointcut="mandatoryInsert">pdo.mandatoryTx</aspect>
        <aspect pointcut="getAll">pdo.requiredTx</aspect>
        <aspect pointcut="delete">pdo.requiresNewTx</aspect>
    </component>

</components>

CdTxManagerClient.php

<?php
require dirname(__FILE__) . "/CdTxManager.class.php";

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

echo "requiredInsert start" . PHP_EOL;
try{
    $manager->requiredInsert();
}catch(Exception $e){
    var_dump($e->getMessage());
}
echo "requiredInsert end" . PHP_EOL;

echo "requiresNewInsert start" . PHP_EOL;
try{
    $manager->requiresNewInsert();
}catch(Exception $e){
    var_dump($e->getMessage());
}
echo "requiresNewInsert end" . PHP_EOL;

echo "mandatoryInsert start" . PHP_EOL;
try{
    $manager->mandatoryInsert();
}catch(Exception $e){
    var_dump($e->getMessage());
}
echo "mandatoryInsert end" . PHP_EOL;

echo "getAll start" . PHP_EOL;
try{
    var_dump($manager->getAll());
}catch(Exception $e){
    var_dump($e->getMessage());
}
echo "getAll end" . PHP_EOL;

try{
    $manager->delete();
}catch(Exception $e){
    var_dump($e->getMessage());
}

?>

実行結果

requiredInsert start
string(38) "There is already an active transaction"
requiredInsert end
requiresNewInsert start
[DEBUG]  - INSERT INTO CD (CONTENT, ID, TITLE) VALUES ('rock', 4, 'Crazy Little Thing Called Love')
[DEBUG]  - INSERT INTO CD (CONTENT, ID, TITLE) VALUES ('rock', 3, 'Staring At The Sun')
requiresNewInsert end
mandatoryInsert start
string(14) "No transaction"
mandatoryInsert end
getAll start
[DEBUG]  - SELECT CD.CONTENT, CD.ID, CD.TITLE FROM CD
object(S2Dao_ArrayList)#515 (3) {
  [0]=>
  object(CdBean)#518 (3) {
    ["id:private"]=>
    string(1) "1"
    ["title:private"]=>
    string(8) "S2Dao!!!"
    ["content:private"]=>
    string(7) "hello!!"
  }
  [1]=>
  object(CdBean)#519 (3) {
    ["id:private"]=>
    string(1) "4"
    ["title:private"]=>
    string(30) "Crazy Little Thing Called Love"
    ["content:private"]=>
    string(4) "rock"
  }
  [2]=>
  object(CdBean)#520 (3) {
    ["id:private"]=>
    string(1) "3"
    ["title:private"]=>
    string(18) "Staring At The Sun"
    ["content:private"]=>
    string(4) "rock"
  }
}
getAll end
[DEBUG]  - DELETE FROM CD WHERE ID = 3
[DEBUG]  - DELETE FROM CD WHERE ID = 4
[DEBUG]  - DELETE FROM CD WHERE ID = 5
string(55) "Target for update must be single row(actual:0).(CdBean)"

pdo.diconはS2Daoとしてあらかじめ用意(srcの直下)されています。 Adviceのコンポーネント名をaspectタグのボディに指定するだけなので簡単です。 簡単にトランザクション管理機能が組み込めることがわかってもらえたと思います。