浅析Java数据库操作工具包jOOQ的使用

 更新时间:2024年04月03日 14:44:03   作者:磊磊落落  
jOOQ?是一个轻量级的?Java?ORM(对象关系映射)框架,可用来构建复杂的?SQL?查询,这篇文章主要来和大家介绍一下jOOQ的使用,需要的可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

jOOQ 是一个轻量级的 Java ORM(对象关系映射)框架,可用来构建复杂的 SQL 查询。jOOQ 可以根据数据库表自动生成对应的 Java 类,且字段类型与数据库一一对应,减少了 SQL 注入的风险。

本文即是对 jOOQ 的初探,包括四个部分:准备数据库和测试数据、jOOQ Java 代码生成、jOOQ 初步使用,以及 jOOQ 与 Spring Boot 的集成。

开始各个部分前,列出本文涉及的各软件版本:

Java:20(BellSoft LibericaJDK)

Maven:3.9.2

MySQL:8.1.0

jOOQ:3.18.6

Spring Boot:3.1.3

1 准备数据库、表和测试数据

探索 jOOQ 的使用之前,需要有一个数据库和几张表。学生课程系统就是一个不错的业务场景,既接近实际又涉及连表等复杂查询,很适合用来作演示学习。

本文为学生课程系统创建了一个 school 数据库,并在其下创建了三张表 student(学生表)、course(课程表)和 score(成绩表)。

如下为完整的建库、建表和数据插入语句:

-- 创建数据库 school
DROP DATABASE IF EXISTS school;
CREATE DATABASE school DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

-- 使用数据库 school
USE school;

-- 创建学生表
DROP TABLE IF EXISTS student;
CREATE TABLE student (
  no INT NOT NULL,                   -- 编号
  name VARCHAR(20) NOT NULL,         -- 姓名
  gender ENUM('男', '女') NOT NULL,  -- 性别
  birthday DATETIME,                 -- 出生日期
  CONSTRAINT PRIMARY KEY (no)        -- 编号为主键
);

-- 为学生表插入数据
INSERT INTO student VALUES
  (1, '闫浩然', '男', '1999-09-01'),
  (2, '肖雪', '女', '2000-03-21'),
  (3, '张如意', '女', '2001-08-08');

-- 创建课程表
DROP TABLE IF EXISTS course;
CREATE TABLE course (
  no INT NOT NULL,             -- 编号
  name VARCHAR(20) NOT NULL,   -- 名称
  CONSTRAINT PRIMARY KEY (no)  -- 编号为主键
);

-- 为课程表插入数据
INSERT INTO course VALUES
  (1, '语文'),
  (2, '数学'),
  (3, '英语');

-- 创建成绩表
DROP TABLE IF EXISTS score;
CREATE TABLE score (
  student_no INT NOT NULL,                                     -- 学生编号
  course_no INT NOT NULL,                                      -- 课程编号
  degree DECIMAL(4, 1) NOT NULL,                               -- 分数
  CONSTRAINT PRIMARY KEY (student_no, course_no),              -- 学生编号与课程编号为联合主键
  CONSTRAINT FOREIGN KEY (student_no) REFERENCES student(no),  -- 学生编号为外键
  CONSTRAINT FOREIGN KEY (course_no) REFERENCES course(no)     -- 课程编号为外键
);

-- 为成绩表插入数据
INSERT INTO score VALUES
  (1, 1, 90.5),
  (1, 2, 88.0),
  (1, 3, 98.0),
  (2, 1, 78.5),
  (2, 2, 68.0),
  (2, 3, 93.0),
  (3, 1, 83.0),
  (3, 2, 94.5),
  (3, 3, 73.0);

2 jOOQ Java 代码生成

该部分尝试用 jOOQ Maven 插件(jooq-codegen-maven)的方式来生成 Java 代码。

本文使用的是在本地搭建的 MySQL 数据库,将第一部分的 SQL 语句在数据库执行后,即可以尝试使用 jOOQ Maven 插件来生成 Java 代码了(主要是表相关的 Java 类和 POJO 类)。

插件jooq-codegen-maven在 Maven 配置文件pom.xml中的配置信息如下:

<plugin>
    <groupId>org.jooq</groupId>
    <artifactId>jooq-codegen-maven</artifactId>
    <version>${jooq.version}</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <jdbc>
            <driver>com.mysql.cj.jdbc.Driver</driver>
            <url>jdbc:mysql://localhost:3306/school</url>
            <user>root</user>
            <password>root</password>
        </jdbc>
        <generator>
            <generate>
                <pojos>true</pojos>
            </generate>
            <database>
                <includes>.*</includes>
                <inputSchema>school</inputSchema>
            </database>
            <target>
                <packageName>com.leileiluoluo.jooq.model.generated</packageName>
                <directory>src/main/java</directory>
            </target>
        </generator>
    </configuration>
</plugin>

然后,使用如下命令生成 Java 代码:

mvn clean generate-sources

可以看到,代码被生成到了src/main/java文件夹下的com.leileiluoluo.jooq.model.generated包下。

3 jOOQ 初步使用

使用 jOOQ 的一个主要目的可能是想借力其丰富的 SQL 构造能力。

