JOOQ事実:JPAコメントからJOOQテーブルマッピングへ


JOOQは簡潔なフレームワークであり、高度なダイナミックフィルタクエリーを使用する際に発生する長期的な問題を解決します.HibernateとJPAには有用なCriteria APIが付属していますが(私は長い間使用しています)、それらを使用する際にできることには理解できる制限があります.たとえば、簡単なSQL操作(JOINS、NESTED SLECTS、AGGREGATIONなど)を超えてはいけません.ウィンドウ関数、ユーザー定義関数、簡単なソートなど、似たようなことをします.
JOOQはHibernateと競争したくありませんが、それを達成できると思います.私はずっとHibernateをデータ層のWRITE部分に使用しているので、その名前やJPAの「Persisting」部分を使用しています.単純から中程度の複雑なクエリーでは、Hibernateは最善を尽くしますが、私はそれだけですべてのクエリーを行う必要はありませんよね?クエリー・プロパティにも欠点があります.これは、少数のインスタンスのみをクエリーするために、ドメイン・モデルに関連付けを追加する必要がある場合があります.
したがって、ネイティブ・クエリーの作成は心配しないので、DSL方式とベンダーとは独立した方法で実行できます.
文字列ベースのカラム名を使用することもできますが、JOOQはタイプの安全なメタデータを使用することによってより良い方法を提供しています.そのため、私たちが最初にしなければならないことは、データベース・アーキテクチャのテーブル・マッピングを生成することです.
JPAモデルを持っているので、データベース・モードDDLを生成できます.そのため、hibernatetool antタスクを使用できます.

	org.apache.maven.plugins
	maven-antrun-plugin
	
		
			generate-test-sql-scripts
			generate-test-resources
			
				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}
		
	

これにより、「create_db.sql」データベースDDLスクリプトが生成され、「maven.sql.plugin」を使用してファイルベースの一時HSQLDBを埋め込むことができます.メモリの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
			process-test-resources
			true
			
				execute
			
			
				ascending
				
					${project.build.directory}/test-classes/hsqldb/
					
						create_db.sql
					
				
				true
			
		
	

したがって、HSQLDBは現在、我々のJPAによって生成されたアーキテクチャによって埋め込まれており、最終的にJOOQコード生成を呼び出してテーブルマッピングを構築することができる.

	org.jooq
	jooq-codegen-maven
	
		
			process-test-classes
			
				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
				target/generated-sources/jooq
			
		
	

mavenで実行すると、テーブルマッピングが生成されるので、Imageクラスの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
        
      
    
  

今、JOOQを始めることができます.SpringアプリケーションコンテキストにDSLContextを追加します.

    
    

すべてが正常かどうかを確認するテストを作成します.
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 Facts符号化の例ですべての設定を行いました.JOOQクエリの構築は自然な感じで、ネイティブSQLコードを書くようなものなので、APIの使い方を本当に学ぶ必要はありません.Java Data Toolboxに追加することを誇りに思っています.
このエンコーディング例では、JOOQマッピングをtest-classesフォルダに生成するため、main/javaソースファイルでは使用できません.これは解決できますが、モデルクラスを個別のMavenモジュールに移動することで既存のソリューションを再構築する必要があります.この個別のモジュールでJOOQモードを生成できます.パッケージ化する前に、モードクラスをテストクラスからclassesフォルダに移動できます.次に、この新しいモジュールを含める必要があります.通常、JOOQモードを使用します.
参考:JOOQ事実:JPAコメントから私たちのJCGパートナーVlad MihalceaのVlad MihalceaブログブログのJOOQテーブルマッピングへ.
翻訳:https://www.javacodegeeks.com/2013/12/jooq-facts-from-jpa-annotations-to-jooq-table-mappings.html