在 Spring Boot 中使用 DataFieldMaxValueIncrementer 获取自增序列
DataFieldMaxValueIncrementer 是 spring-jdbc 项目中的一个接口。用于在应用中生成连续、自增的序列。可用于 主键ID、订单号、流水号 等等。
它基于数据库实现,主要有 2 大子类。
- AbstractSequenceMaxValueIncrementer:用于支持序列(SEQUENCE)的数据库(如 Oracle),使用标准的数据库序列。
- AbstractColumnMaxValueIncrementer:用于不支持序列的数据库(如,MYSQL),使用一张表来模拟。
spring-jdbc 已经为当前流行的关系型数据库提供了具体的实现,类体系结构如下:
DataFieldMaxValueIncrementer
    |-AbstractDataFieldMaxValueIncrementer
        |-AbstractSequenceMaxValueIncrementer
            |-OracleSequenceMaxValueIncrementer # Oracle 数据库
            |-PostgresSequenceMaxValueIncrementer # Postgres 数据库
            |-SqlServerSequenceMaxValueIncrementer # SqlServer 数据库
            |- ...      # 还有一些其他的,这里忽略
        |-AbstractColumnMaxValueIncrementer
            |-MySQLMaxValueIncrementer # MYSQL 数据库
DataFieldMaxValueIncrementer
DataFieldMaxValueIncrementer 接口只有 3 个方法,很简单。
public interface DataFieldMaxValueIncrementer {
    int nextIntValue() throws DataAccessException;
    long nextLongValue() throws DataAccessException;
    String nextStringValue() throws DataAccessException;
}
- nextIntValue:以- int类型返回下一个序列。
- nextLongValue:以- long类型返回下一个序列。
- nextStringValue:以- String类型返回下一个序列。
使用 MySQLMaxValueIncrementer
本文以 MySQLMaxValueIncrementer 为例(毕竟 MYSQL 最流行)。
软件版本如下:
- Spring Boot:3.0.3
- MySQL:8.0.0
创建 Spring Boot 项目
添加 MySQL 驱动 mysql-connector-j 和 spring-boot-starter-jdbc 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>
配置数据库
在配置文件中(application.properties / application.yaml),配置好基础的数据源信息。
spring:
  datasource:
    # 数据源实现
    type: com.zaxxer.hikari.HikariDataSource
    # 驱动类
    driver-class-name: com.mysql.cj.jdbc.Driver
    # URL
    url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&allowMultiQueries=true
    # 用户名
    username: root
    # 密码
    password: root
创建配置类
创建 @Configuration 配置类,实例化 MySQLMaxValueIncrementer 对象,并且注册为 Bean。
package cn.springdoc.demo.configuration;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer;
@Configuration
public class MySQLMaxValueIncrementerConfiguration {
    @Bean
    public MySQLMaxValueIncrementer mySQLMaxValueIncrementer(DataSource dataSource) {
        MySQLMaxValueIncrementer mySQLMaxValueIncrementer = new MySQLMaxValueIncrementer();
        /**
         * 数据源
         */
        mySQLMaxValueIncrementer.setDataSource(dataSource);
        /**
         * 维护序列的表名
         */
        mySQLMaxValueIncrementer.setIncrementerName("T_SEQUENCE");
        /**
         * 序列表中的列名称
         */
        mySQLMaxValueIncrementer.setColumnName("val");
        /**
         * 字符串结果的填充长度,不足长度会在前面填充0,默认为 0
         */
        mySQLMaxValueIncrementer.setPaddingLength(10);
        /**
         * 是否每次获取序列都是使用新的数据库连接,默认为true
         */
        mySQLMaxValueIncrementer.setUseNewConnection(false);
        /**
         * 设置缓存序列的个数,当内存中的序列用完后,会将一次性生成 cacheSize 个序列缓存到内存。
         */
        mySQLMaxValueIncrementer.setCacheSize(1);
        return mySQLMaxValueIncrementer;
    }
    }
通过 @Bean 方法参数注入 Datasource 对象,MySQLMaxValueIncrementer 依赖它来获取数据库连接,进行查询操作。其他的方法都很简单,在注释中都有说明。
最后,我们需要手动在数据库中创建本例中模拟序列的表和列,如下:
CREATE TABLE `t_sequence` (
  `val` bigint unsigned NOT NULL,
  PRIMARY KEY (`val`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
然后初始化唯一的一条记录,设置自增列的初始化值:
INSERT INTO `t_sequence`
(val)
VALUES(0);
测试
创建测试类:
package cn.springdoc.demo.test;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {
    static final Logger log = LoggerFactory.getLogger(DemoApplicationTests.class);
    // 注入 DataFieldMaxValueIncrementer
    @Autowired
    DataFieldMaxValueIncrementer dataFieldMaxValueIncrementer;
    @Test
    public void test() throws Exception {
        
        int intVal = this.dataFieldMaxValueIncrementer.nextIntValue();
        long longVal = this.dataFieldMaxValueIncrementer.nextLongValue();
        String stringVal = this.dataFieldMaxValueIncrementer.nextStringValue();
        
        log.info("next = {}", intVal);
        log.info("next = {}", longVal);
        log.info("next = {}", stringVal);
    }
}
在测试类中注入 DataFieldMaxValueIncrementer 接口,分别调用了它的三个序列生成方法。
执行测试,控制台输出的日志如下:
[           main] c.s.demo.test.DemoApplicationTests       : next = 1
[           main] c.s.demo.test.DemoApplicationTests       : next = 2
[           main] c.s.demo.test.DemoApplicationTests       : next = 0000000003
一切OK,这正是我们需要的自增且连续的序列。
注意 stringVal 值,前面填充了 9 个 0,这是因为我们在配置类中设置了 paddingLength 值为 10,当字符序列长度不足 10 的时候,就会在前面填充 0。
 
				