BootのO/RマッパーにMyBatisを使いたい。
DBアクセス層に何を使うかって、本当によく話題になりますよね。
「これで間違いないでしょ」っていう鉄板の選択肢がないから、だと思うわけですが
「SQLを書きたい日本人」な僕としては、消去法的にMyBatisを使っています。
消去法って言うからには、消えた選択肢があるわけで。
Hibernate : アイドントライク ハイバネートサン
JPA : アイドントライク ハイバネートサン
Doma : アイドントライク APT
S2JDBC : キャノット ユーズ ウィズ スプリング
DBflute : 少し文化が違うんです。
Mirage : 開発止まっちゃったし。
ホントはMirageあたりが大好物で、過去に実案件に投入した時には
まったく問題が起きなくて素晴らしかったのですが
開発が止まっていることと、実績的なアレでなかなか使いにくいんです。
そんなわけで消去法的に残ったMyBatisを使うため
Spring Boot + MyBatisの設定をしました。
ところで、テーブルのカラム名はスネークケース、JavaのEntityはキャメルケース、
という組み合わせで使うことは、とてもよくあることだと思いますが、
MyBatisでそれを行うためには、設定ファイルを書く必要があります。
(あるいはConfigurationクラスを使う)
また、Java8で使えるようになったLocalDateクラスを利用する場合も
TypeHandlerと設定ファイルを書く必要があります。
その辺りを盛り込んだのが、以下になります。
src/main/java/適当なパッケージ/MyBatisConfig.java
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan("package.names.to.persistence") // Mapper層のパッケージ名を指定 public class DataConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(Entity.class.getPackage().getName()); sessionFactory.setConfigLocation(new ClassPathResource("/mybatis-config.xml")); return sessionFactory; } }
このように、mybatis-config.xmlを読み込むように設定を入れました。
設定ファイルは、こんな感じになります。
src/main/resources/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeHandlers> <typeHandler handler="xxx.LocalDateTypeHandler"/> </typeHandlers> </configuration>
スネークケースからキャメルケースへの変換と
LocalDateを使うための型ハンドラの定義を書いています。
型ハンドラの実装は、こんな感じです。
src/main/java/xxx/LocalDateTypeHandler.java
package xxx; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedTypes; import java.sql.CallableStatement; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDate; @MappedTypes(LocalDate.class) public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> { @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, LocalDate localDate, JdbcType jdbcType) throws SQLException { preparedStatement.setDate(i, Date.valueOf(localDate)); } @Override public LocalDate getNullableResult(ResultSet resultSet, String s) throws SQLException { Date date = resultSet.getDate(s); if (date == null) { return null; } return date.toLocalDate(); } @Override public LocalDate getNullableResult(ResultSet resultSet, int i) throws SQLException { return resultSet.getDate(i).toLocalDate(); } @Override public LocalDate getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return callableStatement.getDate(i).toLocalDate(); } }
こんな感じで設定をすれば、あとは世の中にあるサンプル通りに
Mapperクラスを作り、domainクラスを作り、SQLを書いたXMLファイルを作れば動きます。
ただIDEからJUnitを動かした後に、mvn spring-boot:runで起動したり、
逆に、mvn spring-boot:runして起動&停止した後に、JUnitを実行させたりすると
なぜか設定ファイルが見つからず無限ループになってしまうなど、微妙にバギーな挙動をします。
あと、MyBatisのConfigurationクラスと、SpringのConfigurationアノテーションは
名前が被ってしまっているからMyBatisのConfigurationクラスが使いにくいとか、
どうも、Spring Boot + MyBatisの組み合わせは、こなれてない印象があります。
もう少しバージョンが上がれば、良い感じになっていくのかな?