Spring Boot と MyBatis を組み合わせて使用する
クエリレベルでのチューニングの必要があるようなプロジェクトで、Hibernate を適用できないといった場合にたびたび採用されるであろう MyBatis。今回はこれを Spring Boot と組み合わせてみる。
Spring Boot を使用すると、コンフィグレーションのほとんどがアノテーションベースになるため、MyBatis もそうかと思えば、そうではない。MyBatis は MyBatis で、これまでどおり XML ベースのコンフィグレーションファイルが必要になるので、ここは事前に認識しておく。
Spring と MyBatis の連携には、mybatis-spring
という、インテグレーションモジュールがでているので、まずは POM にこれらを追加する。
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1</version> </dependency>
Spring から MyBatis を使用する場合には、SqlSessionTemplate
クラスを使用するので、これを構成する。
@SpringBootApplication public class ApplicationContext { @Autowired @Bean public DataSourceInitializer dataSourceInitializer( @Qualifier("dataSource") DataSource dataSource) { DataSourceInitializer dataSourceInitializer = new DataSourceInitializer(); dataSourceInitializer.setDataSource(dataSource); ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); databasePopulator.addScript(new ClassPathResource("sql")); dataSourceInitializer.setDatabasePopulator(databasePopulator); dataSourceInitializer.setEnabled(false); return dataSourceInitializer; } @Autowired @Bean public DataSourceTransactionManager transactionManager( @Qualifier("dataSource") DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } @Autowired @Bean public SqlSessionTemplate sqlSessionTemplate( @Qualifier("dataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver( new DefaultResourceLoader()); // MyBatis のコンフィグレーションファイル bean.setConfigLocation(resolver.getResource("classpath:config.xml")); // MyBatis で使用する SQL ファイル群 bean.setMapperLocations(resolver.getResources("classpath:sql/*.xml")); return new SqlSessionTemplate(bean.getObject()); } @Primary @Autowired @Bean public DriverManagerDataSource dataSource() { // 今回の例は ORACLE、適宜変更する DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@//localhost:1521/xe"); dataSource.setUsername("system"); dataSource.setPassword("manager"); return dataSource; } }
続いて、MyBatis 側でのコンフィグレーションファイルの内容。この設定ファイルがない場合、アンダースコア区切り (スネークケース) のカラム名が、キャメルケースのフィールドにマッピングされないため、ほとんど必須。
<?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"/> <!-- SQL 内で AS によって設定された列名をマップする設定 --> <setting name="useColumnLabel" value="true"/> </settings> <!-- カラムタイプによりデフォルトとは異なる O/R 変換処理が必要になる場合に設定 --> <!-- <typeHandlers> <typeHandler handler="package.name.LocalDateTypeHandler"/> </typeHandlers> --> </configuration>
最後に Repository (永続化) レイヤでの SqlSessionTemplate
の使い方と SQL ファイルのマッピングを例にあげておく。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 任意の名前空間を定義する --> <mapper namespace="SalesRepository"> <!-- 名前空間 + ID でプロジェクト全体で一意になるように名前を設定 --> <!-- ParameterType は SqlSessionTemplate で渡すパラメタの型 --> <!-- ResultType は SqlSessionTemplate で返ってくるデータの型 --> <select id="findSummaryByYearMonthRange" parameterType="jp.hotpepper.beauty.frozen.model.entity.SearchCondition" resultType="jp.hotpepper.beauty.frozen.model.entity.SalesSummary"> SELECT STORE.STORE_ID AS STORE_ID, STORE.NAME AS STORE_NAME, COUNT(SALES.SALES_ID) AS SALES_COUNT, SUM(SALES.SALES) AS SALES_TOTAL, AVG(CUSTOMER.AGE) AS AGE_AVERAGE FROM SALES LEFT OUTER JOIN STORE ON SALES.STORE_ID = STORE.STORE_ID LEFT OUTER JOIN CUSTOMER ON SALES.CUSTOMER_ID = CUSTOMER.CUSTOMER_ID WHERE <!-- パラメタ型のフィールド名を #{ } で囲んで設定 --> <!-- パラメタ型が String など構造を持たない場合は任意の名前でよい --> SALES.CREATED BETWEEN #{from} AND #{to} GROUP BY STORE.STORE_ID, STORE.NAME ORDER BY STORE_ID </select> </mapper>
@Repository public class MybatisSalesRepository implements SalesRepository { @Autowired private SqlSessionTemplate template; @Override public List<SalesSummary> findSummaryByYearMonth(SearchCondition condition) { List<SalesSummary> items = this.<SalesSummary>getTemplate().selectList( // SQL を定義した XML での、名前空間 + ID を指定 // ID だけでもプロジェクト全体で一意になる場合は、ID のみの指定も可能 "SalesRepository.findSummaryByYearMonthRange", condition); return items; } protected SqlSessionTemplate getTemplate() { return template; } }
全体としてはこのような形で、Spring Boot で MyBatis を使用できるようになる。MyBatis のコンフィグレーションファイルを、アプリケーションコンテキストで別途読み込まないといけないところに気付くまでにずいぶん時間がかかってしまった。