Jooq
提出问题
为什么要用(作用)
与JPA相比
到目前为止,只有少数的数据库抽象框架或者库,真正尊重SQL作为语言的一等公民,而包括JPA、EJB、doubts等许多框架都试图隐藏SQL本身,将其范围最小化。
JOOQ填补了这个空白
与LINQ相比
与SQL相比
SQL可以作为纯文本编写并通过JDBC API传递,多年来,人们对这种方式保持警惕
- 没有类型安全
- 没有语法安全
- 没有绑定值索引安全性
- 详细的SQL字符串连接
- 无聊的绑定值索引技术
- JDBC中的详细资源和异常处理
- 一个非常有“状态”的,不是非常面向对象的jdbc api,很难使用
JOOQ前置了解
代码块内容
若未给出假设,则假定使用的是Oracle语法
当看到独立函数,则是从org.jooq.impl.DSL导入的静态函数。
1 | 每当您看到BOOK / Book,AUTHOR / Author和类似实体时,假设它们是从生成的模式中导入的(静态的) |
1 | //每当看到Java代码中使用“create”时,假设这是org.jooq.DSLContext的一个实例。 |
执行
JOOQ无法确定声明是否完整,直到执行fetch或者execute
1 | create.selectOne().fetch(); |
设置
jOOQ允许使用org.jooq.conf.Settings覆盖运行时行为。如果未指定任何内容,则假定使用默认运行时设置。
样本数据库
jOOQ查询示例针对示例数据库运行。
示例数据库
1 | CREATE TABLE language ( |
示例数据
1 | INSERT INTO language (id, cd, description) VALUES (1, 'en', 'English'); |
不同的使用情况进行JOOQ
JOOQ作为SQL构建器
允许为任何数据库构建有效的SQL,使用JOOQ的查询DSL API将字符串、文字和其他用户定义的对象包装到面向对象、类型安全的AST当中,为SQL建模
1 | // Fetch a SQL string from a jOOQ Query in order to manually execute it with another tool. |
之后,该sql语句可以直接使用JDBC进行执行,
JOOQ作为具有代码生成的SQL构建器
JOOQ作为SQL执行器
使用JOOQ直接执行JOOQ生成的SQL语句,通过让JOOQ执行SQL,JOOQ查询DSL成为真正的嵌入式SQL
1 | // Typesafely execute the SQL statement directly with jOOQ |
并且可以使用任何的SQL构建工具构建SQL语句,之后由JOOQ运行SQL语句。
1 | // Use your favourite tool to construct SQL strings: |
JOOQ for CURD
1 | // Fetch an author |
PRO的JOOQ
JOOQ的工具
- jOOQ的执行监听器:jOOQ允许您将自定义执行监听器挂钩到jOOQ的SQL语句执行生命周期中,以集中协调对正在执行的SQL执行的任何操作。用于记录,标识生成,SQL跟踪,性能测量等。
- 记录:jOOQ内置一个标准的DEBUG记录器,用于记录和跟踪所有已执行的SQL语句和获取的结果集
- 存储过程:jOOQ支持您喜欢的数据库的存储过程和函数。生成所有例程和用户定义类型,并且可以作为函数引用包含在jOOQ的SQL构建API中。
- 批量执行:执行大量SQL语句时,批量执行很重要。与JDBC相比,jOOQ简化了这些操作
- 导出和导入:jOOQ附带API,可以轻松导出/导入各种格式的数据
JOOQ教程入门
JOOQ简单7个步骤
准备
JOOQ的maven
1 | <dependency> |
数据库
创建一个library的数据库以及一个对应的author表
1 | CREATE DATABASE `library`; |
代码生成
使用JOOQ的命令行工具生成映射到我们刚创建的Author表的类。详细信息参照:关于设置代码生成器的jOOQ手册页
最简单的方式是将JOOQ的jar文件(3个)以及数据库的connect文件复制到临时目录下,创建一个library.xml
- 更新用户名、url、driver等数据
database.includes
里包含要创建的数据库表,database.excludes
包含需要排除的数据库表generator.target.package
- 将其设置为要为生成的类创建的父包。- 设置
test.generated
将导致test.generated.Author
和test.generated.AuthorRecord
创建 generator.target.directory
- 要输出的目录。
1 |
|
到达临时目录后,windows键入。记得替换文件名称
1 | java -classpath jooq-3.10.8.jar; jooq-meta-3.10.8.jar; jooq-codegen-3.10.8.jar; mysql-connector-java-5.1.18-bin.jar;。 |
mac键入
1 | java -classpath jooq-3.10.8.jar:jooq-meta-3.10.8.jar:jooq-codegen-3.10.8.jar:mysql-connector-java-5.1.18-bin.jar:。 |
成功后的输出
1 | Nov 1, 2011 7:25:06 PM org.jooq.impl.JooqLogger info |
连接到数据库
连接数据库
1 | // For convenience, always static import your generated tables and jOOQ functions to decrease verbosity: |
查询
DSL不会自己关闭,需要我们自己去完成
1 | DSLContext create = DSL.using(conn, SQLDialect.MYSQL); |
迭代
打印结果
1 | for (Record r : result) { |
探索
数据库版本管理工具Flyway
Flyway简介
Flyway是独立于数据库的应用、管理、跟踪数据库变更的数据库版本管理工具。
Flyway的项目主页是:https://flywaydb.org/
为什么使用Flyway
- 不同的开发人员在开发产品特性时,都有可能更新数据库(添加新表,新的约束等)。当开发人员完成工作并提交代码时,代码会被合并到主分支并在测试服务器上执行单元测试与集成测试。我们在哪个环节来执行数据库的更新操作呢?由QA 部门手工执行sql 脚本?或者我们开发一断程序自动执行数据库更新?以什么顺序来执行这些更新脚本?这些问题同样存在于生产环境。
- 我们的产品部署在不同的客户服务器上,以及很多的测试、联调、实验局、销售环境上。不同的客户和测试环境上都部署着不同版本的产品。当他们需要升级他们的产品到新的版本时,我们不仅需要让他们的管理员可以升级产品到新的版本,同时需要保留他们的已有数据。在升级产品的步骤中,我们清楚地知道客户数据库的当前版本,以及需要在该数据库上执行哪些数据库更新脚本,来更新数据库表结构与数据库中已存在的数据。当升级完成时,数据库表结构及数据应当与升级后的产品版本保持一致。
- 当升级失败时(比如在升级过程中出现网络连接失败),我们应当支持对失败进行修复。
更多Flyway文章(参考以下文档)
Maven配置
在pom文件当中定义如下属性,以便在插件配置中进行重用
1 | <properties> |
maven项目配置
依赖
1 | <!-- We'll add the latest version of jOOQ and our JDBC driver - in this case H2 --> |
Flyway插件
1 | <plugin> |
jooq的maven配置
1 | <plugin> |
数据库增量
当我们开始开发我们的数据库时。为此,我们将创建数据库增量脚本,我们将其放入src/main/resources/db/migration
目录中,如之前为Flyway插件配置的那样。我们将添加这些文件:
- V1__initialise_database.sql
- V2__create_author_table.sql
- V3__create_book_table_and_records.sql
这三个脚本模拟我们的模式版本1-3(注意大写V!)。这是脚本的内容
1 | -- V1__initialise_database.sql |
1 | -- V2__create_author_table.sql |
1 | -- V3__create_book_table_and_records.sql |
数据库迁移与代码生成
以上的三个脚本会由Flyway选取并按照版本顺序执行。执行命令
1 | mvn clean install |
之后观察log from flyway
1 | [INFO] --- flyway-maven-plugin:3.0:migrate (default) @ jooq-flyway-example --- |
jooq log
1 | [INFO] --- jooq-codegen-maven:3.10.8:generate (default) @ jooq-flyway-example --- |
发展
每当有人向Maven模块添加新的迁移脚本时,所有前面的步骤都会自动执行。例如,团队成员可能已经提交了一个新的迁移脚本,您可以将其检出,重建并获取最新的jOOQ生成的源,以用于您自己的开发或集成测试数据库。
现在,完成这些步骤后,您可以继续编写数据库查询。想象一下以下测试用例
1 | import org.jooq.Result; |
SQL构建
https://www.jooq.org/doc/3.10/manual/sql-building/
查询DSL类型
JOOQ暴露了许多接口,并隐藏了客户端代码的大多数实现,原因是
- 接口驱动设计,允许最有效地在流畅的API中建模查询
- 降低客户端代码的复杂性
- API保证,只依赖于公开的接口,而不是具体的实现。
1 | import static org.jooq.impl.DSL.*; |
DSL子类
每种SQL方言都有自己的方言专用DSL,如只使用MySQL方言,则可以选择引用MySQLDSL而不是标准DSL
DSLContext类
创建
1 | // Create it from a pre-existing configuration,从预先的配置中创建 |
配置
可以为这些对象提供配置:
- org.jooq.SQLDialect:数据库的方言。这可能是任何当前支持的数据库类型(有关详细信息,请参阅SQL Dialect)
- org.jooq.conf.Settings:可选的运行时配置(有关详细信息,请参阅自定义设置)
- org.jooq.ExecuteListenerProvider:对可以为jOOQ提供执行侦听器的提供程序类的可选引用(有关详细信息,请参阅ExecuteListeners)
- org.jooq.RecordMapperProvider:对可以为jOOQ提供记录映射器的提供者类的可选引用(有关详细信息,请参阅带有RecordMappers的POJO)
- 任何这些:
- java.sql.Connection:可选的JDBC连接,将在配置的整个生命周期中重复使用(有关详细信息,请参阅连接与数据源)。为简单起见,这是本手册中引用的用例,大部分时间都是如此。
- java.sql.DataSource:一个可选的JDBC DataSource,它将在Configuration的整个生命周期中重用。如果您更喜欢在Connections上使用DataSources,jOOQ将在内部从您的DataSource获取新的Connections,在查询执行后方便地再次关闭它们。这在J2EE或Spring上下文中特别有用(有关详细信息,请参阅Connection与DataSource)
- org.jooq.ConnectionProvider:jOOQ用于“获取”和“释放”连接的自定义抽象。jOOQ将在内部“获取”来自ConnectionProvider的新连接,在查询执行后方便地“释放”它们。(有关详细信息,请参阅Connection vs. DataSource)