SqlSession の利用

MyBatis では SqlSessionFactory を使って SqlSession を生成しました。 そして取得したセッションを使って Mapped Statement を実行し、接続をコミットあるいはロールバックした後、最終的に不要となったセッションをクローズする、というのが一連の流れでした。 MyBatis-Spring では、Spring のトランザクション設定に基づいて自動的にコミット、ロールバック、クローズされるスレッドセーフな SqlSession が注入されるので、直接 SqlSessionFactory を使う必要はありません。

SqlSessionTemplate

SqlSessionTemplate は MyBatis-Spring で最も重要なクラスです。このクラスが MyBatis の SqlSession を管理して、SQL メソッドの実行や例外の変換を行なっています。 このクラスは既存のコードで使用している SqlSession の代わりに使うことを前提に SqlSession インターフェイスを実装しています。SqlSessionTemplate はスレッドセーフで、複数の DAO, Mapper 間で共有することができます。

getMapper() から返された Mapper のメソッドも含めて、SQL メソッドを呼び出す場合、確実に現在の Spring トランザクションに付加された SqlSession が使われるようにするのも SqlSessionTemplate の仕事です。 それ以外にも、セッションのクローズや状況に応じたコミットあるいはロールバックといったライフサイクルの管理、更には MyBatis の例外を Spring の DataAccessException に変換する処理も行います。

SqlSessionTemplate は Spring が管理するトランザクション内で実行され、また Spring によってインジェクトされる複数の Mapper から呼び出すことができるようにスレッドセーフとなっているので、常にこのクラスを MyBatis のデフォルト実装である DefaultSqlSession の代わりに使用するべきです。 同一アプリケーション内でこれら2つのクラスを混在させて使用するとデータの不整合などの問題が発生する可能性があります。

SqlSessionTemplate を生成する際は、SqlSessionFactory をコンストラクタ引数として渡します。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory());
  }
}

この Bean は、直接あなたの DAO Bean にインジェクト(注入)することができます。注入対象の Bean には SqlSession プロパティを定義しておく必要があります。

public class UserDaoImpl implements UserDao {

  private SqlSession sqlSession;

  public void setSqlSession(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }

  public User getUser(String userId) {
    return sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

そして、以下のようにして SqlSessionTemplate を注入します。

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSession" ref="sqlSession" />
</bean>

SqlSessionTemplate には、ExecutorType.BATCH を引数に取るコンストラクタも定義されています。 このコンストラクタを使うと、例えばバッチ処理を行う SqlSession を取得することができます。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
  <constructor-arg index="1" value="BATCH" />
</bean>
@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionTemplate sqlSession() throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory(), ExecutorType.BATCH);
  }
}

これで実行されるステートメントは全てバッチ処理の対象となります。DAO クラス中では、例えば次のように書くことができます。

public class UserService {
  private final SqlSession sqlSession;
  public UserService(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }
  public void insertUsers(List<User> users) {
    for (User user : users) {
      sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
    }
  }
}

デフォルト以外の ExecutorType を使用する場合にのみ SqlSessionFactory の Bean を定義する際に2つの引数を指定する必要があります。

この初期化方式を使用する場合の注意点として、このメソッドが呼び出される時点で異なる ExecutorType で実行されているトランザクションが存在していてはならない、という制限があります。 そのためには、異なる ExecutorType が指定された SqlSessionTemplate への呼び出しを、それぞれが独立したトランザクション内で実行する(例えば PROPAGATION_REQUIRES_NEW を指定しておく)か、あるいは完全にトランザクションの外で実行するようにしてください。

SqlSessionDaoSupport

SqlSessionDaoSupportSqlSession を提供する抽象クラスです。getSqlSession() を呼び出すことで、SQL メソッドを実行するための SqlSessionTemplate を取得することができます。

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

普通は MapperFactoryBean を使った方がコード量が少なくて済みますが、DAO の中で MyBatis 以外の処理を行うため実装クラスが必要となる場合には便利なクラスです。

SqlSessionDaoSupport を使用する際は、sqlSessionFactory または sqlSessionTemplate をセットする必要があります。もし両方のプロパティがセットされた場合、sqlSessionFactory は無視されます。

SqlSessionDaoSupport のサブクラスである UserDaoImpl を Spring Bean として定義する例を挙げておきます。

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>