JOQ事实:从JPA注释到JOQ表映射


JOOQ是一个整洁的框架,它解决了我在高级动态过滤查询方面遇到的一个长期问题。虽然Hibernate和JPA附带了一个有用的标准应用编程接口,我已经使用了很长时间了,但是你可以用它们做的事情有一些可以理解的限制。例如,您不能超越简单的SQL操作(例如,连接、嵌套的SLECTS、聚合)而做类似的事情:window functions,user-defined functions或者easy sequencing举几个例子。

JOOQ并不想和Hibernate竞争,但是我觉得它已经完成了。我一直在使用Hibernate作为我的数据层的写部分,因此它的名字或JPA中的“持久化”部分。对于简单到中等复杂的查询,Hibernate做得最好,但是我不需要完全依赖它来完成所有的查询,是吗?查询属性也有一个缺点,那就是有时候你不得不在你的域模型中添加一个关联,仅仅是为了查询少量的用例。

因此,由于我不害怕编写本机查询,所以我可以在DSL时尚和独立于供应商的方式。

虽然您可以使用基于字符串的列命名,但是JOOQ通过使用类型安全的元数据提供了一种更好的方法,所以我们需要做的第一件事就是为我们的数据库模式生成表映射。

因为我已经有了一个JPA模型,我可以从它生成一个数据库模式DDL,为此我们可以使用休眠工具ant任务。

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <id>generate-test-sql-scripts</id> <phase>generate-test-resources</phase> <goals> <goal>run</goal> </goals> <configuration> <tasks> <property name="maven_test_classpath" refid="maven.test.classpath"/> <path id="hibernate_tools_path"> <pathelement path="${maven_test_classpath}"/> </path> <property name="hibernate_tools_classpath" refid="hibernate_tools_path"/> <taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask"/> <mkdir dir="${project.build.directory}/test-classes/hsqldb"/> <hibernatetool destdir="${project.build.directory}/test-classes/hsqldb"> <classpath refid="hibernate_tools_path"/> <jpaconfiguration persistenceunit="testPersistenceUnit" propertyfile="src/test/resources/META-INF/spring/jdbc.properties"/> <hbm2ddl drop="false" create="true" export="false" outputfilename="create_db.sql" delimiter=";" format="true"/> <hbm2ddl drop="true" create="false" export="false" outputfilename="drop_db.sql" delimiter=";" format="true"/> </hibernatetool> </tasks> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-tools</artifactId> <version>${hibernate.tools.version}</version> <exclusions> <exclusion> <groupId>org.hibernate</groupId> <artifactId>hibernate-commons-annotations</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>${slf4j.version}</version> </dependency> </dependencies> </plugin> 

这将生成一个“create_db.sql”数据库DDL脚本,我们将使用“maven.sql.plugin”来填充一个临时的基于文件的HSQLDB。我更喜欢内存中的HSQLDB,但不幸的是它没有保存插件执行之间的状态。

<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>sql-maven-plugin</artifactId> <dependencies> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> </dependency> </dependencies> <configuration> <driver>org.hsqldb.jdbc.JDBCDriver</driver> <url>jdbc:hsqldb:file:${project.build.directory}/hsqldb/db;shutdown=true</url> <username>sa</username> <password></password> <autocommit>true</autocommit> <settingsKey>hsql-db-test</settingsKey> </configuration> <executions> <execution> <id>create-test-compile-data</id> <phase>process-test-resources</phase> <inherited>true</inherited> <goals> <goal>execute</goal> </goals> <configuration> <orderFile>ascending</orderFile> <fileset> <basedir>${project.build.directory}/test-classes/hsqldb/</basedir> <includes> <include>create_db.sql</include> </includes> </fileset> <autocommit>true</autocommit> </configuration> </execution> </executions> </plugin> 

因此,HSQLDB现在填充了我们的JPA生成的模式,我们最终可以调用JOQ代码生成来构建表映射。

