JOOQ登堂未入室

本文详细介绍了JOOQ这个Java数据库访问框架的使用,包括JOOQ简介、数据库准备、Maven配置、代码生成、多数据源处理、基础的CRUD操作以及自定义代码生成。此外,还展示了如何在SpringBoot中整合JOOQ,提供了数据源配置、事务管理以及DAO和Service的实现。通过实例展示了JOOQ在SQL操作上的灵活性和便利性。
摘要由CSDN通过智能技术生成

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)

结束,学习使我快乐。

陈淅灿

 

 

 

 

 

 

 

 

 

 

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值