1. JOOQ简介
JOOQ,(Java Object Oriented Querying)Java面向对象查询,是一种对象关系映射(Object Relational Mapping)的处理数据库的框架。它是一套基于Java访问关系型数据库的工具包,能够将SQL语言集成到Java中,它具有轻量、简单、并且足够灵活的特点,通过JOOQ我们可以轻松的使用Java面向对象的语法来实现各种复杂的SQL。但它并非完全免费,只是对MySql之类的开源数据库免费。
2. 入门实践
2.1 准备工作
- MySQL 5.6 或 更高
- JDK 1.8
- jOOQ - 3.13.1
- Maven 3.6.0
2.2 初始化数据库脚本
这里我们创建3张表,作者表:author,里面存放金古梁温武侠四大宗师;作品表:book,存放几位作家的经典作品;角色表:role,存放各个作品中出现的主要人物。
CREATE TABLE `author` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(50) NOT NULL DEFAULT '',
`age` int NOT NULL DEFAULT '0',
`birthday` date NULL DEFAULT '1900-01-01',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='作者';
insert into `author`(`id`,`name`,`age`,`birthday`) values
(1,'金庸',94,'1924-03-10'),
(2,'古龙',48,'1938-06-07'),
(3,'梁羽生',85,'1924-03-22'),
(4,'温瑞安',67,'1954-01-01');
CREATE TABLE `book` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`author_id` int NOT NULL COMMENT '作者ID',
`book_name` varchar(50) NOT NULL DEFAULT '',
`publish_year` YEAR NOT NULL DEFAULT '0000'COMMENT '出版年份',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='作品';
insert into `book`(`id`,`author_id`,`book_name`,`publish_year`) values
(1,1,'神雕侠侣','1959'),
(2,1,'天龙八部','1963'),
(3,1,'笑傲江湖','1967'),
(4,1,'射雕英雄传','1957'),
(5,2,'绝代双骄','1966'),
(6,2,'楚留香传奇','1967'),
(7,2,'陆小凤传奇','1973'),
(8,2,'天涯明月刀','1974'),
(9,3,'七剑下天山','1956'),
(10,3,'白发魔女传','1957'),
(11,4,'四大名捕','1984');
CREATE TABLE `role` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`book_id` int NOT NULL COMMENT '作品ID',
`role_name` varchar(10) NOT NULL DEFAULT '',
`skill` varchar(50) NOT NULL DEFAULT '' COMMENT '绝技',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色';
insert into `role`(`id`,`book_id`,`role_name`,`skill`) values
(1,1,'杨过','黯然销魂掌'),
(2,2,'乔峰','降龙十八掌'),
(3,3,'令狐冲','独孤九剑'),
(4,4,'郭靖','降龙十八掌'),
(5,5,'花无缺','移花接玉'),
(6,6,'楚留香','弹指神功'),
(7,7,'陆小凤','灵犀一指'),
(8,8,'傅红雪','白家神刀'),
(9,9,'楚昭南','追风剑法'),
(10,10,'练霓裳','反天山剑法'),
(11,11,'冷凌弃','四十九路无名快剑');
2.3 Maven配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>JOOQLearning</groupId>
<artifactId>JOOQLearning</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<jooq.version>3.13.1</jooq.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>${jooq.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 代码生成器插件 -->
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>${jooq.version}</version>
<configuration>
<jdbc>
<driver>com.mysql.cj.jdbc.Driver</driver>
<url>jdbc:mysql://127.0.0.1:3306/learn-jooq?serverTimezone=GMT%2B8</url>
<user>root</user>
<password>live1234</password>
</jdbc>
<generator>
<database>
<!--需要生代码的数据表,如果需要指定具体的表:(author)|(book)-->
<includes>.*</includes>
<inputSchema>learn-jooq</inputSchema>
</database>
<target>
<!--指明生成代码所在的包路径-->
<packageName>com.vip.jooq.learn.codegen</packageName>
<!--需写成绝对路径-->
<directory>/Users/live-ljt/Documents/git/JOOQLearning/src/main/java</directory>
</target>
</generator>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.4 代码生成
代码生成的原理就是通过读取数据库的元数据,将其转换为Java代码,并生成指定的文件,存放到配置好的指定目录。
# 通过此命令里可以调用 jooq-codegen-maven 插件进行代码生成
mvn jooq-codegen:generate
需要注意的是,本人在mac上实践发现,生成的目标文件夹位置必须写成绝对路径,如果写成"src/main/java",生成代码失败。
生成的代码目录结构如下:
├─src/main/java/.../codegen ---- // 生成路径
│ ├─tables --------------------- // 表定义目录
│ │ ├─Author ------------------- // Author 表描述包含: 字段,主键,索引,所属Schema
│ │ ├─Book ------------------- // Book 表描述
│ │ ├─Role ------------------- // Role 表描述
│ │ └─records ------------------ // 表操作对象目录
│ │ └─AuthorRecord ----------- // AuthorRecord 表操作对象,包含字段get,set方法
│ │ └─BookRecord ------------- // BookRecord 表操作对象,包含字段get,set方法
│ │ └─RoleRecord ------------- // RoleRecord 表操作对象,包含字段get,set方法
│ ├─DefaultCatalog ------------- // Catalog对象,包含Schema常量
│ ├─Keys ----------------------- // 当前数据库所有表主键,唯一索引等常量
│ ├─LearnJooq ------------------ // 数据库`learn-jooq`常量,包含该库所有表描述常量
│ └─Tables --------------------- // 所有数据库表常量
如果需要指定特定的几个表,可以按 author|book|role 这种方式写到<includes>标签内。
2.5 多数据源代码生成
多数据源的情况下,由于每个数据库的连接等配置不同,所以针对每个数据库实例都需要有一套配置。 利用 maven 插件的 executions 选项,可以同时配置多个执行器,另外由于配置过多,不再将这些配置放在 pom.xml 文件,可以把每一个数据库的配置抽离出来,作为单独的文件。
比如创建文件:src/main/resources/codegen-config1.xml
<configuration>
<jdbc>
<driver>com.mysql.cj.jdbc.Driver</driver>
<url>jdbc:mysql://127.0.0.1:3306/learn-jooq?serverTimezone=GMT%2B8</url>
<user>root</user>
<password>live1234</password>
</jdbc>
<generator>
<database>
<includes>.*</includes>
<inputSchema>learn-jooq</inputSchema>
</database>
<target>
<packageName>com.vip.jooq.learn.codegen</packageName>
<directory>/Users/live-ljt/Documents/git/JOOQLearning/src/main/java</directory>
</target>
</generator>
</configuration>
然后pom文件修改如下:
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>${jooq.version}</version>
<executions>
<execution>
<id>learn-jooq</id>
<configuration>
<configurationFile>src/main/resources/codegen-config1.xml</configurationFile>
</configuration>
</execution>
<!-- <execution> -->
<!-- ... -->
<!-- </execution>-->
</executions>
</plugin>
通过命令mvn jooq-codegen:generate@learn-jooq 执行即可生成响应的代码,需注意的是命令后需指定对应配置的ID。
至此,所有准备工作都已完成,下面开始利用JOOQ执行增删改查的操作。
2.6 基础的CRUD
首先介绍JOOQ的一个核心接口:org.jooq.DSLContext,它可以理解为一个SQL执行器,通过静态方法 DSL.using,能够获取一个 DSLContext 实例,此实例抽象了所有对于SQL的操作API,可以通过其提供的API方便的进行SQL操作。
我们先简单写个获取DSLContext实例的工具类,这里直接采用JDBC的方式连接,翻看源码也可以通过连接池获取实例,后文会再做尝试。
public class DSLContextUtil {
private static final String userName = "root";
private static final String password = "live1234";
private static final String url = "jdbc:mysql://localhost:3306/learn-jooq";
public static DSLContext getDSLContextUtils() throws SQLException {
Connection conn = DriverManager.getConnection(url, userName, password);
return DSL.using(conn, SQLDialect.MYSQL);
}
}
2.6.1 SELECT查询
基本查询方法,默认查询指定表的所有字段,返回一个结果集的包装,通过Result.into方法,可以将结果集转换为任意指定类型集合,比如可以转换成我们自定义的POJO,另外也可以通过 Record.getValue 方法取得任意字段值,值类型依赖于字段类型。
DSLContext dslContext = DSLContextUtil.getDSLContextUtils();
Result<Record> fetchResult = dslContext.select().from(AUTHOR).fetch();
System.out.println(fetchResult);
//Result.into方法实现类型转换
List<AuthorRecord> authorRecords = fetchResult.into(AuthorRecord.class);
System.out.println(authorRecords);
Result<Record> fetchAll = dslContext.select().from(AUTHOR)
.where(AUTHOR.ID.in(1, 2)).fetch();
fetchAll.forEach(record -> {
//Record.getValue方法取得字段值
Integer id = record.getValue(AUTHOR.ID);
String name = record.getValue(AUTHOR.NAME);
int age = record.getValue(AUTHOR.AGE);
LocalDate birthday = record.getValue(AUTHOR.BIRTHDAY);
System.out.println("id:"+id+",name:"+name+",age:"+age+",birthday:"+birthday);
});
JOOQ也可直接实现多表关联查询,和写SQL的方法类似,关联查询出来的结果集,可以自定义一个POJO来储存数据。
@Data
public class AuthorBook {
private String name;
private LocalDate birthday;
private String bookName;
private LocalDate publishYear;
}
下面关联author和book两张表查询对应POJO类的指定字段,并且采用limit分页处理:
DSLContext dslContext = DSLContextUtil.getDSLContextUtils();
Result<Record4<String, LocalDate, String, LocalDate>> record4Result =
dslContext.select(AUTHOR.NAME,AUTHOR.BIRTHDAY,BOOK.BOOK_NAME,BOOK.PUBLISH_YEAR)
.from(AUTHOR)
.leftJoin(BOOK).on(AUTHOR.ID.eq(BOOK.AUTHOR_ID))
.limit(1,5)
.fetch();
List<AuthorBook> authorBooks = record4Result.into(AuthorBook.class);
2.6.2 Insert插入
JOOQ的数据操作通常有两种方式, 第一种是使用 DSLContext API 以类SQL的语法进行调用,第二种是利用 Record API 进行调用。另外批量插入的方法是DSLContext.batchInsert(Collection<? extends TableRecord<?>> records).
DSLContext dslContext = DSLContextUtil.getDSLContextUtils();
// 类SQL语法 insertInto 方法第一个参数通常是表常量,同时获取插入数据的ID
Integer id = dslContext.insertInto(ROLE, ROLE.BOOK_ID, ROLE.ROLE_NAME, ROLE.SKILL)
.values(7, "西门吹雪", "一剑西来")
.returning(ROLE.ID)
.fetchOne().getId();
// newRecord() 方法标识添加一条记录,通过链式调用,支持批量插入
dslContext.insertInto(ROLE)
.set(ROLE.BOOK_ID, 2).set(ROLE.ROLE_NAME, "虚竹").set(ROLE.SKILL, "北冥神功")
.newRecord()
.set(ROLE.BOOK_ID, 2).set(ROLE.ROLE_NAME, "段誉").set(ROLE.SKILL, "六脉神剑")
.execute();
//dslContext.newRecord 方法根据表来创建一个Record对象,可以通过 record.insert() 方法插入数据
RoleRecord record = dslContext.newRecord(ROLE);
record.setBookId(7);
record.setRoleName("叶孤城");
record.setSkill("天外飞仙");
record.insert();
List<BookRecord> recordList = IntStream.range(2, 6).mapToObj(i -> {
BookRecord book = new BookRecord();
book.setAuthorId(2);
book.setBookName("陆小凤传奇"+i);
return book;
}).collect(Collectors.toList());
dslContext.batchInsert(recordList).execute();
插入时如果主键重复,可做两种操作,一种忽略插入、一种是更新操作。
DSLContext dslContext = DSLContextUtil.getDSLContextUtils();
// 返回affecteRow影响行数,如果id重复则忽略
int affecteRow = dslContext.insertInto(ROLE, ROLE.ID, ROLE.BOOK_ID, ROLE.ROLE_NAME, ROLE.SKILL)
.values(18, 7 ,"叶孤城", "天外飞仙")
.onDuplicateKeyIgnore()
.execute();
System.out.println(affecteRow);
//主键重复则执行更新操作
dslContext.insertInto(ROLE)
.set(ROLE.ID, 14)
.set(ROLE.BOOK_ID, 2)
.set(ROLE.SKILL, "北冥神功")
.onDuplicateKeyUpdate()
.set(ROLE.BOOK_ID, 2)
.set(ROLE.SKILL, "小无相功")
.execute();
2.6.3 Update修改
update和insert的用法类似,都是有两种方式进行操作,同时支持批量修改DSLContext.batchUpdate(Collection<? extends TableRecord<?>> records).execute();
talk is cheap, show you the code.
DSLContext dslContext = DSLContextUtil.getDSLContextUtils();
dslContext.update(ROLE)
.set(ROLE.BOOK_ID, 2)
.set(ROLE.ROLE_NAME, "乔峰")
.set(ROLE.SKILL, "擒龙功")
.where(ROLE.ID.eq(2))
.execute();
//Record 方式默认通过主键进行作为update语句的where条件
RoleRecord record = dslContext.newRecord(ROLE);
record.setId(1);
record.setRoleName("西狂杨过");
record.setSkill("九阴真经");
record.update();
2.6.4 Delete删除
DSLContext dslContext = DSLContextUtil.getDSLContextUtils();
dslContext.delete(BOOK).where(BOOK.ID.eq(12)).execute();
BookRecord record = dslContext.newRecord(BOOK);
record.setId(13);
int deleteRows = record.delete();//根据主键删除
System.out.println(deleteRows);
BookRecord record1 = new BookRecord();
record1.setId(14);
BookRecord record2 = new BookRecord();
record2.setId(15);
dslContext.batchDelete(record1, record2).execute();
List<BookRecord> recordList = new ArrayList<>();
recordList.add(record1);
recordList.add(record2);
dslContext.batchDelete(recordList).execute();//批量删除
到此,JOOQ最基本的增删改查的方法已经介绍完毕,但上面操作的对象基本是基于*Record类,在实际业务开发过程中并不方便,我们需要转换成具备业务价值的POJO类,JOOQ也提供了方法让我去扩展生成POJO类,以及生成DAO层代码的方法,下面就简单介绍JOOQ自定义代码生成。
2.6 自定义代码生成
首先在pom文件中添加依赖
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen</artifactId>
<version>${jooq.version}</version>
</dependency>
在生成代码的配置文件中可直接添加如下配置,就能在pojos目录下生成对应的POJO类。
<generator>
<generate>
<pojos>true</pojos>
</generate>
<!-- ... -->
</generator>
但是会发现pojos目录下的所有POJO类名和 tables 目录下所有表描述对象的类名完全一样,这样在引用的时候需要关注包的路径,容易引起误解。jOOQ在代码生成的时候,是通过 org.jooq.codegen.GeneratorStrategy 接口,来确定所有文件名称的生成规则,我们可以通过自定义代码生成器CustomGeneratorStrategy 继承原有的 DefaultGeneratorStrategy,重写getJavaClassName 方法,达到区别出 POJO 和表描述类名的目的。
public class CustomGeneratorStrategy extends DefaultGeneratorStrategy {
@Override
public String getJavaClassName(Definition definition, Mode mode) {
String result = super.getJavaClassName(definition, mode);
switch (mode) {
case POJO:
result +="Pojo";
break;
case DEFAULT:
if (definition instanceof TableDefinition) {
result = "T" + result;
}
break;
default:
break;
}
return result;
}
}
在代码生成的配置文件中添加如下内容:
<generator>
<strategy>
<name>com.vip.jooq.learn.config.CustomGeneratorStrategy</name>
</strategy>
</generator>
此时直接执行mvn jooq-codegen:generate@learn-jooq,发现执行报错,找不到我们的自定义类CustomGeneratorStrategy,重新build可以解决问题。代码重新生成后可以看到pojo与table描述类已经区分开来。
主流的ORM框架,都可以通过反向工程来生成和表一一对应的DAO,jOOQ在代码生成器生成的时候,可以通过配置 daos 选项,来生成DAO类。另外在jOOQ在代码生成时,我们也可以通过<interfaces>true</interfaces>配置,来为POJO和Record生成一个接口与表一一对应的接口,接口定义了getter/setter方法,并且定义了接口类型为参数的 from/into 方法。通过此配置,相同表的POJO和Record之间互相转换的性能会有所提升。
<configuration>
<!-- ... -->
<generator>
<generate>
<pojos>true</pojos>
<interfaces>true</interfaces>
<daos>true</daos>
</generate>
<!-- ... -->
</generator>
</configuration>
生成的DAO接口,为POJO提供了一系列的通用操作方法。
public class AuthorDao extends DAOImpl<AuthorRecord, AuthorPojo, Integer> {
/**
* Create a new AuthorDao without any configuration
*/
public AuthorDao() {
super(TAuthor.AUTHOR, AuthorPojo.class);
}
/**
* Create a new AuthorDao with an attached configuration
*/
public AuthorDao(Configuration configuration) {
super(TAuthor.AUTHOR, AuthorPojo.class, configuration);
}
@Override
public Integer getId(AuthorPojo object) {
return object.getId();
}
public List<AuthorPojo> fetchRangeOfId(Integer lowerInclusive, Integer upperInclusive) {
return fetchRange(TAuthor.AUTHOR.ID, lowerInclusive, upperInclusive);
}
public List<AuthorPojo> fetchById(Integer... values) {
return fetch(TAuthor.AUTHOR.ID, values);
}
public AuthorPojo fetchOneById(Integer value) {
return fetchOne(TAuthor.AUTHOR.ID, value);
}
public List<AuthorPojo> fetchRangeOfName(String lowerInclusive, String upperInclusive) {
return fetchRange(TAuthor.AUTHOR.NAME, lowerInclusive, upperInclusive);
}
public List<AuthorPojo> fetchByName(String... values) {
return fetch(TAuthor.AUTHOR.NAME, values);
}
public List<AuthorPojo> fetchRangeOfAge(Integer lowerInclusive, Integer upperInclusive) {
return fetchRange(TAuthor.AUTHOR.AGE, lowerInclusive, upperInclusive);
}
public List<AuthorPojo> fetchByAge(Integer... values) {
return fetch(TAuthor.AUTHOR.AGE, values);
}
public List<AuthorPojo> fetchRangeOfBirthday(LocalDate lowerInclusive, LocalDate upperInclusive) {
return fetchRange(TAuthor.AUTHOR.BIRTHDAY, lowerInclusive, upperInclusive);
}
public List<AuthorPojo> fetchByBirthday(LocalDate... values) {
return fetch(TAuthor.AUTHOR.BIRTHDAY, values);
}
}
JOOQ的基本操作已经介绍完毕,下面介绍下如何在SpringBoot中使用JOOQ.
3.SpringBoot整合JOOQ
在原来依赖的基础上,我们需要添加SpringBoot的相关依赖,以及数据源依赖,这里我们使用性能较优的HikariCP,完整的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>JOOQLearning</groupId>
<artifactId>JOOQLearning</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<jooq.version>3.13.1</jooq.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- base jooq dependency -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>${jooq.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen</artifactId>
<version>${jooq.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jooq</artifactId>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 代码生成器插件 -->
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<version>${jooq.version}</version>
<executions>
<execution>
<id>learn-jooq</id>
<configuration>
<configurationFile>src/main/resources/codegen-config-jooq-learn.xml</configurationFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
将数据库的相关配置放到资源文件目录下的 jdbc.properties 文件内
datasource.jdbc.url=jdbc:mysql://localhost:3306/learn-jooq?serverTimezone=GMT%2B8
datasource.jdbc.username=root
datasource.jdbc.password=live1234
在代码生成的配置文件中添加如下配置,代码生成会自动为DAO类添加 @Repository 注解将其声明为SpringBean。并且会自动在有参构造器 XxxxDao(Configuration configuration) 上添加 @Autowried 注解。
<generate>
<pojos>true</pojos>
<daos>true</daos>
<interfaces>true</interfaces>
<springAnnotations>true</springAnnotations>
</generate>
生成的Dao类
@Repository
public class AuthorDao extends DAOImpl<AuthorRecord, AuthorPojo, Integer> {
public AuthorDao() {
super(TAuthor.AUTHOR, AuthorPojo.class);
}
@Autowired
public AuthorDao(Configuration configuration) {
super(TAuthor.AUTHOR, AuthorPojo.class, configuration);
}
...
}
接下来是配置数据源和事务管理器,并且启用注解式事务管理。
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig {
@Value("${datasource.jdbc.url}")
private String url;
@Value("${datasource.jdbc.username}")
private String username;
@Value("${datasource.jdbc.password}")
private String password;
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
return new TransactionAwareDataSourceProxy(new HikariDataSource(config));
}
}
DAO的初始化,需要有个 Configuration 实例,因此我们需要声明一些jOOQ相关的Bean,除了Configuration实例外,经常使用 DSLContext 也可以声明为SpringBean。
@Configuration
@Import(DataSourceConfig.class)
public class JooqConfiguration {
@Bean
public DSLContext dslContext(org.jooq.Configuration configuration) {
return DSL.using(configuration);
}
@Bean
public org.jooq.Configuration configuration(DataSource dataSource) {
org.jooq.Configuration configuration = new DefaultConfiguration();
configuration.set(SQLDialect.MYSQL);
configuration.set(dataSource);
return configuration;
}
}
最后我们写个Service类来测试下最终效果,输出结果如下:
@ComponentScan
public class BookServiceTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(BookServiceTest.class);
BookService bean = context.getBean(BookServiceImpl.class);
BookPojo bookPojo = bean.getBookPojoById(1);
System.out.println(bookPojo);
}
}
23:03:43.844 [main] DEBUG org.jooq.tools.LoggerListener - Executing query : select `learn-jooq`.`book`.`id`, `learn-jooq`.`book`.`author_id`, `learn-jooq`.`book`.`book_name`, `learn-jooq`.`book`.`publish_year` from `learn-jooq`.`book` where `learn-jooq`.`book`.`id` = ?
23:03:43.846 [main] DEBUG org.jooq.tools.LoggerListener - -> with bind values : select `learn-jooq`.`book`.`id`, `learn-jooq`.`book`.`author_id`, `learn-jooq`.`book`.`book_name`, `learn-jooq`.`book`.`publish_year` from `learn-jooq`.`book` where `learn-jooq`.`book`.`id` = 1
23:03:43.863 [main] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
23:03:43.950 [main] DEBUG org.jooq.tools.LoggerListener - Fetched result : +----+---------+---------+------------+
23:03:43.950 [main] DEBUG org.jooq.tools.LoggerListener - : | id|author_id|book_name|publish_year|
23:03:43.950 [main] DEBUG org.jooq.tools.LoggerListener - : +----+---------+---------+------------+
23:03:43.950 [main] DEBUG org.jooq.tools.LoggerListener - : | 1| 1|神雕侠侣 |1959-01-01 |
23:03:43.951 [main] DEBUG org.jooq.tools.LoggerListener - : +----+---------+---------+------------+
23:03:43.951 [main] DEBUG org.jooq.tools.LoggerListener - Fetched row(s) : 1
BookPojo (1, 1, 神雕侠侣, 1959-01-01)
结束,学习使我快乐。
陈淅灿