Sudhakar Rayavaram
Problem solver (And maker),
Inquisitive (Root to most of my problems),
Software craftsman (Don't ask me for estimates)

Part of TarkaLabs

Technical Advisor for SportIndia

17 Feb 2014
Externalize JDBI queries

Usual style of writing queries with JDBI is to embed them in method level annotations like below

1     public interface WhyDao {
2         @SqlQuery("select * from whytable where name = :name order by created_date")
3         public abstract List<Why> getWhys(@Bind("name") String name);
4     }

This type of embedding queries is good for small ones. Since java does not (yet) support multiline strings, this style gets messy with long queries (I am talking about queries that goes beyond 3 lines with sensible line widths).

Fortunately JDBI offers an alternate if you really wanted to keep the queries easily readable and editable.

1     @OverrideStatementLocatorWith(QueryLocator.class)
2     public interface WhyDao {
3         @SqlQuery
4         public abstract List<Why> getWhys(@Bind("name") String name);
5     }

Above is the same class rewritten by externalizing the query string. The key element in this code is the annotation @OverrideStatementLocatorWith. It takes in a subclass of StatementLocator class (QueryLocator here) which will have the strategy to find the sql string to use.

An example implementation of StatementLocator might look like this

 1     public class QueryLocator implements StatementLocator {
 2         @Override
 3         public String locate(String name, StatementContext ctx) throws Exception {
 4             String query = Queries.get(name);
 5             if (query == null) {
 6                 throw new RuntimeException("Unable to find any query for '" + name + "'");
 7             }
 8             return query;
 9         }
10     }

name parameter will contain the method name (in our case 'getWhys') which can be use to find the appropriate query string. Queries class is my custom singleton class which loads all the queries from external text file(s) containing sqls during bootstrap.