JOOQ
1. JOOQ快速入门
环境配置:
Springboot 2.7.15
Jdk 8
Gradle 7.4.2
-
步骤 1:创建 Spring Boot 项目,引入 JOOQ 依赖。
首先引入JOOQ,配置
build.gradle
文件plugins { id 'java' // 使用 Java 插件,使项目成为 Java 项目 id 'org.springframework.boot' version '2.7.15' // 使用 Spring Boot 插件,指定版本为 2.7.15 id 'io.spring.dependency-management' version '1.0.15.RELEASE' // 使用 Spring 依赖管理插件,指定版本为 1.0.15.RELEASE id 'nu.studer.jooq' version '4.2' // 使用 JOOQ 插件,指定版本为 4.2 } group = 'com.hzp' // 指定项目的 Group ID,通常用于唯一标识一个项目的组织或公司 version = '0.0.1-SNAPSHOT' // 指定项目的版本号,用于区分不同版本的发布 java { sourceCompatibility = '1.8' // 设置源代码和目标字节码的兼容性为 Java 8 } repositories { mavenCentral() // 配置 Maven 中央仓库,用于获取项目依赖的库文件 } dependencies { implementation 'org.springframework.boot:spring-boot-starter' // 添加 Spring Boot 核心依赖,包含了开发 Web 应用所需的基本库和工具 testImplementation 'org.springframework.boot:spring-boot-starter-test' // 添加 Spring Boot 测试依赖,用于编写单元测试 implementation 'org.springframework.boot:spring-boot-starter-web' // 添加 Spring Boot Web 相关依赖,用于开发 Web 应用 implementation 'org.springframework.boot:spring-boot-starter-jooq' // 添加 Spring Boot JOOQ 依赖,用于与数据库交互 implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.33' // 添加 MySQL 数据库驱动依赖,用于连接和操作 MySQL 数据库 jooqRuntime(group: 'mysql', name: 'mysql-connector-java', version: '8.0.33') // 添加 JOOQ 运行时依赖,用于在运行时执行 JOOQ 生成的代码 } jooq { version = '3.12.3' // JOОQ 版本号 edition = 'OSS' // 使用开源版本 generateSchemaSourceOnCompilation = false // 不在编译期间自动生成 Schema 源码 sample(sourceSets.main) { // 配置 JOОQ 代码生成器使用的样例 jdbc { driver = 'com.mysql.cj.jdbc.Driver' // 数据库驱动类 url = 'jdbc:mysql://localhost:3306/demo?currentSchema=baseuc-dev&useUnicode=true&characterEncoding=utf-8' // 数据库连接 URL user = 'root' // 数据库用户名 password = 'root' // 数据库密码 } generator { name = 'org.jooq.codegen.DefaultGenerator' // JOОQ 代码生成器的类名 strategy { name = 'org.jooq.codegen.DefaultGeneratorStrategy' // JOОQ 代码生成策略的类名 } database { inputSchema = 'demo' // 输入的数据库 Schema 名称,如果是Mysql那么输入数据库库名 outputSchema = 'hzp' // 生成的数据库 Schema 名称 includes = 'user | goods' // 包含的数据库表名称,多个表名使用竖线分隔 } generate { relations = true // 是否生成对应数据库表之间关系的代码 deprecated = false // 是否生成已过时(deprecated)的代码 records = true // 是否生成记录(Record)类代码 immutablePojos = true // 是否生成不可变(immutable)的 Plain Old Java Objects(POJOs) fluentSetters = true // 是否为生成的代码启用流畅的 Setter 方法 } target { packageName = 'com.tcc.auth.infrastructure.dao' // 生成的代码包名 } } } } tasks.named('test') { useJUnitPlatform() }
配置日志文件,在resource文件夹下新建logback.xml文件,配置JOOQ输出SQL语句
<configuration> <!-- 输出到控制台 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 配置 JOOQ 的日志级别为 DEBUG --> <logger name="org.jooq.tools.LoggerListener" level="DEBUG"/> <!-- 根据需要指定其他 logger --> <!-- 根 logger --> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration>
-
步骤 2:配置数据库连接,包括数据库驱动、URL、用户名和密码等。
spring: datasource: url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver hikari: connection-timeout: 10000 validation-timeout: 3000 idle-timeout: 500000 max-lifetime: 500000 maximum-pool-size: 10 minimum-idle: 5
-
步骤 3:生成代码,使用 JOOQ 自动生成实体类和查询接口。
点击jooq生成工具,生成实体类
-
步骤 4:编写简单的 CRUD 操作,演示 JOOQ 的基本用法。
创建一个UserService接口,编写简易方法,获取数据库中所有内容
public interface UserService { /** * 获取所有的用户 * @return */ public List<User> getAllUsers(); }
创建UserServiceImpl实现类
@Service public class UserServiceImpl implements UserService{ @Resource DSLContext dsl; @Override public List<User> getAllUsers() { //JOOQ使用类似于SQL语句的方式进行查询 Result<Record> fetch = dsl.select().from(USER).fetch(); //使用`into(xxx.class)`可以将返回的Record类型转换成所需要的类型 List<User> userList = fetch.into(User.class); return userList; } }
编写测试类
@Test void getAllUsersTest() { List<User> allUsers = userService.getAllUsers(); allUsers.forEach(System.out::println); }
-
步骤 5:运行项目,验证功能是否正常。
项目运行正常
2. 基础CRUD
2.1 查询
-
基本查询:介绍 JOOQ 的基本,包括选择字段、条件过滤、排序等。
-
根据字段查询
-
查询id相同的用户
首先在接口内创建根据ID查找用户的方法
/** * 根据ID获取用户 * @return */ public User getUserById(Long id);
实现类实现该方法
@Override public User getUserById(Long id) { return dsl.selectFrom(USER).where(USER.ID.eq(id)).fetchOne().into(User.class); }
编写测试类进行测试
@Test void getUsersByIDTest() { User userById = userService.getUserById(1684830092908216322L); System.out.println(userById);//User (1684830092908216322, abc, 123, null) }
-
条件过滤
在接口内创建一个查询Email为空的用户的方法
/** * 获取所有Email为空的用户 * @return */ public List<User> getAllUsersWithoutEmail();
实体类实现该方法
@Override public List<User> getAllUsersWithoutEmail() { List<User> userList = dsl.selectFrom(USER).where(USER.EMAIL.isNull()).fetch().into(User.class); return userList; }
编写测试类进行测试
@Test void getAllUsersWithoutEmailTest(){ List<User> allUsersWithoutEmail = userService.getAllUsersWithoutEmail(); allUsersWithoutEmail.forEach(System.out::println); }
-
排序
接口新建一个方法,实现根据ID进行排序,从小到大
/** * 返回结果根据ID大小排序 * @return */ public List<User> getAllUsersOrderByID();
编写实现类
@Override public List<User> getAllUsersOrderByID() { Result<UserRecord> fetch = dsl.selectFrom(USER).orderBy(USER.ID.asc()).fetch(); List<User> userList = fetch.into(User.class); return userList; }
编写测试类进行测试
@Test void getAllUsersOrderByIDTest(){ List<User> allUsersOrderByID = userService.getAllUsersOrderByID(); allUsersOrderByID.forEach(System.out::println); }
-
-
-
连接查询:演示如何进行多表连接查询。
接口新建方法,根据用户id返回用户拥有的商品
/** * 返回用户所拥有的所有货品 * @return */ public List<Goods> getGoodsByUserId();
编写实现类
@Override public List<Goods> getGoodsByUserId(Long userId) { // 通过DSLContext对象执行查询并获取结果集 Result<Record> fetch = dsl.select(GOODS.fields()) // 选择GOODS表中的所有列作为查询结果 .from(USER) // 指定查询的主表为USER表 .join(GOODS) // 进行USER表和GOODS表的连接操作 .on(USER.ID.eq(GOODS.USER_ID)) // 指定连接条件,即USER表的ID与GOODS表的USER_ID相等 .where(USER.ID.eq(userId)) // 添加查询条件,筛选USER表中ID等于给定userId的记录 .fetch(); // 执行查询并获取结果集 // 将结果集映射到Goods对象列表 List<Goods> goodsList = fetch.into(Goods.class); // 将Result<Record>对象转换为List<Goods>对象,映射规则为按照字段名与属性名进行对应 return goodsList; // 返回查询结果 }
编写测试类
@Test void getGoodsByUserIdTest(){ List<Goods> goodsByUserId = userService.getGoodsByUserId(1684093012058025986L); goodsByUserId.forEach(System.out::println); }
-
聚合函数和分组:使用 JOOQ 进行聚合函数查询和分组操作。
接口新建一个方法,统计用户有多少件商品
/** * 统计用户有多少件商品 * @return */ HashMap<String,Integer> getGoodsCountGroupByUser();
实现类实现方法
@Override public HashMap<String, Integer> getGoodsCountGroupByUser() { /* dsl.select(USER.USER_NAME, count(GOODS.ID)):选择要查询的列,即用户姓名和商品数量(使用聚合函数count统计商品ID出现的次数)。 .from(USER):指定要查询的表是USER。 .leftJoin(GOODS).on(USER.ID.eq(GOODS.USER_ID)):通过左连接将USER表与GOODS表连接起来,连接条件是USER.ID等于GOODS.USER_ID,这样可以关联每个用户所拥有的商品。 .groupBy(USER.USER_NAME):按照USER.USER_NAME进行分组,这样可以得到每个用户对应的商品数量。 .fetch():执行查询并获取结果。 */ Result<Record2<String, Integer>> fetch = dsl.select(USER.USER_NAME, count(GOODS.ID)) .from(USER) .leftJoin(GOODS).on(USER.ID.eq(GOODS.USER_ID)) .groupBy(USER.USER_NAME) .fetch(); HashMap<String, Integer> result = new HashMap<>(); for (Record2<String, Integer> record : fetch) { String s = record.get(USER.USER_NAME); Integer integer = record.get(count()); result.put(s,integer); } return result; }
编写测试类
@Test void getGoodsCountGroupByUser(){ HashMap<String, Integer> goodsCountGroupByUser = userService.getGoodsCountGroupByUser(); goodsCountGroupByUser.forEach((s, integer) -> System.out.println("UserName:" + s + ",GoodsNum:" + integer)); }
2.2 插入
接口新增方法,插入用户
/**
* 插入用户
* @param user
*/
void addUser(User user);
实现类实现该方法
@Override
public void addUser(User user) {
dsl.insertInto(USER)
.set(USER.ID,user.getId())
.set(USER.USER_NAME, user.getUserName())
.set(USER.EMAIL, user.getEmail())
.set(USER.PASSWORD, user.getPassword())
.execute();
}
测试类测试
@Test
void addUserTest(){
User user = new User(18L,"TCC","123456","688@qq.com");
userService.addUser(user);
}
2.3 更新
接口创建方法,更新user
/**
* 更新用户
* @param user
*/
void updateUser(User user);
实体类实现该方法
@Override
public void updateUser(User user) {
dsl.update(USER)
.set(USER.USER_NAME, user.getUserName())
.set(USER.EMAIL, user.getEmail())
.set(USER.PASSWORD, user.getPassword())
.where(USER.ID.eq(user.getId())) // 根据用户ID进行更新
.execute();
}
- 对于
INSERT
或UPDATE
操作,.execute()
返回一个表示受影响的行数的整数值。该值表示成功执行操作后所影响的数据库记录行数。- 对于
DELETE
操作,.execute()
同样返回一个表示受影响的行数的整数值,表示成功删除的数据库记录行数。
编写测试类测试
@Test
void updateUserTest(){
User user = new User(18L,"TCC","156","688@qq.com");
userService.updateUser(user);
}
2.4 删除
接口创建方法,删除user
/**
* id删除用户
* @param id
*/
void deleteUserById(Long id);
实体类实现该方法
@Override
public void deleteUserById(Long id) {
dsl.deleteFrom(USER)
.where(USER.ID.eq(id)) // 根据用户ID进行删除
.execute();
}
编写测试类测试
@Test
void deleteUserTest(){
userService.deleteUserById(0L);
}