javajdbi

JDBI3 dynamically create a WHERE clause


How I can create dynamic where

public interface ThingDAO {
   @SqlQuery("SELECT * FROM things <where>)
   List<Thing> findThingsWhere(@Define("where") String where);
}

JDBI How can I dynamically create a WHERE clause while preventing SQL Injection?

But it's not actually for JDBI3


Solution

  • There are two main approaches to achieving dynamic filtering in queries:

    Static WHERE clause approach:

    SELECT * FROM things
    WHERE (:foo IS NULL OR foo_column = :foo)
    AND (:bar IS NULL or bar_column = :bar)
    

    Template engine approach

    Out of the box, Jdbi 3 only provides simple templating that replaces e.g. <where> with your @Define("where") parameter.

    This default template engine can be overridden with whatever you like. Jdbi provides additional template engines for StringTemplate 4, and for Freemarker.

    StringTemplate 4 is no longer actively maintained, so I'll just show you the example for Freemarker.

    FreeMarker

    Add a dependency:

    <dependency>
      <groupId>org.jdbi</groupId>
      <artifactId>jdbi3-freemarker</artifactId>
    </dependency>
    

    The @UseFreemarkerEngine annotation can be used on a SQL object, which causes the query to first be rendered as a Freemarker template.

    The @UseFreemarkerSqlLocator is like @UseFreemarkerEngine, but with the added bonus of loading SQL from files on the classpath. This permits refactoring commonly used SQL patterns into reusable files, which can be referenced through an #include directive.

    <#include "/org/jdbi/v3/freemarker/util.ftl">
    <#include "util2.ftl">
    select name from something
    where id in (<#list somethings as something>${something.id}<#sep>, </#list>)
    <@groupBy field="name" />
    <@orderBy field="name" />
    

    util.ftl:

    <#macro orderBy field order="ASC">
      ORDER BY ${field} ${order}
    </#macro>
    

    util2.ftl:

    <#macro groupBy field>
      GROUP BY ${field}
    </#macro>