Рубрики
Без рубрики

Факты JOOQ: От аннотаций JPA до сопоставлений таблиц JOOQ

Автор оригинала: 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 SingularAttribute product;
    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 List getImageProductDTOs_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 .