Как стать автором
Обновить

JOOQ. Введение. Между Сциллой и Харибдой

Уровень сложностиПростой
Время на прочтение8 мин
Количество просмотров3.2K

Я думаю первый вопрос когда речь заходит о JOOQ, что не странно, является - а зачем он нам, собственно говоря, нужен ? В большинстве случаев, и мой случай в том числе, при работе с базой данных легче всего было начать, если мы говорим о Java и Spring Boot, либо c Spring JPA, либо c Spring JDBC Template - это то что прямо из коробки нам доступно - бери и используй. Одно другому, в принципе, не мешает, и мы можем одни задачи решать быстро и удобно с помощью JPA, а другие более сложные и специфичные отдавать на выполнению Spring JDBC Template. И вот в этот гармоничный союз пытается протиснуться библиотека JOOQ.

Почему не JPA ?

Пропустив некоторую ретроспективу, мы найдем в предисловии документации самого JOOQ - Причина существования jOOQ – по сравнению с JPA

До сих пор существовало лишь несколько фреймворков или библиотек абстракции баз данных, которые по-настоящему уважали SQL как первоклассного гражданина среди языков. Большинство фреймворков, включая отраслевые стандарты JPA, EJB, Hibernate, JDO, Criteria Query и многие другие, пытаются скрыть сам SQL, сводя к минимуму его область действия до таких вещей, как JPQL, HQL, JDOQL и различные другие низшие языки запросов.

и следом

jOOQ призван заполнить этот пробел.

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

Да, но если мы будем более конкретными, тот же Hibernate (который реализует JPA стандарт) компенсируют несоответствие между объектно-ориентированным подходом и моделью реляционной базы данных. Предоставляет очень удобные штучки к которым можно очень быстро привыкнуть.

Однако одним из наиболее сложных вариантов дизайна для фреймворка ORM является API для построения корректных и типобезопасных запросов. Тот же HQL (JPQL) лаконичен, но очевидными недостатками этого подхода являются недостаточная безопасность типов и отсутствие статической проверки запросов.

Так же сложные запросы делали разработку невыносимой и на все это как ответ появился, с версии JPA 2.0 - Criteria Query API, но читабельность кода оставляет, мягко говоря, желать лучшего, так что после чтения чужого кода по части данного API, даже "Улисс" Романа Джойса вам покажется детской сказкой...

Вот наглядный пример:

predicates.add(criteriaBuilder.or(new Predicate[] {
    criteriaBuilder.and(
              criteriaBuilder.equal(root.get("firstName"), "Олег"),
              criteriaBuilder.equal(root.get("lastName"), "Петров")
    ),
    criteriaBuilder.and(
              criteriaBuilder.equal(root.get("firstName"), "Мария"),
              criteriaBuilder.equal(root.get("lastName"), "Попова")
    )
}));

И как это могло бы выглядеть в старом добром SQL (ключевое слово - могло бы)

 ... and (first_name, last_name) IN (values('Олег', 'Петров'), values('Мария', 'Попова'))

И тут у нас по справедливости назревает вопрос: а почему бы нам не использовать просто SQL, ну как минимум когда нужно построить сложный запрос, ну посредством того же Spring JDBC Template, к примеру?

И вообще, факт существования HQL или JPQL уже сомнителен, зачем мы уходили от SQL ? С чем боролись на то и напоролись... И это не такой уж популизм я вам скажу!

А что тогда не так с SQL ?

Автор приводит список аргументов в пользу того почему "SQL можно записать в виде обычного текста и передать через JDBC API" - это не ок :

  • Нет типовой безопасности

  • Нет синтаксической безопасности

  • Чрезмерная конкатенация строк SQL

  • Скучные методы индексации значений привязки

  • Чрезмерная обработка ресурсов и исключений в JDBC

  • Не очень объектно-ориентированный JDBC API, который сложно использовать.

Стоит отметить, что главный риск это допустить синтаксическую ошибку и не обнаружить ее на этапе компиляции, но разве хорошо написанные интеграционные тесты не разрешат эту проблему ? Да что там, более того! Возможно такая опасность со стороны простого SQL скрипта будет стимулировать нас как программистов обкладывать наш код тестами более ответственно, а они в свою очередь поверх этого помогут обнаружить куда больше ошибок, которые и сам JOOQ на этапе компиляции не обнаружит ?! Ну по крайне мере, хочется верить, что это нас заставит это делать.

Та же конкатенация при правильном использовании, например, JdbcTemplate может быть в разы менее чрезмерной.

Но все же вернемся к документации в которой автор подводит итог:

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