<plugin> <groupId>org.jooq</groupId> <artifactId>jooq-codegen-maven</artifactId> <executions> <execution> <phase>process-test-classes</phase> <goals> <goal>generate</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>${hsqldb.version}</version> </dependency> </dependencies> <configuration> <jdbc> <driver>org.hsqldb.jdbc.JDBCDriver</driver> <url>jdbc:hsqldb:file:${project.build.directory}/hsqldb/db</url> <user>sa</user> <password></password> </jdbc> <generator> <name>org.jooq.util.JavaGenerator</name> <database> <name>org.jooq.util.hsqldb.HSQLDBDatabase</name> <includes>.*</includes> <excludes></excludes> <inputSchema>PUBLIC</inputSchema> </database> <generate></generate> <target> <packageName>vladmihalcea.jooq.schema</packageName> <directory>target/generated-sources/jooq</directory> </target> </generator> </configuration> </plugin> 

如果你喜欢阅读这篇文章,你可能想订阅my newsletter并获得折扣my book还有。

Vlad Mihalcea&apos;s Newsletter

通过maven运行,我们得到了生成的表映射,所以让我们将图像类的一个JPA元模型与相关的JOOQ表映射进行比较:

如果你喜欢这篇文章,我打赌你会喜欢的my book还有。






JPA元模型看起来像:

@StaticMetamodel(Image.class) public abstract class Image_ {  public static volatile SingularAttribute<Image, Product> product; public static volatile SingularAttribute<Image, Long> id; public static volatile SetAttribute<Image, Version> versions; public static volatile SingularAttribute<Image, Integer> index; public static volatile SingularAttribute<Image, String> 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<vladmihalcea.jooq.schema.tables.records.ImageRecord> {  private static final long serialVersionUID = 1596930978;  /** * The singleton instance of <code>PUBLIC.IMAGE</code> */ 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<vladmihalcea.jooq.schema.tables.records.ImageRecord> getRecordType() { return vladmihalcea.jooq.schema.tables.records.ImageRecord.class; }  /** * The column <code>PUBLIC.IMAGE.ID</code>.  */ public final org.jooq.TableField<vladmihalcea.jooq.schema.tables.records.ImageRecord, java.lang.Long> ID = createField("ID", org.jooq.impl.SQLDataType.BIGINT.nullable(false), this);  /** * The column <code>PUBLIC.IMAGE.INDEX</code>.  */ public final org.jooq.TableField<vladmihalcea.jooq.schema.tables.records.ImageRecord, java.lang.Integer> INDEX = createField("INDEX", org.jooq.impl.SQLDataType.INTEGER, this);  /** * The column <code>PUBLIC.IMAGE.NAME</code>.  */ public final org.jooq.TableField<vladmihalcea.jooq.schema.tables.records.ImageRecord, java.lang.String> NAME = createField("NAME", org.jooq.impl.SQLDataType.VARCHAR.length(255), this);  /** * The column <code>PUBLIC.IMAGE.PRODUCT_ID</code>.  */ public final org.jooq.TableField<vladmihalcea.jooq.schema.tables.records.ImageRecord, java.lang.Long> PRODUCT_ID = createField("PRODUCT_ID", org.jooq.impl.SQLDataType.BIGINT, this);  /** * Create a <code>PUBLIC.IMAGE</code> table reference */ public Image() { super("IMAGE", vladmihalcea.jooq.schema.Public.PUBLIC); }  /** * Create an aliased <code>PUBLIC.IMAGE</code> 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<vladmihalcea.jooq.schema.tables.records.ImageRecord, java.lang.Long> getIdentity() { return vladmihalcea.jooq.schema.Keys.IDENTITY_IMAGE; }  /** * {@inheritDoc} */ @Override public org.jooq.UniqueKey<vladmihalcea.jooq.schema.tables.records.ImageRecord> getPrimaryKey() { return vladmihalcea.jooq.schema.Keys.SYS_PK_10059; }  /** * {@inheritDoc} */ @Override public java.util.List<org.jooq.UniqueKey<vladmihalcea.jooq.schema.tables.records.ImageRecord>> getKeys() { return java.util.Arrays.<org.jooq.UniqueKey<vladmihalcea.jooq.schema.tables.records.ImageRecord>>asList(vladmihalcea.jooq.schema.Keys.SYS_PK_10059, vladmihalcea.jooq.schema.Keys.UK_OQBG3YIU5I1E17SL0FEAWT8PE); }  /** * {@inheritDoc} */ @Override public java.util.List<org.jooq.ForeignKey<vladmihalcea.jooq.schema.tables.records.ImageRecord, ?>> getReferences() { return java.util.Arrays.<org.jooq.ForeignKey<vladmihalcea.jooq.schema.tables.records.ImageRecord, ?>>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); } } 

代码可在GitHub