@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
まとめ
- JDBIが依存しているバージョンの
org.antlr:stringtemplate
を依存性に追加 - DAOに
@UseStringTemplate3StatementLocator
を付ける - SQL自体をBindしたいところに
<query>
のように書く -
@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