[关闭]
@Rays 2017-02-21T14:53:13.000000Z 字数 6808 阅读 3005

Data Geekery发布了Java ORM工具jOOQ的3.9.0版,用于构建类型安全查询

Java ORM


摘要: Data Geekery公司发布了其Java ORM工具包jOOQ的3.9.0版。jOOQ实现从数据库生成代码,用于类型安全查询。本文给出了jOOQ的入门实例,并通过访谈介绍了jOOQ的主要特性及进一步发展。

作者: Michael Redlich

正文:

Data Geekery公司发布了其Java对象关系映射(ORM,Object-Relational Mapping)工具包jOOQ的3.9.0版。jOOQ首次推出于2010年8月,实现从数据库生成代码,用于类型安全查询。该新版本给出的新特性包括:

开源版本的jOOQ支持下列数据库:

jOOQ的许可页面给出了一系列的许可选项。

入门指南

为初步了解如何构建jOOQ应用,我们考虑使用如下实体关系图去建模一个称为pubs的出版数据库:

我们使用Maven,依赖关系中应包括数据库驱动和jOOQ的代码生成器:

  1. <dependencies>
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <version>6.0.3</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.jooq</groupId>
  9. <artifactId>jooq</artifactId>
  10. <version>3.9.0</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.jooq</groupId>
  14. <artifactId>jooq-meta</artifactId>
  15. <version>3.9.0</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.jooq</groupId>
  19. <artifactId>jooq-codegen</artifactId>
  20. <version>3.9.0</version>
  21. </dependency>
  22. </dependencies>

在配置文件的<profile>部分,定义数据库的相关属性:

  1. <profile>
  2. <id>default</id>
  3. <activation>
  4. <activeByDefault>true</activeByDefault>
  5. </activation>
  6. <properties>
  7. <jdbc.user>root</jdbc.user>
  8. <jdbc.password></jdbc.password>
  9. <jdbc.url>jdbc:mysql://localhost:3306/pubs</jdbc.url>
  10. <jdbc.driver>com.mysql.cj.jdbc.Driver</jdbc.driver>
  11. </properties>
  12. </profile>

下面plugin部分的<generate></generate>标签对内,定义了Maven编译目标、生成和jOOQ代码生成器的属性,其中包括使用的数据库、数据库模式,并定义了jOOQ生成的Java代码包。

  1. <plugin>
  2. <groupId>org.jooq</groupId>
  3. <artifactId>jooq-codegen-maven</artifactId>
  4. <version>3.9.0</version>
  5. <!-- The plugin should hook into the generate goal -->
  6. <executions>
  7. <execution>
  8. <goals>
  9. <goal>generate</goal>
  10. </goals>
  11. </execution>
  12. </executions>
  13. <configuration>
  14. <jdbc>
  15. <driver>${jdbc.driver}</driver>
  16. <url>${jdbc.url}</url>
  17. <user>${jdbc.user}</user>
  18. <password>${jdbc.password}</password>
  19. </jdbc>
  20. <generator>
  21. <database>
  22. <name>org.jooq.util.mysql.MySQLDatabase</name>
  23. <includes>.*</includes>
  24. <excludes></excludes>
  25. <inputSchema>pubs</inputSchema>
  26. </database>
  27. <target>
  28. <packageName>org.redlich.pubs.model</packageName>
  29. <directory>src/main/java</directory>
  30. </target>
  31. </generator>
  32. </configuration>
  33. </plugin>

以下pubs数据库SQL查询示例将被jOOQ建模:

  1. SELECT title,publish_date,authors.last_name,types.type,publishers.publisher
  2. FROM publications
  3. INNER JOIN authors ON authors.id = publications.author_id
  4. INNER JOIN types ON types.id = publications.type_id
  5. INNER JOIN publishers ON publishers.id = publications.publisher_id;

