Статьи

Шаблонирование SQL с помощью jOOQ или MyBatis

Многие сравнивают  JOOQ  с  MyBatis . Оба они рассматриваются как популярные альтернативы стандарту устойчивости Java в JPA, так как оба они гораздо более ориентированы на SQL, чем сам JPA. При сравнении двух инструментов первое очевидное отличие заключается в следующем:

  • jOOQ — это внутренний язык, специфичный для предметной области, моделирующий SQL через свободный API Java .
  • MyBatis — это механизм шаблонирования и отображения SQL на основе XML, в котором динамический SQL может создаваться с помощью XML-DSL.

Текущий успех MyBatis в основном основан на том, что он предоставил жизнеспособную альтернативу JPA в то время, когда JPA был еще спорным стандартом, и когда JPA пришлось доказать, что он лучше, чем  JDO , что решает очень похожие проблемы. Эта предоставленная альтернатива — то, что нравится многим пользователям, ориентированным на SQL:

  • Разделение кода Java и SQL, извлечение кода SQL во внешние файлы. Это позволяет администраторам баз данных исправлять строки SQL в продуктивной среде, добавлять подсказки и выполнять другие настройки.
  • Автоматическое сопоставление данных табличных результатов с объектами. Это также достигается в том же XML DSL, что и спецификация динамического SQL.

Реализация шаблонов SQL с помощью jOOQ

Эти вещи могут быть достигнуты и с  JOOQ  . Но в отличие от MyBatis, SQL-шаблоны jOOQ (как будет представлено в jOOQ 3.2) не будут использовать проприетарный язык шаблонов. Вы должны иметь возможность самостоятельно выбирать язык, предоставляя jOOQ очень простой адаптер. Это позволит использовать:

Давайте посмотрим на пример шаблона Velocity. В этом примере добавляется динамический список параметров ID в предложение WHERE:

SELECT
  a.first_name,
  a.last_name,
  count(*)
FROM
  t_author a
LEFT OUTER JOIN
  t_book b ON a.id = b.author_id
WHERE
  1 = 0
#foreach ($param in $p)
  OR a.id = ?
#end
GROUP BY
  a.first_name,
  a.last_name
ORDER BY
  a.id ASC

Приведенный выше шаблон может быть передан следующей реализации шаблона jOOQ, которая использует произвольные входные объекты для создания конкретного jOOQ QueryPart. QueryPart — это объект, который может отображать переменные SQL и связывать:

class VelocityTemplate
implements org.jooq.Template {
 
  private final String file;
 
  public VelocityTemplate(String file) {
    this.file = file;
  }
 
  @Override
  public QueryPart transform(Object... input) {
 
    // Velocity code
    // -----------------------------------------
    URL url = this.getClass().getResource(
      "/org/jooq/test/_/templates/");
    File file = url.getFile();
 
    VelocityEngine ve = new VelocityEngine();
    ve.setProperty(RESOURCE_LOADER, "file");
    ve.setProperty(FILE_RESOURCE_LOADER_PATH,
      new File(file ).getAbsolutePath());
    ve.setProperty(FILE_RESOURCE_LOADER_CACHE,
      "true");
    ve.init();
 
    VelocityContext context = new VelocityContext();
    context.put("p", input);
 
    StringWriter writer = new StringWriter();
    ve.getTemplate(file, "UTF-8")
      .merge(context, writer);
 
    // jOOQ Code
    // -----------------------------------------
    return DSL.queryPart(writer.toString(), input);
  }
}

Очень простой клеевой код. Поскольку у вас есть полный контроль над адаптером реализации механизма шаблонов, вы также можете добавить в свой адаптер кэширование, пулы объектов и т. Д.

Приведенный выше шаблон можно легко использовать в API jOOQ, где jOOQ допускает простой SQL. Например, как запрос верхнего уровня:

Template tpl = new VelocityTemplate(
    "authors-and-books.vm");
 
DSL.using(configuration)
   .resultQuery(tpl, 1, 2, 3)
   .fetch();

Или в виде вложенного выбора, встроенного в типичный DSL jOOQ:

DSL.using(configuration)
   .select()
   .from(new TableSourceTemplate("my-table.vm"))
   .fetch();

Конечно, вы также можете воспользоваться функциями отображения записей в jOOQ, которые позволяют вам реализовать собственные алгоритмы отображения таблиц в объекты. Часто это может быть лучшим выбором, чем полагаться на любую аппаратную конфигурацию XML, такую ​​как MyBatis:

List<MyType> result =
DSL.using(configuration)
   .select()
   .from(new TableSourceTemplate("my-table.vm"))
   .fetch(new RecordMapper<Record, MyType>() {
      public MyType map(Record record) {
        // Custom mapping logic here
      }
   });

или с Java 8: 

List<MyType> result =
DSL.using(configuration)
   .select()
   .from(new TableSourceTemplate("my-table.vm"))
   .fetch((Record) -> new MyType().init(record));

Возможности велики

Шаблонирование SQL является мощным инструментом, когда вы предпочитаете простой, основанный на строках SQL, который можно время от времени настраивать с помощью небольшого цикла или оператора if, чтобы вставить какое-то динамическое предложение SQL. Есть несколько механизмов SQL, которые пытаются решить эту проблему тем или иным способом:

Среди всего вышеперечисленного все инструменты поставляются с простым проприетарным языком шаблонов, но jOOQ — единственный, который поощряет вас использовать выбранный вами шаблонизатор и, таким образом, предлагает произвольную расширяемость шаблонов в будущем.