Статьи

Введите Safe SQL в Java

Струны, Струны, Струны

Независимо от того, используете ли вы фреймворки, такие как JPA , MyBatis или Spring Data JDBC, вы всегда заканчиваете тем, что объявляете операторы SQL в виде строки Java.
Проблема с этим подходом заключается в том, что вы должны писать тесты для каждого оператора, чтобы убедиться, что это даже допустимый SQL. Нет никакой гарантии времени компиляции, что инструкция SQL будет выполнена.

Избавьтесь от струн!

Встроенный SQL

Я начал профессиональную разработку программного обеспечения в 1995 году на программировании мэйнфрейм-компьютеров IBM в COBOL. Для доступа к базе данных мы использовали что-то под названием «Встроенный SQL»:

1
2
3
4
5
EXEC SQL
SELECT lastname, firstname
INTO :lastname, :firstname
FROM employee
WHERE id = :id

Отличительной особенностью Embedded SQL было то, что прекомпилятор проверял каждый оператор SQL и только в том случае, если он был верным, скомпилированный код.
Ниже вы можете увидеть шаги компиляции. (Источник: http://www.redbooks.ibm.com/redbooks/pdfs/sg246435.pdf )

1-Java строка

SQLJ

Когда я впервые встретил Java и JDBC в 2000 году, я был озадачен тем, что ничего подобного не существует. Я узнал, что была инициатива под названием SQLJ, начатая в 1997 году, но она никогда не развивалась. Я понятия не имею, почему, может быть, потому что это было трудно интегрировать для поставщиков IDE и прекомпиляторов, где это не очень распространено для Java. По крайней мере шаги компиляции похожи на Embedded SQL:

2-Java строка

Сравнивая JDBC и SQLJ, мы видим, что нет большой разницы от объема кода, который вы должны написать, но все, что после #sql, является безопасным по типу, потому что прекомпилятор проверяет синтаксис, где, как и в JDBC, есть строка, которая может содержать любая ошибка и ошибка произойдет поздно в производстве.

И тогда я нашел JOOQ!

Десять лет назад Лукас Эдер выпустил первую версию jOOQ . По данным сайта jOOQ «Самый простой способ написать SQL на Java»

Давайте попробуем написать тот же запрос, что и выше, с помощью jOOQ:

1
2
3
4
5
List<EmployeeDTO> records = create
         .select(EMPLOYEE.LASTNAME, EMPLOYEE.FIRSTNAME, EMPLOYEE.SALARY)
         .from(EMPLOYEE)
         .where(EMPLOYEE.SALARY.between(80000, 100000))
         .fetchInto(EmployeeDTO.class);

Довольно круто, не правда ли? Да, но как это работает?

1. Генератор кода

jOOQ использует генератор кода для генерации классов Java из объектов базы данных.

Например, это извлечение класса, сгенерированного jOOQ для таблицы EMPLOYEE:

01
02
03
04
05
06
07
08
09
10
11
public class Employee extends TableImpl<EmployeeRecord> {
 
    public static final Employee EMPLOYEE = new Employee();
 
    public final TableField<EmployeeRecord, Integer> ID = createField("ID", org.jooq.impl.SQLDataType.INTEGER.nullable(false).identity(true), this, "");
    public final TableField<EmployeeRecord, String> LASTNAME = createField("LASTNAME", org.jooq.impl.SQLDataType.VARCHAR(50).nullable(false), this, "");
    public final TableField<EmployeeRecord, String> FIRSTNAME = createField("FIRSTNAME", org.jooq.impl.SQLDataType.VARCHAR(50).nullable(false), this, "");
    public final TableField<EmployeeRecord, Integer> SALARY = createField("SALARY", org.jooq.impl.SQLDataType.INTEGER, this, "");
    public final TableField<EmployeeRecord, Integer> DEPARTMENT_ID = createField("DEPARTMENT_ID", org.jooq.impl.SQLDataType.INTEGER.nullable(false), this, "");
    public final TableField<EmployeeRecord, Integer> MANAGER_ID = createField("MANAGER_ID", org.jooq.impl.SQLDataType.INTEGER, this, "");
}

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

Как настроить генератор и какие форматы ввода возможны для генератора, будет описано в следующем посте. (Оставайтесь в курсе)

2. Домен-специфический язык

Вторая часть jOOQ — это DSL (Domain Specific Language), который позволяет писать SQL-код на Java.
И в отличие от SQL в Strings, DSL заставляет меня писать правильный SQL!

Примеры

Итак, давайте посмотрим еще несколько примеров. Примеры основаны на этой модели данных:

4-Java строка

Вставить

1
2
3
4
dsl.insertInto(DEPARTMENT)
   .columns(DEPARTMENT.NAME)
   .values("HR")
   .execute();

Выбрать

1
2
3
4
dsl.select(DEPARTMENT.NAME)
    .from(DEPARTMENT)
    .where(DEPARTMENT.NAME.eq("IT"))
    .fetchOne();

Обновить

1
2
3
4
dsl.update(DEPARTMENT)
   .set(DEPARTMENT.NAME, "IT2")
   .where(DEPARTMENT.ID.eq(departmentId))
   .execute();

Удалить

1
2
3
dsl.deleteFrom(EMPLOYEE)
   .where(EMPLOYEE.ID.eq(employeeId))
   .execute();

Что дальше?

Это было просто краткое введение. В следующем посте мы более подробно рассмотрим все функции, которые предоставляет jOOQ.

В то же время вы можете проверить код здесь: https://github.com/simasch/jooq-hr

Опубликовано на Java Code Geeks с разрешения Саймона Мартинелли, партнера по нашей программе JCG . Посмотрите оригинальную статью здесь: Введите Safe SQL в Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.