使用jOOQ生成的代码,可以编写实现访问数据库和编写类型安全查询的应用:

  1. public class Application {
  2. public static void main(String[] args) throws Exception {
  3. String user = System.getProperty("jdbc.user");
  4. String password = System.getProperty("jdbc.password");
  5. String url = System.getProperty("jdbc.url");
  6. String driver = System.getProperty("jdbc.driver");
  7. Class.forName(driver).newInstance();
  8. try(Connection connection = DriverManager.getConnection(url,user,password)) {
  9. DSLContext dslContext = DSL.using(connection,SQLDialect.MYSQL);
  10. Result<Record> result = dslContext.select()
  11. .from(PUBLICATIONS)
  12. .join(AUTHORS)
  13. .on(AUTHORS.ID.equal(PUBLICATIONS.AUTHOR_ID))
  14. .join(TYPES)
  15. .on(TYPES.ID.equal(PUBLICATIONS.TYPE_ID))
  16. .join(PUBLISHERS)
  17. .on(PUBLISHERS.ID.equal(PUBLICATIONS.PUBLISHER_ID))
  18. .fetch();
  19. for(Record record : result) {
  20. Long id = record.getValue(PUBLICATIONS.ID);
  21. String title = record.getValue(PUBLICATIONS.TITLE);
  22. Long authorID = record.getValue(PUBLICATIONS.AUTHOR_ID);
  23. String lastName = record.getValue(AUTHORS.LAST_NAME);
  24. String firstName = record.getValue(AUTHORS.FIRST_NAME);
  25. String type = record.getValue(TYPES.TYPE);
  26. String publisher = record.getValue(PUBLISHERS.PUBLISHER);
  27. Date publishDate = record.getValue(PUBLICATIONS.PUBLISH_DATE);
  28. }
  29. }
  30. catch(Exception exception) {
  31. exception.printStackTrace();
  32. }
  33. }
  34. }

注意jOOQ所生成的表名和表的属性名是大写的。完整的jOOQ项目代码可以在GitHub上找到。

Data Geekery GmbH的创始人兼CEO Lukas Eder向InfoQ介绍了最新版的jOOQ。

InfoQ:您当前在Data Geekery的担任什么职责?

Eder:我是推出jOOQ产品的Data Geekery公司的创立人和CEO。我还担任SQL和PL/SQL的顾问,主要针对性能优化。我当前正在开展一些SQL培训项目,今年将写出一本关于SQL的书。

InfoQ:是什么使得jOOQ不同于Hibernate、Speedment和Apache Torque等其他的Java ORM框架的?

Eder:在我个人看来,对比各种框架间的哲学/方法是十分有意思的,将每一个具体的实现看成是一种,嗯……该怎么说呢,实现细节吧。

我认为Java操作RDBMS的方式至多有五类。下面我为每类方法给出几个例子,但实际肯定远远不止这些:

  • 将SQL逻辑从Java中分离处理:例如:MyBatis、厂商特定的存储过程、视图和jOOQ的存储过程等。
  • 将SQL逻辑作为SQL语句字符串嵌入Java中:例如:JDBC、jOOQ的纯SQL、Spring、JDBi、JPA原生查询等。
  • 以SQL为中心的内部领域特定语言(DLS)方式,将SQL逻辑嵌入Java:例如:jOOQ、Criteria API(用于JPQL而非SQL)等。
  • 以“符合语言习惯”的API集合方式,将SQL逻辑嵌入Java:例如:Speedment、JINQ、Slick(Scala编写)、LINQ(.NET编写)等。
  • 通过ORM或活动记录(Active Record),将SQL逻辑嵌入Java:例如:JPA及其实现,包括Hibernate、jOOQ UpdatableRecords、ActiveJDBC等。

上述各类技术方法各有其优劣之处。jOOQ侧重于前三类方法,因为所有这些方法是以SQL为中心的,即在用户与数据库的交互方式中SQL语言是其重要的组成部分。这些用户不认可由“符合语言习惯”的API集合或ORM所强加的认知障碍,他们只是想编写SQL。

提供支持代码生成的流畅API是jOOQ的主要特性。这些API从高层次上提供了编译时对开发人员的类型安全,无需对SQL语言做过多妥协。对此内部DSL技术,我写过一个博客文章:

https://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course

同时,jOOQ鼓励开发人员:

  • 通过视图和存储过程将业务逻辑推送到数据库。视图和存储过程在jOOQ中同样非常易用。
  • 使用高级标准和厂商特定的SQL特性,这在分析和报表时尤为有用。
  • 以RDBMS为中心的方法思考。当你的系统规模增长超过了微不足道的单客户或十数个表这样规模数据库时,这就是种非常好的工作方式了。