下面即会使用 jOOQ 以及在第二部分生成的 Java 代码(主要是表相关的类和 POJO 类)来实现一些常用的查询。

如下即是使用 jOOQ 来查询所有 Student 的一段示例代码:

import com.leileiluoluo.jooq.model.generated.tables.pojos.Student;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;

import static com.leileiluoluo.jooq.model.generated.Tables.STUDENT;

public class JOOQSimpleQueryTest {

    public static void main(String[] args) {
        String username = "root";
        String password = "root";
        String url = "jdbc:mysql://localhost:3306/school";

        try (Connection conn = DriverManager.getConnection(url, username, password)) {
            DSLContext context = DSL.using(conn, SQLDialect.MYSQL);

            List<Student> students = context.selectFrom(STUDENT)
                    .fetchInto(Student.class);

            students.forEach(student -> {
                System.out.printf("no: %s, name: %s, gender: %s, birthday: %s\n", student.getNo(), student.getName(), student.getGender(), student.getBirthday());
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

可以看到,上面这段代码首先使用DriverManager.getConnection(url, username, password);来创建了一个数据库连接;然后使用DSL.using(conn, SQLDialect.MYSQL);来创建了DSLContext对象;然后即可以用DSLContext来像写 SQL 语句一样(context.selectFrom(STUDENT).fetchInto(Student.class);)来拼装查询语句了,查询结果会自动转换为 POJO 类的类型,非常方便快捷。

程序运行结果如下:

no: 1, name: 闫浩然, gender: 男, birthday: 1999-09-01T00:00
no: 2, name: 肖雪, gender: 女, birthday: 2000-03-21T00:00
no: 3, name: 张如意, gender: 女, birthday: 2001-08-08T00:00

上面的示例针对的是单表查询的情形,下面再看一下复杂查询的拼装:

DSLContext context = DSL.using(conn, SQLDialect.MYSQL);

List<Record3<String, String, BigDecimal>> studentCourseScores = context.select(
                STUDENT.NAME,
                COURSE.NAME,
                SCORE.DEGREE
        ).from(SCORE)
        .join(STUDENT).on(SCORE.STUDENT_NO.eq(STUDENT.NO))
        .join(COURSE).on(SCORE.COURSE_NO.eq(COURSE.NO))
        .fetch();

studentCourseScores.forEach(record -> {
    String studentName = record.getValue(STUDENT.NAME);
    String courseName = record.getValue(COURSE.NAME);
    BigDecimal degree = record.getValue(SCORE.DEGREE);
    System.out.printf("student: %s, course: %s, degree: %s\n", studentName, courseName, degree);
});

上面的查询涉及三个表的连接,依然可以像写 SQL 一样来进行构造。

程序运行结果如下:

student: 张如意, course: 语文, degree: 83.0
student: 肖雪, course: 语文, degree: 78.5
student: 闫浩然, course: 语文, degree: 90.5
student: 张如意, course: 数学, degree: 94.5
student: 肖雪, course: 数学, degree: 68.0
student: 闫浩然, course: 数学, degree: 88.0
student: 张如意, course: 英语, degree: 73.0
student: 肖雪, course: 英语, degree: 93.0
student: 闫浩然, course: 英语, degree: 98.0

其对应的 SQL 语句如下:

SELECT
    s.name,
    c.name,
    sc.degree
FROM score sc
JOIN student s
    ON sc.student_no=s.no
JOIN course c
    ON sc.course_no=c.no;

通过这两段示例程序,即可以看到 jOOQ 的使用非常的简单。针对单表的查询,可以直接将结果映射到 POJO 类;对于多表连接等复杂查询,拼装起来也并不复杂,且结果可以转换为一个多值的类RecordN<?, ?, ?, ...>

4 jOOQ 与 Spring Boot 的集成

第三部分的示例仅适用于本地测试的情形,对于实际的项目,还需要考虑其如何与框架进行集成。

该部分即会探索 jOOQ 与 Spring Boot 的集成,主要会探索两个方面:DSLContext的自动创建、DAO 层的封装。

4.1 DSLContext 的自动创建

在 Spring Boot 中使用 jOOQ 时,DSLContext如何进行创建,这些交给spring-boot-starter-jooq就可以了,我们依然在application.xml采用通用的数据库配置即可,DSLContext会由 Spring 容器自动创建,我们只需在需要的地方进行自动注入就可以了。

# application.yaml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/school
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
// StudentDao.java
@Service
public class StudentDaoImpl implements StudentDao {

    @Autowired
    private DSLContext context;

}

4.2 DAO 层的封装

虽然 jOOQ 也支持自动生成 DAO 层,但其生成的 DAO 层代码比较泛化,有很多方法可能根本就用不着。所以,经过调研后,本人决定仅使用其构建 SQL 的能力(以及自动生成的表相关的类和 POJO 类),DAO 层还是根据业务情形自己来实现比较好一些。

如下即是为 Student 查询设计的 StudentDao 的示例代码:

// StudentDao.java
package com.leileiluoluo.jooq.dao;

import com.leileiluoluo.jooq.model.generated.tables.pojos.Student;

import java.util.List;
import java.util.Optional;

public interface StudentDao {

    Integer countAll();

    List<Student> listAll();

    List<Student> listWithPagination(int offset, int limit);

    Optional<Student> getByNo(Integer no);

    void save(Student record);

    void update(Student record);

    void deleteByNo(Integer no);

}
// StudentDaoImpl.java
package com.leileiluoluo.jooq.dao.impl;

import com.leileiluoluo.jooq.dao.StudentDao;
import com.leileiluoluo.jooq.model.generated.tables.pojos.Student;
import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

import static com.leileiluoluo.jooq.model.generated.Tables.STUDENT;

@Service
public class StudentDaoImpl implements StudentDao {

    @Autowired
    private DSLContext context;

    @Override
    public Integer countAll() {
        return context.fetchCount(STUDENT);
    }

    @Override
    public List<Student> listAll() {
        return context.selectFrom(STUDENT)
                .fetchInto(Student.class);
    }

    @Override
    public List<Student> listWithPagination(int offset, int limit) {
        return context.selectFrom(STUDENT)
                .offset(offset)
                .limit(limit)
                .fetchInto(Student.class);
    }

    @Override
    public Optional<Student> getByNo(Integer no) {
        Student student = context.select()
                .from(STUDENT)
                .where(STUDENT.NO.eq(no))
                .fetchOneInto(Student.class);

        return Optional.ofNullable(student);
    }

    @Override
    public void save(Student student) {
        context.insertInto(STUDENT)
                .set(STUDENT.NO, student.getNo())
                .set(STUDENT.NAME, student.getName())
                .set(STUDENT.GENDER, student.getGender())
                .set(STUDENT.BIRTHDAY, student.getBirthday())
                .execute();
    }

    @Override
    public void update(Student student) {
        context.update(STUDENT)
                .set(STUDENT.NAME, student.getName())
                .set(STUDENT.GENDER, student.getGender())
                .set(STUDENT.BIRTHDAY, student.getBirthday())
                .where(
                        STUDENT.NO.eq(student.getNo())
                )
                .execute();
    }

    @Override
    public void deleteByNo(Integer no) {
        context.deleteFrom(STUDENT)
                .where(
                        STUDENT.NO.eq(no)
                ).execute();
    }

}

可以看到,增、删、改、查都有了,基本满足了实际业务中的需要;在其上设计 Service 和 Controller 即可以实现真实的 REST 业务需求了。

综上,本文准备了一些测试数据,探索了 jOOQ 的代码生成和 SQL 构建能力,最后还思考了其与 Spring Boot 的集成。总体来看,jOOQ 还是比较易用的,是一个不错的 MyBatis 或 Hibernate 替代方案。

以上就是浅析Java数据库操作工具包jOOQ的使用的详细内容,更多关于Java jOOQ数据库操作工具包的资料请关注程序员之家其它相关文章!

相关文章

  • Java WindowBuilder 安装及基本使用的教程

    Java WindowBuilder 安装及基本使用的教程

    这篇文章主要介绍了Java WindowBuilder 安装及基本使用的教程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Java开发中读取XML与properties配置文件的方法

    Java开发中读取XML与properties配置文件的方法

    这篇文章主要介绍了Java开发中读取XML与properties配置文件的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-01-01
  • springboot mybatis手动事务的实现

    springboot mybatis手动事务的实现

    本文主要介绍了springboot mybatis手动事务的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-12-12
  • 简单谈谈Java中String类型的参数传递问题

    简单谈谈Java中String类型的参数传递问题

    这篇文章主要介绍了简单谈谈Java中String类型的参数传递问题的相关资料,需要的朋友可以参考下
    2015-12-12
  • 基于JTable的列宽与内容自适应的实现方法

    基于JTable的列宽与内容自适应的实现方法

    本篇文章是对JTable的列宽与内容自适应的实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Java操作hdfs文件系统过程

    Java操作hdfs文件系统过程

    这篇文章主要介绍了Java操作hdfs文件系统过程,这个过程需要前置准备默认服务器上的hadoop服务已经启动、本地如果是windows环境,需要本地配置下hadoop的环境变量,下面来看看具体得操作过程吧
    2022-02-02
  • Spring Boot2.0使用Spring Security的示例代码

    Spring Boot2.0使用Spring Security的示例代码

    这篇文章主要介绍了Spring Boot2.0使用Spring Security的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • SpringMVC实现文件上传与下载、拦截器、异常处理器等功能

    SpringMVC实现文件上传与下载、拦截器、异常处理器等功能

    这篇文章主要给大家介绍了关于SpringMVC实现文件上传与下载、拦截器、异常处理器等功能的相关资料,这些功能在我们日常开发中经常会遇到,本文通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-09-09
  • springboot如何解决非controller类引用service的问题

    springboot如何解决非controller类引用service的问题

    这篇文章主要介绍了springboot如何解决非controller类引用service的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • SpringMvc web.xml配置实现原理过程解析

    SpringMvc web.xml配置实现原理过程解析

    这篇文章主要介绍了SpringMvc web.xml配置实现原理过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08

最新评论

?


http://www.vxiaotou.com