Skip to content

使用配置类方式整合 jOOQ ​

在本教程中,我们将演示如何在 tio-boot 项目中,以纯 Java 配置类的方式整合 jOOQ,实现:

  • 不使用 XML
  • 不使用代码生成插件(可选)
  • 使用 DSLContext 进行类型安全 SQL
  • 通过 AOP 自动注入 jOOQ 核心对象
  • 与 tio-boot 生命周期完美整合

通过本文,不仅能学会 怎么做,还能理解 为什么这样做更合理。


一、为什么选择 jOOQ ​

在进入实战之前,先明确一点: jOOQ 并不是 ORM,而是“类型安全的 SQL 构建器”。

jOOQ 的核心优势 ​

  1. SQL 即一等公民

    • 不隐藏 SQL
    • 不强行抽象关系模型
  2. 类型安全

    • 编译期校验字段、表名
    • 避免字符串 SQL 带来的运行期错误
  3. 极强的可控性

    • 复杂 SQL、子查询、窗口函数非常友好
    • 非常适合中大型系统
  4. 非常适合与 tio-boot 这种轻量框架结合

    • 无侵入
    • 全代码配置
    • 启动速度快

二、添加依赖 ​

在 pom.xml 中加入以下依赖:

xml
  <properties>
    <!-- 项目属性 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>21</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>

    <!-- 版本属性 -->
    <lombok-version>1.18.44</lombok-version>
    <fastjson2.version>2.0.52</fastjson2.version>

    <hotswap-classloader.version>1.2.9</hotswap-classloader.version>
    <tio-boot.version>2.1.8</tio-boot.version>
    <jfinal-aop.version>1.4.0</jfinal-aop.version>


    <!-- 应用程序属性 -->
    <final.name>web-hello</final.name>
    <main.class>nexus.io.test.HelloApp</main.class>
  </properties>

  <dependencies>
    <!-- 日志框架 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.3.3</version>
    </dependency>

    <!-- Tio Boot 框架 -->
    <dependency>
      <groupId>nexus.io</groupId>
      <artifactId>tio-boot</artifactId>
      <version>${tio-boot.version}</version>
    </dependency>

    <!-- 热加载类加载器 -->
    <dependency>
      <groupId>nexus.io</groupId>
      <artifactId>hotswap-classloader</artifactId>
      <version>${hotswap-classloader.version}</version>
    </dependency>

    <!-- JFinal AOP -->
    <dependency>
      <groupId>nexus.io</groupId>
      <artifactId>jfinal-aop</artifactId>
      <version>${jfinal-aop.version}</version>
    </dependency>

    <!-- FastJSON2 用于 JSON 解析 -->
    <dependency>
      <groupId>com.alibaba.fastjson2</groupId>
      <artifactId>fastjson2</artifactId>
      <version>${fastjson2.version}</version>
    </dependency>

    <!-- Lombok 用于简化代码 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok-version}</version>
      <optional>true</optional>
      <scope>provided</scope>
    </dependency>

    <!-- JUnit 用于测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <!-- jOOQ -->
    <dependency>
      <groupId>org.jooq</groupId>
      <artifactId>jooq</artifactId>
      <version>3.18.6</version>
    </dependency>

    <!-- PostgreSQL 驱动 -->
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.5.0</version>>
    </dependency>

    <!-- Druid 数据源 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.10</version>
    </dependency>
  </dependencies>

三、启动类 ​

java
package demo.jooq;

import nexus.io.annotation.AComponentScan;
import nexus.io.tio.boot.TioApplication;

@AComponentScan
public class JooqApp {
  public static void main(String[] args) {
    TioApplication.run(JooqApp.class, args);
  }
}

四、数据库表结构 ​

以 system_admin 表为例:

sql
CREATE TABLE system_admin (
  id SERIAL PRIMARY KEY,
  login_name VARCHAR(64),
  password VARCHAR(64)
);

五、模型类(可选) ​

jOOQ 不强制要求实体类,但在 Web 返回时通常仍然需要 DTO:

java
package demo.jooq.model;

import lombok.Data;

@Data
public class SystemAdmin {
  private Integer id;
  private String loginName;
  private String password;
}

六、jOOQ 核心配置类 ​

这是整合的核心部分。

1. 配置目标 ​

  • 创建数据源
  • 创建 DSLContext
  • 注册到 AOP 容器
  • 应用关闭时自动释放资源

2. JooqConfig 配置类 ​

app.properties

