intra-mart Accel Platform SAStruts+S2JDBC プログラミングガイド 第17版 2020-08-01

データベース

プログラミング方法

ここでは、S2JDBCを使ったデータベースプログラミングの方法を説明します。
S2JDBCは、Seasar2のO/R Mapperです。
S2JDBCの詳細については http://s2container.seasar.org/2.4/ja/s2jdbc_abstract.html を参照してください。

エンティティの作成

エンティティのソースコードは、S2JDBC-Gen を使ってデータベース上のテーブル定義から自動生成します。
S2JDBC-Genの使用方法については、 http://s2container.seasar.org/2.4/ja/s2jdbc_gen/setup.html
Antタスクの実行を参照してください。

サービスの作成

エンティティに対する操作を格納するクラスをサービスと呼びます。 ビジネスロジックは、エンティティかサービスに定義します。
サービスを作成する場合は、親クラスに org.seasar.extension.jdbc.service.S2AbstractService を指定します。
例えば、エンティティ「Sample」に対するサービスは、次のようになります。
public SampleService extends S2AbstractService<Sample> {
    ...
}

サービスクラスを利用したレコードの取得

アクションクラスでサービスを利用して Sample テーブルのレコードを全て取得する場合は、以下のように実装します。
public class SimpleAction { 
    /**
     * サービス
     */
    @Resource
    protected SampleService sampleService;

    /**
     * Sampleテーブルのデータ配列
     */
    public List<Sample> records;

    @Execute(validator = false)
    public String index() {
        records = sampleService.findAll();
        return "index.jsp";
    }
}
SampleServiceは、@Resource アノテーションを付与することで自動バインディングされます。
そのためサービスクラスのインスタンス生成は行う必要がありません。
SampleService#findAll()メソッドを利用すると、Sampleテーブルの全データが取得できます。

指定した条件に一致するレコードの取得

指定した条件に一致したレコードを取得したい場合は、JdbcManagerを使用してデータベースにアクセスします。
例えば、Sample テーブルからidが「sample01」のレコードを取得する場合は以下のように実装します。
public SampleService extends S2AbstractService<Sample> {
    /**
     * Sampleテーブルからidが「sample01」のデータを取得します。
     */
    public Sample getSample01() {
        return jdbcManager.from(Sample.class).where("id = ?", "sample01").getSingleResult();
    }
}

シェアードデータベースの利用

シェアードデータベースに接続するには、以下の設定を行う必要があります。
  1. 各 Web Application Server 毎にシェアードデータベースとして接続するデータソースの接続設定を行います。
  2. 任意の接続IDを指定して intra-mart Accel Platform のシェアードデータベース設定を行います。
  3. diconファイルにシェアードデータベースの設定を行います。
  4. シェアードデータベース接続用のサービスを作成します。

コラム

ここではS2JDBCの設定方法のみ説明します。
データソースの設定方法およびシェアードデータベース設定方法については、セットアップガイドを参照してください。
例えば、接続ID「sample」として設定されているシェアードデータベースに接続する場合、以下のように設定・実装します。
  1. 「jdbc.dicon」をコピーして、「tenant-jdbc.dicon」を作ります。
  2. 「jdbc.dicon」をコピーして、「shared-jdbc.dicon」を作り、以下のように編集することでシェアードデータベースを設定します。
    編集前
    ...
    <!-- from intra-mart -->
    <component  name="dataSource"
        class="jp.co.intra_mart.framework.extension.seasar.util.AutoDetectedDataSource">
    </component>
    ...
    
    編集後
    ...
    <!-- from intra-mart -->
    <component  name="sharedDataSource" class="jp.co.intra_mart.framework.extension.seasar.util.SharedDataSource">
        <arg>"sample"</arg><!-- 環境に応じた接続IDを指定 -->
    </component>
    ...
    
    argには、接続先シェアードデータベースの接続IDを指定します。環境に応じて変更してください。
  3. 「jdbc.dicon」を以下のように編集し、「tenant-jdbc.dicon」と「shared-jdbc.dicon」をincludeするだけにします。
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
        "https://www.seasar.org/dtd/components24.dtd">
    <components namespace="jdbc">
        <include path="tenant-jdbc.dicon"/>
        <include path="shared-jdbc.dicon"/>
    </components>
    
  4. 「s2jdbc.dicon」をコピーして、「s2tenant-jdbc.dicon」を作り、以下のように編集します。
    編集前
    ...
    <include path="jdbc.dicon"/>
    <include path="s2jdbc-internal.dicon"/>
    <component name="jdbcManager" class="org.seasar.extension.jdbc.manager.JdbcManagerImpl">
        <property name="maxRows">0</property>
    ...
    
    編集後
    ...
    <include path="tenant-jdbc.dicon"/><!-- この行を編集-->
    <include path="s2jdbc-internal.dicon"/>
    <component name="jdbcManager" class="org.seasar.extension.jdbc.manager.JdbcManagerImpl">
        <property name="maxRows">0</property>
    ...
    
  5. 「s2jdbc.dicon」をコピーして、「s2shared-jdbc.dicon」を作り、以下のように編集することでシェアードデータベース用のJdbcManagerを設定します。
    編集前
    ...
    <include path="jdbc.dicon"/>
    <include path="s2jdbc-internal.dicon"/>
    <component name="jdbcManager" class="org.seasar.extension.jdbc.manager.JdbcManagerImpl">
        <property name="maxRows">0</property>
    ...
    
    編集後
    ...
    <include path="shared-jdbc.dicon"/><!-- この行を編集-->
    <include path="s2jdbc-internal.dicon"/>
    <component name="sharedJdbcManager" class="org.seasar.extension.jdbc.manager.JdbcManagerImpl"><!-- この行を編集 -->
        <property name="maxRows">0</property>
    ...
    
  6. 「s2jdbc.dicon」を以下のように編集し、「s2tenant-jdbc.dicon」と「s2shared-jdbc.dicon」をincludeするだけにします。
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
        "https://www.seasar.org/dtd/components24.dtd">
    <components>
        <include path="s2tenant-jdbc.dicon"/>
        <include path="s2shared-jdbc.dicon"/>
    </components>
    
  7. 「app.dicon」に、「s2jdbc.dicon」がincludeされていることを確認します。includeされていなければincludeします。
  8. シェアードデータベース接続用のサービスクラスを作成し、@ResourceでJdbcManagerを指定します。
    public abstract class AbstractSharedDatabaseService<T> extends S2AbstractService<T> {
        @Resource(name="sharedJdbcManager")
        public void setJdbcManager(JdbcManager jdbcManager) {
            this.jdbcManager = jdbcManager;
        }
    }
    