Что такое JOOQ

jOOQ (Java Object Oriented Querying) генерирует код Java из вашей базы данных и позволяет создавать типобезопасные SQL-запросы с помощью своего гибкого API.

Проблемы от которые JOOQ, по мнению официального сайта, должен нас избавить:

  • База данных в первую очередь. Устали от ORM, управляющих вашей моделью базы данных?

  • Типобезопасный SQL. Надоело обнаруживать синтаксические ошибки SQL в рабочей среде ?

  • Генерация кода. Вам надоело переименовывать имена таблиц и столбцов в коде Java ?

  • Стандартизация. Озадачены тонкими различиями в диалектах SQL ?

  • Жизненный цикл запроса. Раздражает загадочная генерация SQL вашего ORM?

  • Процедуры. Удивленый отсутствием поддержки хранимых процедур в ORM?

Подразумеватся что наш ответ на эти вопросы - да, хотя немного сомнительно, в том плане что на столько ли эти проблемы сами про себе существенны, что бы отказываться от насиженного места. Тут пусть каждый отвечает как полагает.

Но как по мне, вот к примеру, та же проблема переименовывания таблиц и полей - разве сама не является проблемой разработки самой базы, и если такое получилось, то может стоить пересмотреть способ разработки непосредственно базы. Ну ладно, что это я, может я просто не сталкивался с по-настоящему крутыми проектами где эти проблемы сплошь и рядом или наоборот...

Как использовать JOOQ

jOOQ изначально создавался как библиотека для полной абстракции JDBC

  • Типобезопасное обращение к объектам базы данных посредством сгенерированной схемы, таблицы, столбца, записи, процедуры, типа, dao, артефактов pojo

  • Типобезопасное построение SQL / построение SQL с помощью полного запроса DSL API, моделирующего SQL как предметно-ориентированный язык в Java

  • Абстракция диалекта SQL и эмуляция предложений SQL для улучшения совместимости между базами данных и включения недостающих функций в более простых базах данных

По сути, jOOQ изначально был разработан для замены любой другой структуры абстракции базы данных, за исключением тех, которые обрабатывают пул соединений.

Как я говорил выше, мы можем использовать JPA и JDBC вместе для разных целей, так же автор в документации допускает возможность использования того же Hibernate для 70% процентов запросов и сам jOOQ для оставшихся 30%, где он действительно необходим.

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

Генерация кода

Как я уже говорил ранее - генерация исходного кода не является обязательной, но она является одним из основных преимуществ jOOQ, если вы хотите повысить производительность разработки.

Что нам дает генерация кода:

  • Вводите код Java непосредственно в соответствии со схемой базы данных, сохраняя всю доступную информацию о типах.

  • Когда схема вашей базы данных изменится, ваш сгенерированный код также изменится. Удаление столбцов приведет к ошибкам компиляции, которые можно обнаружить заранее.

Код можно генерировать из разных источников:

  • Непросредственно из самой базы. Необходимо подключаться к базе при генерации кода.

  • С помощью DDL на основе скриптов миграции, к примеру Flyway или Liquibase

  • На основе JPA

  • Можно создать собственный генератор.

Подробнее о генерации кода и о каждом из способов будет описано в отдельной статье

DSL API

DSL API - это основной способ создания запросов или их частей в jOOQ
jOOQ поставляется со своим собственным DSL, который эмулирует SQL на Java
Пример из документации:

SELECT *
  FROM author a
  JOIN book b ON a.id = b.author_id
 WHERE a.year_of_birth > 1920
   AND a.first_name = 'Paulo'
 ORDER BY b.title
Result<Record> result =
create.select()
      .from(AUTHOR.as("a"))
      .join(BOOK.as("b")).on(a.ID.eq(b.AUTHOR_ID))
      .where(a.YEAR_OF_BIRTH.gt(1920)
      .and(a.FIRST_NAME.eq("Paulo")))
      .orderBy(b.TITLE)
      .fetch();

Подробнее о синтаксисе, разных запросах и good practices в отдельной статье.

Диалект SQL

Как сказано в документации

Хотя jOOQ пытается максимально полно представить стандарт SQL, многие функции зависят от поставщика конкретной базы данных и ее «диалекта SQL».

То есть в этом плане мы не ограниченны и это здрово. Как пример приводится is distinct from определенный SQL:1999 и реализуемый только H2, HSQLDB и Postgres.

То есть то что у нас в SQL выглядит так:

author.A IS DISTINCT FROM author.B

И JOOQ нас в этом не ограничивает:

AUTHOR.A.isDistinctFrom(AUTHOR.B)

