淺析Java數(shù)據(jù)庫操作工具包jOOQ的使用
jOOQ 是一個輕量級的 Java ORM(對象關系映射)框架,可用來構建復雜的 SQL 查詢。jOOQ 可以根據(jù)數(shù)據(jù)庫表自動生成對應的 Java 類,且字段類型與數(shù)據(jù)庫一一對應,減少了 SQL 注入的風險。
本文即是對 jOOQ 的初探,包括四個部分:準備數(shù)據(jù)庫和測試數(shù)據(jù)、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 準備數(shù)據(jù)庫、表和測試數(shù)據(jù)
探索 jOOQ 的使用之前,需要有一個數(shù)據(jù)庫和幾張表。學生課程系統(tǒng)就是一個不錯的業(yè)務場景,既接近實際又涉及連表等復雜查詢,很適合用來作演示學習。
本文為學生課程系統(tǒng)創(chuàng)建了一個 school 數(shù)據(jù)庫,并在其下創(chuàng)建了三張表 student(學生表)、course(課程表)和 score(成績表)。
如下為完整的建庫、建表和數(shù)據(jù)插入語句:
-- 創(chuàng)建數(shù)據(jù)庫 school DROP DATABASE IF EXISTS school; CREATE DATABASE school DEFAULT CHARSET utf8 COLLATE utf8_general_ci; -- 使用數(shù)據(jù)庫 school USE school; -- 創(chuàng)建學生表 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) -- 編號為主鍵 ); -- 為學生表插入數(shù)據(jù) INSERT INTO student VALUES (1, '閆浩然', '男', '1999-09-01'), (2, '肖雪', '女', '2000-03-21'), (3, '張如意', '女', '2001-08-08'); -- 創(chuàng)建課程表 DROP TABLE IF EXISTS course; CREATE TABLE course ( no INT NOT NULL, -- 編號 name VARCHAR(20) NOT NULL, -- 名稱 CONSTRAINT PRIMARY KEY (no) -- 編號為主鍵 ); -- 為課程表插入數(shù)據(jù) INSERT INTO course VALUES (1, '語文'), (2, '數(shù)學'), (3, '英語'); -- 創(chuàng)建成績表 DROP TABLE IF EXISTS score; CREATE TABLE score ( student_no INT NOT NULL, -- 學生編號 course_no INT NOT NULL, -- 課程編號 degree DECIMAL(4, 1) NOT NULL, -- 分數(shù) CONSTRAINT PRIMARY KEY (student_no, course_no), -- 學生編號與課程編號為聯(lián)合主鍵 CONSTRAINT FOREIGN KEY (student_no) REFERENCES student(no), -- 學生編號為外鍵 CONSTRAINT FOREIGN KEY (course_no) REFERENCES course(no) -- 課程編號為外鍵 ); -- 為成績表插入數(shù)據(jù) 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 數(shù)據(jù)庫,將第一部分的 SQL 語句在數(shù)據(jù)庫執(zhí)行后,即可以嘗試使用 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 類)來實現(xiàn)一些常用的查詢。
如下即是使用 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);
來創(chuàng)建了一個數(shù)據(jù)庫連接;然后使用DSL.using(conn, SQLDialect.MYSQL);
來創(chuàng)建了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: 數(shù)學, degree: 94.5
student: 肖雪, course: 數(shù)學, degree: 68.0
student: 閆浩然, course: 數(shù)學, 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
的自動創(chuàng)建、DAO 層的封裝。
4.1 DSLContext 的自動創(chuàng)建
在 Spring Boot 中使用 jOOQ 時,DSLContext
如何進行創(chuàng)建,這些交給spring-boot-starter-jooq
就可以了,我們依然在application.xml
采用通用的數(shù)據(jù)庫配置即可,DSLContext
會由 Spring 容器自動創(chuàng)建,我們只需在需要的地方進行自動注入就可以了。
# 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 層代碼比較泛化,有很多方法可能根本就用不著。所以,經(jīng)過調研后,本人決定僅使用其構建 SQL 的能力(以及自動生成的表相關的類和 POJO 類),DAO 層還是根據(jù)業(yè)務情形自己來實現(xiàn)比較好一些。
如下即是為 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(); } }
可以看到,增、刪、改、查都有了,基本滿足了實際業(yè)務中的需要;在其上設計 Service 和 Controller 即可以實現(xiàn)真實的 REST 業(yè)務需求了。
綜上,本文準備了一些測試數(shù)據(jù),探索了 jOOQ 的代碼生成和 SQL 構建能力,最后還思考了其與 Spring Boot 的集成??傮w來看,jOOQ 還是比較易用的,是一個不錯的 MyBatis 或 Hibernate 替代方案。
以上就是淺析Java數(shù)據(jù)庫操作工具包jOOQ的使用的詳細內容,更多關于Java jOOQ數(shù)據(jù)庫操作工具包的資料請關注腳本之家其它相關文章!
相關文章
java serialVersionUID解決序列化類版本不一致問題面試精講
這篇文章主要為大家介紹了serialVersionUID解決序列化類版本不一致問題的面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10SpringBoot中優(yōu)化if-else語句的七種方法
if-else語句是控制流程的基本工具,但過度使用會使代碼變得復雜且難以維護,在SpringBoot , SpringCloud項目中,優(yōu)化if-else結構變得尤為重要,本文將深入探討七種策略,旨在減少SpringBoot , SpringCloud項目中 if-else的使用,需要的朋友可以參考下2024-07-07Java找出兩個大數(shù)據(jù)量List集合中的不同元素的方法總結
本文將帶大家了解如何快速的找出兩個相似度非常高的List集合里的不同元素。主要通過Java API、List集合雙層遍歷比較不同、借助Map集合查找三種方式,需要的可以參考一下2022-10-10