properties
jdbc.url=jdbc:postgresql://192.168.3.151/defaultdb
jdbc.user=postgres
jdbc.pswd=1234566
java
package demo.jooq.config;

import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;

import com.alibaba.druid.pool.DruidDataSource;
import nexus.io.annotation.AConfiguration;
import nexus.io.annotation.Initialization;
import nexus.io.constants.ServerConfigKeys;
import nexus.io.hook.HookCan;
import nexus.io.jfinal.aop.AopManager;
import nexus.io.tio.utils.environment.EnvUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@AConfiguration
public class JooqConfig {

  @Initialization
  public void initJooq() {

    String jdbcUrl = EnvUtils.get(ServerConfigKeys.JDBC_URL);
    String username = EnvUtils.getStr(ServerConfigKeys.JDBC_USER);
    String password = EnvUtils.getStr(ServerConfigKeys.JDBC_PSWD);
    // 1. 数据源
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUrl(jdbcUrl);
    dataSource.setUsername(username);
    dataSource.setPassword(password);

    // 2. 创建 DSLContext
    DSLContext dslContext = DSL.using(dataSource, SQLDialect.POSTGRES);

    // 3. 注册到 AOP 容器
    AopManager.me().addSingletonObject(DSLContext.class, dslContext);

    log.info("jOOQ DSLContext initialized");

    // 4. 应用关闭时释放资源
    HookCan.me().addDestroyMethod(() -> {
      dataSource.close();
      log.info("DruidDataSource closed");
    });
  }
}

七、DAO / Repository 层(使用 jOOQ) ​

1. SystemAdminDao ​

java
package demo.jooq.dao;

import org.jooq.DSLContext;
import org.jooq.impl.DSL;

import nexus.io.annotation.Inject;

import demo.jooq.model.SystemAdmin;

public class SystemAdminDao {

  @Inject
  private DSLContext dsl;

  public SystemAdmin findByLoginName(String loginName) {
    return dsl.select(DSL.field("id"), DSL.field("login_name"), DSL.field("password"))
        //
        .from(DSL.table("system_admin"))
        //
        .where(DSL.field("login_name").eq(loginName))
        //
        .fetchOneInto(SystemAdmin.class);
  }
}

2. 为什么这样写 ​

  • DSLContext 是 jOOQ 的核心
  • SQL 结构清晰,完全可控
  • fetchOneInto 自动映射到 POJO
  • 不依赖 XML、不依赖注解、不依赖反射扫描

八、控制器层 ​

java
package demo.jooq.controller;

import com.jfinal.kit.Kv;
import nexus.io.annotation.Inject;
import nexus.io.annotation.RequestPath;

import demo.jooq.dao.SystemAdminDao;
import demo.jooq.model.SystemAdmin;

@RequestPath("/systemAdmin")
public class SystemAdminController {

  @Inject
  private SystemAdminDao systemAdminDao;

  public Kv getByLoginName() {
    SystemAdmin admin = systemAdminDao.findByLoginName("litong");
    return Kv.by("data", admin);
  }
}

九、访问测试 ​

启动应用后访问:

http://localhost/systemAdmin/getByLoginName

返回结果:

json
{
  "data": {
    "id": 1,
    "loginName": "litong",
    "password": "00000000"
  }
}

十、AOP 自动注入原理 ​

1. 注入链路 ​

JooqConfig
  ↓
DSLContext 创建
  ↓
AopManager.addSingletonObject
  ↓
@Inject 自动注入

2. 为什么 DSLContext 适合单例 ​

  • jOOQ 本身无状态
  • 线程安全
  • 连接由 DataSource 管理

十一、为什么 jOOQ + tio-boot 非常搭 ​

对比 MyBatis ​

维度MyBatisjOOQ
SQLXML / 注解纯 Java
类型安全部分完全
动态 SQLXML 标签Java 表达式
重构友好一般极强
复杂 SQL一般非常强

十二、什么时候推荐用 jOOQ ​

  • 中大型系统
  • SQL 复杂度高
  • 对性能和可维护性要求高
  • 不想被 ORM 约束

不推荐的场景:

  • CRUD 极简单
  • 团队 SQL 基础较弱

十三、总结 ​

通过本文,我们完成了:

  • 使用 配置类方式整合 jOOQ
  • 不使用 XML、不使用生成器
  • 与 tio-boot 生命周期深度整合
  • 使用 AOP 实现 DSLContext 自动注入
  • 编写完全类型安全、可维护的 SQL

一句话总结:

jOOQ + tio-boot = 高性能、强控制、低侵入的现代 Java Web 数据访问方案。

MIT Licensed