Автор оригинала: Vlad Mihalcea.
JOOQ -это аккуратная структура, и она решает давнюю проблему, с которой я сталкивался с расширенными динамическими отфильтрованными запросами. В то время как Hibernate и JPA поставляются с полезным API критериев, которым я пользуюсь уже довольно давно, существуют понятные ограничения на то, что вы можете с ними сделать. Например, вы не можете выйти за рамки простых операций SQL (например, ОБЪЕДИНЕНИЯ, ВЛОЖЕННЫЕ ВЫБОРКИ, АГРЕГИРОВАНИЕ) и сделать что-то вроде: оконные функции , определяемые пользователем функции или простая последовательность , чтобы назвать несколько.
ДЖУКУ не хочется соревноваться с Hibernate, но вместо этого я чувствую, что это завершает его. Я использовал Hibernate в основном для ЗАПИСИ части моего слоя данных, отсюда его название или “Сохраняющаяся” часть в JPA. Для простых и средних сложных запросов Hibernate делает все возможное, но мне не нужно полагаться только на него для всех моих запросов, не так ли? Существует также недостаток в запросе свойств, и это связано с тем, что иногда вам приходится добавлять ассоциацию в модель домена только для того, чтобы запросить ее для небольшого числа вариантов использования.
Итак, поскольку я не боюсь писать собственные запросы, я мог бы, следовательно, сделать это в BSL моде и независимо от поставщика.
Хотя вы можете использовать именование столбцов на основе строк, JOOQ предлагает лучший подход за счет использования типобезопасных метаданных, поэтому первое, что нам нужно сделать, это создать сопоставление таблиц для нашей схемы базы данных.
Поскольку у меня уже есть модель JPA, я могу сгенерировать на ее основе DDL-схему базы данных, и для этого мы можем использовать задачу ant-инструмент hibernate.
org.apache.maven.plugins maven-antrun-plugin generate-test-sql-scripts generate-test-sources run org.hibernate hibernate-entitymanager ${hibernate.version} org.slf4j slf4j-api org.hibernate hibernate-tools ${hibernate.tools.version} org.hibernate hibernate-commons-annotations org.slf4j slf4j-api ${slf4j.version} org.slf4j slf4j-simple ${slf4j.version}
Это создаст DDL-скрипт базы данных “create_db.sql”, который мы будем использовать для заполнения временного файла на основе HSQLDB, используя “maven.sql.plugin”. Я бы предпочел HSQLDB в памяти, но, к сожалению, он не сохранял состояние между выполнением плагина.
org.codehaus.mojo sql-maven-plugin org.hsqldb hsqldb ${hsqldb.version} org.hsqldb.jdbc.JDBCDriver jdbc:hsqldb:file:${project.build.directory}/hsqldb/db;shutdown=true sa true hsql-db-test create-test-compile-data generate-test-sources true execute ascending ${project.build.directory}/test-classes/hsqldb/ create_db.sql true
Таким образом, HSQLDB теперь заполнен нашей схемой, сгенерированной JPA, и мы, наконец, можем вызвать генерацию кода JOOQ для построения сопоставления таблиц.
org.jooq jooq-codegen-maven generate-test-sources generate org.hsqldb hsqldb ${hsqldb.version} org.hsqldb.jdbc.JDBCDriver jdbc:hsqldb:file:${project.build.directory}/hsqldb/db sa org.jooq.util.JavaGenerator org.jooq.util.hsqldb.HSQLDBDatabase .* PUBLIC vladmihalcea.jooq.schema ${project.build.directory}/generated-sources/java
Запустив maven, мы получим сгенерированное сопоставление таблиц, поэтому давайте сравним метамодель JPA для класса изображений с соответствующим сопоставлением таблиц JOOQ:
Метамодель JPA выглядит так:
@StaticMetamodel(Image.class) public abstract class Image_ { public static volatile SingularAttributeproduct; public static volatile SingularAttribute id; public static volatile SetAttribute versions; public static volatile SingularAttribute index; public static volatile SingularAttribute name; }
и сопоставление таблиц JOOQ
@javax.annotation.Generated(value = { "http://www.jooq.org", "3.2.0" }, comments = "This class is generated by jOOQ") @java.lang.SuppressWarnings({ "all", "unchecked", "rawtypes" }) public class Image extends org.jooq.impl.TableImpl{ private static final long serialVersionUID = 1596930978; /** * The singleton instance of PUBLIC.IMAGE */ public static final vladmihalcea.jooq.schema.tables.Image IMAGE = new vladmihalcea.jooq.schema.tables.Image(); /** * The class holding records for this type */ @Override public java.lang.Class getRecordType() { return vladmihalcea.jooq.schema.tables.records.ImageRecord.class; } /** * The column PUBLIC.IMAGE.ID. */ public final org.jooq.TableField ID = createField("ID", org.jooq.impl.SQLDataType.BIGINT.nullable(false), this); /** * The column PUBLIC.IMAGE.INDEX. */ public final org.jooq.TableField INDEX = createField("INDEX", org.jooq.impl.SQLDataType.INTEGER, this); /** * The column PUBLIC.IMAGE.NAME. */ public final org.jooq.TableField NAME = createField("NAME", org.jooq.impl.SQLDataType.VARCHAR.length(255), this); /** * The column PUBLIC.IMAGE.PRODUCT_ID. */ public final org.jooq.TableField PRODUCT_ID = createField("PRODUCT_ID", org.jooq.impl.SQLDataType.BIGINT, this); /** * Create a PUBLIC.IMAGE table reference */ public Image() { super("IMAGE", vladmihalcea.jooq.schema.Public.PUBLIC); } /** * Create an aliased PUBLIC.IMAGE table reference */ public Image(java.lang.String alias) { super(alias, vladmihalcea.jooq.schema.Public.PUBLIC, vladmihalcea.jooq.schema.tables.Image.IMAGE); } /** * {@inheritDoc} */ @Override public org.jooq.Identity getIdentity() { return vladmihalcea.jooq.schema.Keys.IDENTITY_IMAGE; } /** * {@inheritDoc} */ @Override public org.jooq.UniqueKey getPrimaryKey() { return vladmihalcea.jooq.schema.Keys.SYS_PK_10059; } /** * {@inheritDoc} */ @Override public java.util.List > getKeys() { return java.util.Arrays. >asList(vladmihalcea.jooq.schema.Keys.SYS_PK_10059, vladmihalcea.jooq.schema.Keys.UK_OQBG3YIU5I1E17SL0FEAWT8PE); } /** * {@inheritDoc} */ @Override public java.util.List > getReferences() { return java.util.Arrays. >asList(vladmihalcea.jooq.schema.Keys.FK_9W522RC4D0KFDKQ390IHV92GB); } /** * {@inheritDoc} */ @Override public vladmihalcea.jooq.schema.tables.Image as(java.lang.String alias) { return new vladmihalcea.jooq.schema.tables.Image(alias); } }
Теперь нам также необходимо ознакомить Maven с нашими недавно созданными классами метаданных JOOQ, чтобы он мог скомпилировать их на следующем этапе тестирования-компиляции.
org.codehaus.mojo build-helper-maven-plugin add-source process-test-sources add-test-source ${project.build.directory}/generated-sources/java
Теперь я могу начать играть с Джуком. Давайте добавим DSLContext в контекст нашего приложения Spring:
И мы напишем тест, чтобы проверить, все ли работает правильно:
private ListgetImageProductDTOs_JOOQ() { return transactionTemplate.execute(new TransactionCallback >() { @Override public List
doInTransaction(TransactionStatus transactionStatus) { return jooqContext .select(IMAGE.NAME, PRODUCT.NAME) .from(IMAGE) .join(PRODUCT).on(IMAGE.PRODUCT_ID.equal(PRODUCT.ID)) .where(PRODUCT.NAME.likeIgnoreCase("%tv%")) .and(IMAGE.INDEX.greaterThan(0)) .orderBy(IMAGE.NAME.asc()) .fetch().into(ImageProductDTO.class); } }); }
Который генерирует следующий SQL
SELECT "PUBLIC"."image"."name", "PUBLIC"."product"."name" FROM "PUBLIC"."image" JOIN "PUBLIC"."product" ON "PUBLIC"."image"."product_id" = "PUBLIC"."product"."id" WHERE ( Lower("PUBLIC"."product"."name") LIKE Lower('%tv%') AND "PUBLIC"."image"."index" > 0 ) ORDER BY "PUBLIC"."image"."name" ASC
Это первый раз, когда я использовал JOOQ, и мне не потребовалось слишком много времени, чтобы просмотреть документацию и настроить все в моем примере кодирования фактов Hibernate. Построение запросов JOOQ кажется естественным, это похоже на написание собственного кода SQL, поэтому мне не нужно на самом деле изучать API, чтобы знать, как его использовать. Я с гордостью добавлю его в свой набор инструментов Java Data Toolbox.
Этот пример кодирования генерирует сопоставления JOOQ в папку тестовых классов, поэтому вы не можете использовать их из исходных файлов main/java. Это можно решить, но для этого требуется рефакторинг существующего решения путем перемещения классов моделей в отдельный модуль Maven. Вы можете создать схему JOOQ в этом отдельном модуле, где перед упаковкой вы переместите классы схемы из тестовых классов в папку классы. Затем вам придется включить этот новый модуль, где вы обычно используете схему JOOQ.
Код доступен на GitHub .