LoginSignup
4
2

More than 5 years have passed since last update.

JDBIでDynamic Query(動的クエリ)

Last updated at Posted at 2016-03-11
@Grab('org.jdbi:jdbi:2.71')
@Grab('org.antlr:stringtemplate:3.2.1')
@Grab('com.h2database:h2:1.4.191')

import org.skife.jdbi.v2.DBI
import org.skife.jdbi.v2.sqlobject.SqlQuery
import org.skife.jdbi.v2.sqlobject.customizers.Define
import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator

@UseStringTemplate3StatementLocator
public interface MyDAO {
    @SqlQuery("<query> --comment\\<>")
    String query(@Define("query") String query)
}

DBI dbi = new DBI("jdbc:h2:mem:test")
MyDAO myDAO = dbi.onDemand(MyDAO.class)
String result = myDAO.query("select now()")

println result

まとめ

  1. JDBIが依存しているバージョンの org.antlr:stringtemplate を依存性に追加
  2. DAOに @UseStringTemplate3StatementLocator を付ける
  3. SQL自体をBindしたいところに <query> のように書く
  4. @Define をBindする変数に付ける

よもやま

JDBIのorg.antlr:stringtemplateのdependencyが、optionalがtrueになっているため明示的に依存性として付ける必要がある。これをしないとアノテーションエラーが出る。JDBIが依存しているバージョンを指定せず、最新を指定してもエラーが出たので正直ハマった。

java.lang.annotation.AnnotationFormatError: Invalid default: public abstract java.lang.Class org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator.errorListener()

@UseStringTemplate3StatementLocatorを付ける場合、Bindする以外の<\\<のようにエスケープする必要がある。例えば select * from where now() < foo_atのようなSQL。
これをしないとアプリ自体は止まらないが、例外が発生する。

problem parsing template 'PHF1ZXJ5PiAtLWNvbW1lbnRcPD4gPA=='
line 1:22: unexpected char: '<'
    at org.antlr.stringtemplate.language.AngleBracketTemplateLexer.nextToken(AngleBracketTemplateLexer.java:149)
    at antlr.TokenBuffer.fill(TokenBuffer.java:69)
    at antlr.TokenBuffer.LA(TokenBuffer.java:80)
    at antlr.LLkParser.LA(LLkParser.java:52)
    at org.antlr.stringtemplate.language.TemplateParser.template(TemplateParser.java:103)
    at org.antlr.stringtemplate.StringTemplate.breakTemplateIntoChunks(StringTemplate.java:850)
    at org.antlr.stringtemplate.StringTemplate.setTemplate(StringTemplate.java:441)
    at org.antlr.stringtemplate.StringTemplateGroup.defineTemplate(StringTemplateGroup.java:679)
    at org.skife.jdbi.v2.sqlobject.stringtemplate.StringTemplate3StatementLocator.locate(StringTemplate3StatementLocator.java:260)
    at org.skife.jdbi.v2.SQLStatement.wrapLookup(SQLStatement.java:1284)
    at org.skife.jdbi.v2.SQLStatement.internalExecute(SQLStatement.java:1293)
    at org.skife.jdbi.v2.Query.fold(Query.java:173)
    at org.skife.jdbi.v2.Query.first(Query.java:273)
    at org.skife.jdbi.v2.Query.first(Query.java:264)
    at org.skife.jdbi.v2.sqlobject.ResultReturnThing$SingleValueResultReturnThing.result(ResultReturnThing.java:110)
    at org.skife.jdbi.v2.sqlobject.ResultReturnThing.map(ResultReturnThing.java:46)
    at org.skife.jdbi.v2.sqlobject.QueryHandler.invoke(QueryHandler.java:43)
    at org.skife.jdbi.v2.sqlobject.SqlObject.invoke(SqlObject.java:212)
    at org.skife.jdbi.v2.sqlobject.SqlObject$2.intercept(SqlObject.java:109)
    at org.skife.jdbi.v2.sqlobject.CloseInternalDoNotUseThisClass$$EnhancerByCGLIB$$6a1f7715.query(<generated>)
    at MyDAO$query.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:122)
    at jdbi.run(jdbi.groovy:21)
    at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:261)
    at groovy.lang.GroovyShell.run(GroovyShell.java:522)
    at groovy.lang.GroovyShell.run(GroovyShell.java:511)
    at groovy.ui.GroovyMain.processOnce(GroovyMain.java:650)
    at groovy.ui.GroovyMain.run(GroovyMain.java:381)
    at groovy.ui.GroovyMain.process(GroovyMain.java:367)
    at groovy.ui.GroovyMain.processArgs(GroovyMain.java:126)
    at groovy.ui.GroovyMain.main(GroovyMain.java:106)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:106)
    at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:128)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

既に存在するDAOに上記アノテーションを付ける場合、<が存在するか調べエスケープすることを徹底する。

参考

http://stackoverflow.com/questions/19424573/how-to-do-in-query-in-jdbi
http://search.maven.org/#artifactdetails%7Corg.jdbi%7Cjdbi%7C2.71%7Cjar

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2