Так же отдельной частью автор останавливается на Oracle SQL диалекте

Oracle SQL гораздо более выразителен, чем многие другие диалекты SQL. Он содержит множество уникальных ключевых слов, предложений и функций, которые не предусмотрены стандартом SQL.
...
jOOQ имеет историческую связь с расширениями Oracle SQL. Если что-то поддерживается в Oracle SQL, то с высокой вероятностью оно попадет в jOOQ API.

QueryDSL

Под конец обзора хотелось бы напомнить что JOOQ не является чем-то исключительным, и если даже вы искали что-то подобное, то стоит так же рассмотреть схожие в некотором роде аналоги и QueryDSL является достойным из них.

Обратимся к документации:

QueryDSL — это платформа, которая позволяет создавать статически типизированные SQL-запросы.

QueryDSL появился из-за необходимости поддерживать запросы HQL типобезопасным способом. Инкрементальное построение запросов HQL требует объединения строк и приводит к трудночитаемому коду. Небезопасные ссылки на типы и свойства домена через простые строки были еще одной проблемой при построении HQL на основе строк.

HQL для Hibernate был первым целевым языком для Querydsl, но в настоящее время он поддерживает в качестве серверных частей JPA, JDO, JDBC, Lucene, Hibernate Search, MongoDB, Collections и RDFBean.

В общем, QueryDSL создан для повышения качества работы JPA (изначально Hibernate) и как бы является альтернативой запросам JPQL и Criteria API и не претендует на большое. То есть он реализует многое чем кичится JOOQ, но в то же время, в некоторых рамках, выполняя точечную задачу. Ведь хоть и в документации JOOQ говорит что его можно использовать в 30% случаях против 70% которые можно, в силу удобства, отдать JPA, но
все равно он претендует на полную доминацию, в то время как QueryDSL именно и создан для тех 30 процентов.

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

Заключение

Это была первая вводная статья о JOOQ, в которой я основываясь на документации бегло пробежался по основным позициям, и так же я планирую поэтапно более подробнее раскрыть некоторые уже упомянутые здесь части позже, а именно:

  • JOOQ. Code generation - особо важная часть библиотеки

  • JOOQ. DSL API - непосредственно то с чем больше всего нам придется работать и на что, лично я, возлагаю большие надежды

  • JOOQ. vs JPA - сравнение по всем параметрам со старым добрым JPA и не только

В общем, я сам пока не полностью осознаю JOOQ, а точнее его необходимость и его место в моей профессиональной деятельности, но данный цикл статей, как итог, будет являться и технической базой для ознакомления с особенностями JOOQ и так же его философским осмыслением и ответом на вопрос - Зачем ?

Прошу в комментариях описать свой опыт работы с библиотекой JOOQ, так как я учту это в последующих статьях.

P.S. Почему трудно понять жука ?

Что если я вам скажу что между жуком и белым движением есть некоторые параллели ?
Стоит описать позицию последних, что бы моя мысль стала яснее.

Так вот, вернем к недавней нашей общей истории и вспомним ту конъюнктуру которая сложилось на момент борьбы белого движения. В идеологическом плане у нас одной стороны левые с их красноречивыми лозунгами и манифестациями - все народу!, или как там Шариков пояснил - "Взять все, да и поделить", а с другой стороны крайне правые взывавшие в душах людей к религиозной основополагающей любви к монарху и к трепету перед пурпурной мантией. И вот чаще всего по среди этих двух таких разных, но очень конкретных сил, и находилась та блеклая позиция белого движения. Почему блеклая, вы спросите, а тут все просто - позиция левых и правых была конкретная, с чётко выраженной позицией, с ясным нарративом, а позиция тех которые понимали и чувства к устоявшемуся за тысячелетия порядку и осознавали необходимость перемен, которые и за народ и за частников, была, как говорится, ни вашим ни нашим. И многие идеи поэтому и проигрывают потому что не могут увлечь толпы этой не очевидной биполярной позицией, на подобии тех идей что имеют ярко выраженные преимущества.

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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
А на какой вы стороне?
33.33% JPA15
48.89% JOOQ22
44.44% SQL20
Проголосовали 45 пользователей. Воздержались 8 пользователей.
Теги:
Хабы:
Всего голосов 4: ↑3 и ↓1+4
Комментарии20

Публикации

Истории

Работа

Java разработчик
296 вакансий

Ближайшие события

28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
2 – 18 декабря
Yandex DataLens Festival 2024
МоскваОнлайн
11 – 13 декабря
Международная конференция по AI/ML «AI Journey»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань