トランザクション

これは MyBatis-Spring を使う主な理由の一つでもありますが、MyBatis-Spring を使うと MyBatis の処理を Spring が管理するトランザクションの一部として実行できるようになります。 MyBatis-Spring は、MyBatis のために新しいトランザクションマネージャーを生成するのではなく、Spring が生成した DataSourceTransactionManager を利用します。

Spring のトランザクションマネージャーが定義されていれば、通常の手順で Spring のトランザクションを利用することができます。@Transactional アノテーションと AOP 形式での指定、どちらも利用可能です。 トランザクション内では SqlSession が一つ生成され、トランザクションの生存期間中はこの SqlSession が使用されます。このセッションは、トランザクション完了時にコミットあるいはロールバックされます。

MyBatis-Spring ではトランザクションは透過的に管理されるので、あなたの DAO クラスにコードを追加する必要はありません。

標準的な設定

Spring の 設定ファイルで DataSourceTransactionManager を生成するだけで、Spring のトランザクション処理が有効となります。

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource" />
</bean>
@Configuration
public class DataSourceConfig {
  @Bean
  public DataSourceTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
  }
}

ここで指定する DataSource は、通常 Spring で利用される JDBC DataSource であればどのようなデータソースでも構いません。 例えば、コネクションプールや JNDI 経由で取得した DataSource などです。

ただし、トランザクションマネージャーに対して指定する DataSource は、SqlSessionFactoryBean に対して指定したものと同じでなくてはなりません。もし別のデータソースを指定した場合、トランザクション機能は正しく動作しません。

Container Managed Transactions

JEEコンテナを利用していて、Spring の処理を CMT (Container Managed Transaction) の一部として利用したい場合、JtaTransactionManager あるいはそのコンテナ固有のサブクラスを使って Spring を設定する必要があります。 最も簡単なのは、Spring のトランザクション名前空間 又は JtaTransactionManagerFactoryBean を使う方法です。

<tx:jta-transaction-manager />
@Configuration
public class DataSourceConfig {
  @Bean
  public JtaTransactionManager transactionManager() {
    return new JtaTransactionManagerFactoryBean().getObject();
  }
}

このように設定しておくと MyBatis は、CMT を使うように設定された他の Spring リソースと同じように動作します。 Spring は、既存のコンテナ管理されたトランザクションがあれば、そのトランザクションに SqlSession を付加して利用します。 もしトランザクションを要求する処理が呼び出された時点で開始されたトランザクションがなければ、Spring が新しいコンテナ管理されたトランザクションを開始します。

CMT は使いたいが、Spring のトランザクション管理は利用したくないという場合、Spring のトランザクションマネージャーを定義してはいけません。 またこの場合、MyBatis 側で生成された ManagedTransactionFactory を使うように SqlSessionFactoryBean を設定する必要があります。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionFactory">
    <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>
</bean>
@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionFactory sqlSessionFactory() {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setTransactionFactory(new ManagedTransactionFactory());
    return factoryBean.getObject();
  }
}

トランザクションをプログラム的に制御する

MyBatis の SqlSession では、トランザクションをプログラム的に制御するためのメソッドが用意されています。 しかし、MyBatis-Spring では、あなたの Bean にインジェクト(注入)されるのは Spring が管理する SqlSession あるいは Mapper です。つまり、トランザクションを制御するのは常に Spring でなくてはなりません。

Spring が管理している SqlSession に対して SqlSession.commit(), SqlSession.rollback(), SqlSession.close() を呼び出すことはできません。 もしこれらのメソッドを呼び出した場合、UnsupportedOperationException がスローされます。あなたの Bean に注入される Mapper クラスでは、これらのメソッドは隠蔽されています。

Spring が管理するトランザクションの外側で SqlSession のデータメソッドあるいは Mapper メソッドを呼び出した場合、JDBC 接続に対する auto-commit の設定に関わらず、変更は直ちにコミットされます。

もしあなたがトランザクションをプログラム的に制御したいのであれば、the Spring reference document(Data Access -Programmatic transaction management- を参照してください。 以下のコードは、PlatformTransactionManager を使ってトランザクションを手動で制御する例です。

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionStatus txStatus =
        transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
      userMapper.insertUser(user);
    } catch (Exception e) {
      transactionManager.rollback(txStatus);
      throw e;
    }
    transactionManager.commit(txStatus);
  }
}

TransactionTemplate を使用して commitrollback メソッドを省略することもできます。

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.execute(txStatus -> {
      userMapper.insertUser(user);
      return null;
    });
  }
}

ここでは Mapper を使っていますが、SqlSession を使うこともできます。