コラム

複数のシェアードデータベースを利用するには、「jdbc.dicon」と「s2jdbc.dicon」をシェアードデータベース毎にコピーして設定する必要があります。

トランザクション

トランザクション制御の設定は customizer.dicon で行っています。 intra-mart Accel Platform 標準のトランザクション制御の
設定では、アクション、ロジック、サービスのメソッドに TxAttributeCustomizer が設定されております。
この設定により、メソッドの開始前にトランザクションが開始されていなければ開始され、実行メソッドが正常に終了した場合は
コミットされ、例外がスローされた場合にはロールバックされます。

UserTransaction#setRollbackOnly()を利用したロールバック

トランザクションをロールバックしたいが例外をスローしたくないというケースがよくがあります。
このような場合は、UserTransaction#setRollbackOnly()を利用することで、例外をスローせず
トランザクションをロールバックさせることができます。
public SampleAction {
    @Resource
    @ActionForm
    public SampleForm sampleForm;

    @Resource
    protected SampleService sampleService;

    @Resource
    protected UserTransaction userTransaction;

    /**
     * フォームの入力内容を登録します。
     */
    @Execute(validator = true)
    public String add() { 
        Sample entity = new Sample();
        // フォームの入力内容をエンティティにコピーします。
        BeanUtil.copyProperties(sampleForm, entity);
        try {
            // 登録処理
            sampleService.insert(entity);
        } catch (Exception e) {
            // トランザクションをロールバック
            try {
                userTransaction.setRollbackOnly(); 
            } catch (Exception e) {
                throw  new  SystemRuntimeException(e);
            }
        }
        return "index.jsp";
    }

}

UserTransactionオブジェクトを使用したトランザクションの完全手動制御

トランザクションの開始、コミット、ロールバックは、UserTransaction オブジェクトを使用して手動で制御することができます。
トランザクションを手動で制御する場合は、TransactionAttributeType.NOT_SUPPORTEDを利用して実行メソッドをトランザクション対象外にした上で、UserTransaction オブジェクトを使用したトランザクション制御を行います。
public SampleAction {
    @Resource
    @ActionForm
    public SampleForm sampleForm;

    @Resource
    protected SampleService sampleService;

    @Resource
    protected UserTransaction userTransaction;

    /**
     * フォームの入力内容を登録します。
     */
    @Execute(validator = true)
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public String add() { 
        Sample entity = new Sample();
        // フォームの入力内容をエンティティにコピーします。
        BeanUtil.copyProperties(sampleForm, entity);
        
        // トランザクションの開始
        userTransaction.begin();
        try {
            // 登録処理
            sampleService.insert(entity);
        } catch (Exception e) {
            // finally 句でトランザクションをロールバックさせるため、ロールバックを予約しておく
            userTransaction.setRollbackOnly(); 
        } finally {
            if (userTransaction.getStatus() == Status.STATUS_ACTIVE) {
                // コミット
                userTransaction.commit();
                return "index.jsp";
            } else {
                // ロールバック
                userTransaction.rollback();
                return "error.jsp";
            }
        }
    }
}