Эта статья является частью нашего академического курса под названием jOOQ — тип безопасного запроса в БД .
jOOQ — хороший выбор в приложении Java, где важны SQL и конкретная реляционная база данных. Это альтернатива, когда JPA / Hibernate слишком много абстрагирует, JDBC слишком мало. Он показывает, как современный предметно-ориентированный язык может значительно повысить производительность труда разработчиков, внедряя SQL в Java.
В этом курсе мы увидим, как мы можем эффективно запрашивать базы данных, используя jOOQ. Проверьте это здесь !
Содержание
1. Введение
Существует множество SPI ( интерфейсов поставщиков услуг ), которые позволяют управлять жизненным циклом различных объектов в jOOQ. Эти SPI могут быть введены через объект Configuration . В этом разделе мы узнаем, как эти объекты управления жизненным циклом взаимодействуют с jOOQ.
Примеры, показанные в этом разделе, также доступны из пакета org.jooq.academy.section4 .
2. ConnectionProvider
Самый важный SPI — это тот, который предоставляет jOOQ соединение JDBC. До сих пор в примерах Connection передавался непосредственно DSL.using() :
|
1
|
DSLContext dsl = DSL.using(connection); |
Эта нотация является просто удобством для более подробного варианта, где connection заключено в DefaultConnectionProvider :
|
1
|
DSLContext dsl = DSL.using(new DefaultConfiguration().set(new DefaultConnectionProvider(connection))); |
В более сложных настройках, чем в этом руководстве, вы можете вместо этого предоставить jOOQ источник данных, например, при использовании пула Connection или даже при использовании распределенных транзакций через JTA. Популярные DataSources использования DataSources поддерживаются нативно, в том числе с помощью удобных методов (хотя вам необходимо предоставить SQLDialect , поскольку его нельзя получить из DataSource
|
1
|
DSLContext dsl = DSL.using(dataSource, SQLDialect.H2); |
Если вы хотите реализовать любой другой тип источника JDBC- Connection , вы можете реализовать свой собственный ConnectionProvider
|
1
2
3
4
5
6
7
|
public interface ConnectionProvider { // jOOQ will acquire a connection through this method prior to query execution Connection acquire() throws DataAccessException; // jOOQ will release previously acquired connections again through this method after query execution void release(Connection connection) throws DataAccessException;} |
3. SQLDialect
jOOQ сгенерирует и выполнит ваш оператор SQL в контексте конкретного SQLDialect. Это может быть лучше всего проиллюстрировано на примере при запуске следующей программы:
|
1
2
3
4
|
// This renders SELECT 1 / SELECT 1 FROM DUAL in various SQL dialect familiesArrays.stream(SQLDialect.families()) .map(family -> String.format("%9s : ", family) + DSL.using(family).render(DSL.selectOne())) .forEach(System.out::println); |
Когда вы выполняете вышеуказанную программу, вы можете получить что-то вроде следующего:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
ACCESS : select 1 as [one] from (select count(*) dual from MSysResources) as dual ASE : select 1 [one] CUBRID : select 1 "one" from "db_root" DB2 : select 1 "one" from "SYSIBM"."DUAL" DERBY : select 1 as "one" from "SYSIBM"."SYSDUMMY1" FIREBIRD : select 1 "one" from "RDB$DATABASE" H2 : select 1 "one" from dual INFORMIX : select 1 "one" from (select 1 as dual from systables where tabid = 1) as dual INGRES : select 1 "one" from (select 1 as dual) as dual HSQLDB : select 1 as "one" from "INFORMATION_SCHEMA"."SYSTEM_USERS" MARIADB : select 1 as `one` from dual MYSQL : select 1 as `one` from dual ORACLE : select 1 "one" from dual POSTGRES : select 1 as "one" SQLITE : select 1 oneSQLSERVER : select 1 [one] SYBASE : select 1 [one] from [SYS].[DUMMY] |
Все операторы были сгенерированы из одного и того же выражения DSL DSL.selectOne() . В большинстве случаев вам не нужно беспокоиться о небольших различиях между различными диалектами SQL, поскольку jOOQ будет абстрагировать их от вас в одном API.
Поддерживается ли какой-либо данный элемент API jOOQ вашим SQLDialect можно увидеть из аннотации @Support для большинства методов DSL. Возьмите метод DSL.denseRank() , который моделирует оконную функцию DENSE_RANK() . Он объявлен в API jOOQ как таковой:
|
1
2
|
@Support({ CUBRID, DB2, INFORMIX, POSTGRES, ORACLE, SQLSERVER, SYBASE })public static WindowOverStep<Integer> rank() { ... } |
4. Настройки
Настройки используются для предоставления jOOQ информации об общем отображении запросов и поведении их выполнения. Они регулируются XSD, который доступен здесь: http://www.jooq.org/xsd/jooq-runtime-3.3.0.xsd (проверьте последние руководства или веб-сайт на наличие потенциальных обновлений)
В текущей версии настройки jOOQ содержат флаги для управления…
- Должны ли таблицы полностью соответствовать схеме
- Должны ли таблицы и схемы быть переведены / отображены (например, для реализации мультитенантности)
- Следует ли заключать в кавычки имена схем, таблиц и столбцов (например, для поддержки чувствительных к регистру имен)
- Должны ли генерируемые ключевые слова SQL быть прописными или строчными
- Нужно ли форматировать сгенерированный SQL (например, для ведения журнала отладки)
- Должны ли значения привязки отображаться в виде вопросительных знаков, именованных параметров или встроенных
- Должны ли быть выполнены статические или подготовленные операторы
- Активна ли регистрация выполнения
- Активна ли оптимистическая блокировка
- Должны ли активные записи сохранять ссылку на
Configurationкоторая их произвела - Есть ли у активных записей обновляемые первичные ключи
- Следует ли кэшировать информацию об отражении
5. ExecuteListeners
ExecuteListener — это один из пары SPI ( Service Provider Interface), который вы можете использовать, чтобы подключиться к высокому уровню рендеринга запросов jOOQ, привязки переменных и жизненного цикла выполнения. В следующем примере показан простой способ измерения времени выполнения запроса для каждого запроса.
5.1. Пример ExecuteListener
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
ExecuteListener listener = new DefaultExecuteListener() { @Override public void start(ExecuteContext ctx) { // Register the start time to the current context ctx.data("time", System.nanoTime()); } @Override public void end(ExecuteContext ctx) { // Extract the start time from the current context Long time = (Long) ctx.data("time"); System.out.println("Execution time : " + ((System.nanoTime() - time) / 1000 / 1000.0) + "ms. Query : " + ctx.sql()); }}; |
Этот слушатель может затем использоваться в Configuration следующим образом:
|
1
2
3
4
5
6
7
8
|
DSL.using(new DefaultConfiguration() .set(SQLDialect.H2) .set(new DefaultConnectionProvider(connection)) .set(new DefaultExecuteListenerProvider(listener)) ) .select(AUTHOR.ID) .from(AUTHOR) .fetch(); |
Вызов fetch() теперь будет инициировать весь жизненный цикл выполнения запроса, включая реализованные обратные вызовы start() и end() . Это приведет к следующему выводу на консоль:
|
1
|
Execution time : 0.101ms. Query : select "PUBLIC"."AUTHOR"."ID" from "PUBLIC"."AUTHOR" |
Существуют и другие SPI для конкретных случаев использования. Подробнее о них см. В руководстве по jOOQ .