当然,有意思的是认识到各种方法之间并非必须要相互竞争。很多jOOQ用户也在同一应用中使用Hibernate,例如:

  • 将jOOQ用于分析、ETL、报表和复杂查询。
  • 在一次事务中更新大量实体时,将Hibernate用于辅助CRUD。

最后一点,作为一种实现方式,jOOQ重视那些寻求精准实现的用户,对于这些用户而言,SQL语言十分重要。并且我也敢于这样说,jOOQ帮助了不少客户理解到,SQL对很多问题而言常是最好的解答,比他们想象的还要好。

InfoQ: 您为我们介绍一下使用jOOQ的友商和客户的情况吧?

Eder: 一般而言,jOOQ有助于友商和客户超越他人并成为更好的开发人员。让我解释一下。

我时常非常有兴趣去了解我们用户基础的多样性。一开始,我们按自己的做事和思考问题方式设计jOOQ。在创建Data Geekery之前,我供职于一个大型瑞士银行的电子银行系统团队,时至今日我依然帮助他们解决Oracle SQL性能问题。很显然,他们也在Oracle上使用jOOQ。

他们的系统中大约有500多张数据表,其中一些表有数十亿行的数据,数以千计的视图,数百个PL/SQL软件包,并伴以大量的Java代码。大量的业务逻辑是在深度嵌套的SQL视图和过程中实现的。当然,这个系统也可以使用一些其他的方式构建,但是我们的以SQL为中心的系统给出了一个非常优雅的设计,的确在Oracle数据库上运行得很好。

并非所有的公司都有他们那样的复杂数据库,区别jOOQ在这些公司中的应用的是jOOQ为Java/SQL开发所带来的简单性和乐趣。你也知道,SQL是一门古老的语言,所有的关键字和有意思的语法让SQL看上去十分奇特(对此我近期有一篇博文,地址是:https://blog.jooq.org/2016/12/09/a-beginners-guide-to-the-true-order-of-sql-operations/)。

声明式编程使用了受关系代数所启发的编程语言,在声明式编程中存在着大美之处。使用jOOQ可以简单并直观地编写SQL语句,这对于开发人员而言确实是一种激励。我听到的最好的事情(从不少的客户那里!)是jOOQ是如何有助于满足他们的好奇心,去探索最宽广、最激动人心的SQL世界。

换句话说,对于分布在各行各业中才华横溢的客户,我们只是充当他们的催化剂。

InfoQ: jOOQ中将会出现哪些特性?

Eder: 我们对与SQL有关的所有特性都有兴趣。当前,我们正对一个内建的SQL解析器进行实验(已经添加在3.9版本中)。这样jOOQ“仅仅”是一个SQL表达式树模型库。使用jOOQ构建SQL语句的用户不再需要构建查询字符串,而是构建一个表达式树,这个树会生成SQL字符串。为什么不反其道而行之,将SQL字符串解析为jOOQ表达式树呢?我们这样做,使得用户可以将一种SQL字符串(例如DB2的)转换为另一种的SQL字符串(例如PostgreSQL的)。这是对于遗留数据库是一种多么好用的迁移工具呀!敬请期待!

jOOQ已有内建的SQL转换SPI,可对我们前面到的表达式树进行转换。这里可能给出包括类似于多租户、行级安全、性能优化以及更多的杀手级特性。这些特性在昂贵的商业数据库中是即可使用的,但是在不少开源数据库中却并非如此。我们已经提供了架构,未来我们将提供这样的即可使用特性。

SQL:2011规范中,最令人激动的特性莫过于可以编写时态查询。假定对于一家保险企业,如果某个特定的政策发生了变化。不同于传统的SQL,你不需要使用UPDATE语句去实际更改数据记录,只需为具有不同时效的同一逻辑策略创建一个新纪录。SQL:2011允许清晰地编写一个时态UPDATE语句,同样也适用于时态DELETE语句的编写。我们正探索在所有的数据库中仿效该特性,包括不支持时态查询的数据库(包括除Oracle和DB2之外的大部分数据库)。

我们还有更多的想法。SQL语言是一个庞大的生态系统,我们拥抱一切与SQL相关的事情。因此敬请期待更为激动人心的jOOQ版本!

资源

更多的jOOQ相关内容,可参考下列文章:

查看英文原文:Data Geekery Releases Version 3.9.0 of jOOQ, a Java ORM Tool for Building Type Safe Queries

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注