トランザクション

Doma はローカルトランザクションをサポートしています。このドキュメントでは、ローカルトランザクションの設定方法と使用方法について説明します。

グローバルトランザクションを使用する場合は、JTA (Java Transaction API) をサポートするフレームワークまたはアプリケーションサーバーを使用してください。

設定クラスの定義 も参照してください。

設定

ローカル トランザクションを使用するには、次の条件が必要です。

  • ConfiggetDataSource から LocalTransactionDataSource を返します。

  • コンストラクターで上記の LocalTransactionDataSource を使用して LocalTransactionManager を生成します

  • 上記の LocalTransactionManager を使用してデータベースアクセスを制御します

LocalTransactionManager を生成・取得する方法はいくつかありますが、最も簡単な方法は Config 実装クラスのコンストラクタ内で生成し、 Config 実装クラスをシングルトンにする方法です。

以下に例を示します。

public class DbConfig implements Config {

    private static final DbConfig CONFIG = new DbConfig();

    private final Dialect dialect;

    private final LocalTransactionDataSource dataSource;

    private final TransactionManager transactionManager;

    private DbConfig() {
        dialect = new H2Dialect();
        dataSource = new LocalTransactionDataSource(
                "jdbc:h2:mem:tutorial;DB_CLOSE_DELAY=-1", "sa", null);
        transactionManager = new LocalTransactionManager(
                dataSource.getLocalTransaction(getJdbcLogger()));
    }

    @Override
    public Dialect getDialect() {
        return dialect;
    }

    @Override
    public DataSource getDataSource() {
        return dataSource;
    }

    @Override
    public TransactionManager getTransactionManager() {
        return transactionManager;
    }

    public static DbConfig singleton() {
        return CONFIG;
    }
}

使用法

コード例では次の DAO インターフェイスを使用します。

@Dao
public interface EmployeeDao {
    @Sql("select /*%expand*/* from employee where id = /*id*/0")
    @Select
    Employee selectById(Integer id);

    @Update
    int update(Employee employee);

    @Delete
    int delete(Employee employee);
}

トランザクションの開始と終了

次の TransactionManager のメソッドのいずれかを使用してトランザクションを開始できます。

  • required

  • requiresNew

  • notSupported

ラムダ式を使用して、トランザクションで実行する処理を記述します。

TransactionManager tm = DbConfig.singleton().getTransactionManager();
EmployeeDao dao = new EmployeeDaoImpl(DbConfig.singleton());

tm.required(() -> {
    Employee employee = dao.selectById(1);
    employee.setName("hoge");
    employee.setJobType(JobType.PRESIDENT);
    dao.update(employee);
});

ラムダ式が正常に終了すると、トランザクションはコミットされます。ラムダ式が例外をスローした場合、トランザクションはロールバックされます。

明示的なロールバック

例外をスローする以外に、 setRollbackOnly メソッドを使用してトランザクションをロールバックすることもできます。

TransactionManager tm = DbConfig.singleton().getTransactionManager();
EmployeeDao dao = new EmployeeDaoImpl(DbConfig.singleton());

tm.required(() -> {
    Employee employee = dao.selectById(1);
    employee.setName("hoge");
    employee.setJobType(JobType.PRESIDENT);
    dao.update(employee);
    // Mark as rollback
    tm.setRollbackOnly();
});

セーブポイント

セーブポイントを使用すると、トランザクション内の特定の変更をキャンセルできます。

TransactionManager tm = DbConfig.singleton().getTransactionManager();
EmployeeDao dao = new EmployeeDaoImpl(DbConfig.singleton());

tm.required(() -> {
    // Search and update
    Employee employee = dao.selectById(1);
    employee.setName("hoge");
    dao.update(employee);

    // Create a savepoint
    tm.setSavepoint("beforeDelete");

    // Delete
    dao.delete(employee);

    // Rollback to the savepoint (cancel the deletion above)
    tm.rollback("beforeDelete");
});