MyBatisの@SelectProviderでListやMapの値をバインド変数に指定
MyBatisの@SelectProvider
のマッピングで、Listの要素をバインド変数に指定する方法がやっと分かったのでメモ。
Mapの場合も合わせて書いておきます。
環境
今回試した環境は以下の通り。おそらくですがMyBatisがver.3なら問題ないと思います。
- MyBatis 3.4.6
- mybatis-spring-boot-starter 1.3.2
- Java 8 以上
準備
以下のコードを試すには、SpringBootが必要です。
ここでは、動作を簡単に確認できるように、SpringBootのCommandLineRunnerを使って実行できるようにしておきます。
MyBatis側は、app.model.Task
と関連するORMがセッティング済みとします。
App
-CommandLineRunner
の実装サンプル
package app; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ComponentScan; import app.dao.TaskDao; @EnableAutoConfiguration @EnableConfigurationProperties @ComponentScan("app") // 今回はスキャン対象がこのクラスのサブパッケージにあるので必須ではない public final class App implements CommandLineRunner { @Autowired TaskDao dao; @Override public void run(String... args) throws Exception { // ここにメイン処理を書く } public static void main(String[] args) { SpringApplication application = new SpringApplication(App.class); application.setWebEnvironment(false); ApplicationContext context = application.run(); SpringApplication.exit(context); } }
@SelectProvider
の実装サンプルと呼び出し例
Mapの場合は、参考ページに書いてあったので分かりましたが、肝心のListの場合はググっても見つけられなかったので、それっぽく書いてみたらできました。
具体的には...@Param("keywords") List<String>
に対して#{keywords[0]}
と書けば、そのバインド変数にkeywords.get(0)
の値が割当てられます。
TaskDao
-@SelectProvider
の実装サンプル
package app.dao; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.ResultMap; import org.apache.ibatis.annotations.SelectProvider; import org.apache.ibatis.jdbc.SQL; import app.model.Task; @Mapper public interface TaskDao { @SelectProvider(type = SqlProvider.class, method = "findBy") @ResultMap("app.dao.TaskMapper.BaseResultMap") List<Task> findBy(@Param("keywords") List<String> keywords, @Param("params") Map<String, Object> params); static final class SqlProvider { public SqlProvider() { } public String findBy(@Param("keywords") List<String> keywords, @Param("params") Map<String, Object> params) { return new SQL() {{ SELECT("*"); FROM("tasks"); // ※このLIKE句はMySQLだとエラーにはなりませんが正しく動作しませんでした // SQL を 'like #{...}'に、 値を %xxx% にすれば動作します for (int i = 0, n = keywords.size(); i < n; i++) { WHERE("title || ' ' || body like '%'|| #{keywords[" + i + "]} ||'%'"); } if (params.containsKey("word")) { WHERE("title || ' ' || body like '%'|| #{params.word} ||'%'"); } }}.toString(); } } }
TaskDao
の呼び出し例
// importは省略 // Appのrunメソッド List<String> keywords = new ArrayList<>(Arrays.asList("meeting", "shopping")); Map<String, Object> params = new HashMap<>(); params.put("word", "urgent"); List<Task> records = dao.findBy(keywords, params); records.stream().forEach(x -> System.out.println(x.getTitle()));
- 実行時のデバッグログ
11:31:27.154 [main] DEBUG app.dao.TaskDao.findBy - ==> Preparing: SELECT * FROM tasks WHERE (title || ' ' || body like '%'|| ? ||'%' AND title || ' ' || body like '%'|| ? ||'%' AND title || ' ' || body like '%'|| ? ||'%') 11:31:27.205 [main] DEBUG app.dao.TaskDao.findBy - ==> Parameters: meeting(String), shopping(String), urgent(String)
参考URL
MyBatis で生SQLを叩きたい (Qiita)
https://qiita.com/kumazo/items/72ecdb2923b77aaa0c94
MyBatis - MyBatis 3 | SQL ビルダークラス
http://www.mybatis.org/mybatis-3/ja/statement-builders.html
(おわり)