JOOQ 的使用 - 拼接 SQL 语句 (SQL Builder)
jOOQ 是基于 JDBC 之上的一个抽象层,提供了多种多样的模型来与关系型数据库进行互操作;其使用与 mybatis 和 Hibernate ORM 不同的思路来实现 对象关系映射 ORM 。
本篇主要介绍基于 jOOQ 的 SQL 语句拼接 (SQL Builder) 。
不同的关系型数据库提供方对 SQL 的支持程度不完全一致,形成了不同的 SQL 方言。不同的 SQL 方言在语法的细节、支持的数据类型等方面存在差别;想要手写可移植的 SQL 简直是难于上青天。
jOOQ 可以用来帮助构建 SQL 语句,忽略不同 SQL 方言的存在。
在 Maven 中使用 jOOQ,需要在 pom.xml
文件中添加以下依赖:
<dependency> <groupId>org.jooq</groupId> <artifactId>jooq</artifactId> <version>${jooq_version}</version> </dependency>
截至 2019-10-21,jOOQ 的最新版本是 3.12.1
。
以下所有的 SQL 生成用例都使用了 JUnit 5 的参数化测试能力,参数化的数据为 6 种不同的关系型数据库方言。
公共代码:
// junit 5 parameterized test data source static Stream<Arguments> dslContexts() { return Stream.of( Arguments.of(SQLDialect.DEFAULT), Arguments.of(SQLDialect.H2), Arguments.of(SQLDialect.HSQLDB), Arguments.of(SQLDialect.POSTGRES), Arguments.of(SQLDialect.MYSQL) ); }
🔗create table
使用 jOOQ 来生成 create table
的 SQL:
@ParameterizedTest @MethodSource("dslContexts") void test_buildCreateTable(final SQLDialect dialect) { final DSLContext dsl = DSL.using(dialect); final String sql = dsl .createTable("user") .column("id", DefaultDataType.getDataType(dialect, Long.class) .identity(true) .nullable(false)) .column("name", DefaultDataType.getDataType(dialect, String.class) .nullable(false) .length(100)) .column("created_at", DefaultDataType.getDataType(dialect, LocalDateTime.class) .nullable(false)) .constraint(DSL.primaryKey("id")) .getSQL(ParamType.INLINED); System.out.println(dialect.getName() + " => " + sql); }
生成的 create table
语句简单对比如下:
-
create table "user"( "id" bigint not null, "name" varchar(100) not null, "created_at" timestamp not null, primary key ("id") )
-
create table "user"( "id" bigint not null auto_increment, "name" varchar(100) not null, "created_at" timestamp not null, primary key ("id") )
-
create table "user"( "id" bigint generated by default as identity(start with 1) not null, "name" varchar(100) not null, "created_at" timestamp not null, primary key ("id") )
-
create table `user`( `id` bigint not null auto_increment, `name` varchar(100) not null, `created_at` timestamp not null, primary key (`id`) )
-
create table "user"( "id" serial8 not null, "name" varchar(100) not null, "created_at" timestamp not null, primary key ("id") )
🔗select
使用 jOOQ 来生成 select
的 SQL:
@ParameterizedTest @MethodSource("dslContexts") void test_buildSelect(final SQLDialect dialect) { final DSLContext dsl = DSL.using(dialect); final String sql = dsl .select(DSL.field("u.id"), DSL.field("u.name"), DSL.field("b.book_name")) .from(DSL.table("user").as("u")) .join(DSL.table("book").as("b")) .on(DSL.field("u.id").eq(DSL.field("b.author_id"))) .where(DSL.field("u.name").eq("Elvis Wang")) .getSQL(ParamType.INLINED); System.out.println(dialect.getName() + " => " + sql); }
生成的 select
语句简单对比如下:
-
select u.id, u.name, b.book_name from user "u" join book "b" on u.id = b.author_id where u.name = 'Elvis Wang'
-
select u.id, u.name, b.book_name from user "u" join book "b" on u.id = b.author_id where u.name = 'Elvis Wang'
-
select u.id, u.name, b.book_name from user as "u" join book as "b" on u.id = b.author_id where u.name = 'Elvis Wang'
-
select u.id, u.name, b.book_name from user as \`u\` join book as \`b\` on u.id = b.author_id where u.name = 'Elvis Wang'
-
select u.id, u.name, b.book_name from user as "u" join book as "b" on u.id = b.author_id where u.name = 'Elvis Wang'
🔗insert
使用 jOOQ 来生成 insert
的 SQL:
@ParameterizedTest @MethodSource("dslContexts") void test_buildInsert(final SQLDialect dialect) { final DSLContext dsl = DSL.using(dialect); final String sql = dsl .insertInto(DSL.table("user")) .columns(DSL.field("name"), DSL.field("created_at")) .values(DSL.value("Elvis Wang"), DSL.value(LocalDateTime.now())) .getSQL(ParamType.INLINED); System.out.println(dialect.getName() + " => " + sql); }
生成的 insert
语句简单对比如下:
-
insert into user (name, created_at) values ( 'Elvis Wang', timestamp '2019-10-17 18:28:29.305' )
-
insert into user (name, created_at) values ( 'Elvis Wang', timestamp '2019-10-17 18:28:29.332' )
-
insert into user (name, created_at) values ( 'Elvis Wang', timestamp '2019-10-17 18:28:29.335' )
-
insert into user (name, created_at) values ( 'Elvis Wang', {ts '2019-10-17 18:28:29.338'} )
-
insert into user (name, created_at) values ( 'Elvis Wang', timestamp '2019-10-17 18:28:29.341' )
🔗update
使用 jOOQ 来生成 update
的 SQL:
@ParameterizedTest @MethodSource("dslContexts") void test_buildUpdate(final SQLDialect dialect) { final DSLContext dsl = DSL.using(dialect); final String sql = dsl .update(DSL.table("user")) .set(DSL.field("name"), "Elvis Wang") .set(DSL.field("created_at"), LocalDateTime.now()) .where(DSL.field("id").eq(1993L)) .getSQL(ParamType.INLINED); System.out.println(dialect.getName() + " => " + sql); }
生成的 update
语句简单对比如下:
-
update user set name = 'Elvis Wang', created_at = timestamp '2019-10-17 19:46:58.42' where id = 1993
-
update user set name = 'Elvis Wang', created_at = timestamp '2019-10-17 19:46:58.456' where id = 1993
-
update user set name = 'Elvis Wang', created_at = timestamp '2019-10-17 19:46:58.46' where id = 1993
-
update user set name = 'Elvis Wang', created_at = {ts '2019-10-17 19:46:58.462'} where id = 1993
-
update user set name = 'Elvis Wang', created_at = timestamp '2019-10-17 19:46:58.465' where id = 1993
🔗delete
使用 jOOQ 来生成 delete
的 SQL:
@ParameterizedTest @MethodSource("dslContexts") void test_buildDelete(final SQLDialect dialect) { final DSLContext dsl = DSL.using(dialect); final String sql = dsl .delete(DSL.table("user")) .where(DSL.field("id").eq(1993L)) .getSQL(ParamType.INLINED); System.out.println(dialect.getName() + " => " + sql); }
生成的 delete
语句简单对比如下:
-
delete from user where id = 1993
-
delete from user where id = 1993
-
delete from user where id = 1993
-
delete from user where id = 1993
-
delete from user where id = 1993
完整的示例代码可以参见 jOOQ Usecases 。
以上。