You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 2中如何通过JPA每日重置PostgreSQL序列?

实现每日重置的「日期+序列ID」生成规则(Spring Boot 2 + JPA + PostgreSQL)

当然可以搞定!这种「日期+每日自增序列」的业务需求其实挺常见的,结合Spring Boot 2、JPA、Hibernate和PostgreSQL的特性,我们有几种靠谱的实现方式,我给你一步步拆解清楚:

方案一:PostgreSQL序列 + Spring定时任务重置

利用PostgreSQL原生的序列特性,配合Spring定时任务每天零点重置序列,是最直接的方式。

1. 先在数据库创建序列

执行这条SQL创建一个初始值为1的序列:

CREATE SEQUENCE daily_seq START 1;

2. 自定义Hibernate ID生成器

我们需要写一个自定义的ID生成器,把当前日期和序列值拼接起来。Hibernate允许我们实现IdentifierGenerator接口来定制ID生成逻辑:

import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class DailySequenceIdGenerator implements IdentifierGenerator {

    // 定义日期格式,比如20240520
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) {
        // 调用PostgreSQL的nextval获取下一个序列值
        String seqQuery = "SELECT nextval('daily_seq')";
        Long seqValue = session.createNativeQuery(seqQuery, Long.class).uniqueResult();
        
        // 拼接日期和序列值,比如202405200001
        String dateStr = LocalDate.now().format(DATE_FORMAT);
        return dateStr + String.format("%04d", seqValue);
    }
}

然后在你的实体类的ID字段上配置这个生成器:

import javax.persistence.*;

@Entity
public class YourBusinessEntity {

    @Id
    @GenericGenerator(
        name = "daily_seq_generator",
        strategy = "com.your.package.path.DailySequenceIdGenerator" // 替换成你的类路径
    )
    @GeneratedValue(generator = "daily_seq_generator")
    private String id;

    // 其他业务字段、getter/setter...
}

3. 定时任务每日重置序列

接下来需要每天零点把序列重置回1,用Spring的@Scheduled定时任务就能轻松实现:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Component
public class DailySequenceResetTask {

    @PersistenceContext
    private EntityManager entityManager;

    // 每天零点执行,cron表达式可以根据需求调整
    @Scheduled(cron = "0 0 0 * * ?")
    public void resetDailySequence() {
        // PostgreSQL重置序列的SQL语句
        String resetQuery = "ALTER SEQUENCE daily_seq RESTART WITH 1";
        entityManager.createNativeQuery(resetQuery).executeUpdate();
    }
}

别忘了在Spring Boot的启动类上加上@EnableScheduling注解,开启定时任务支持!

方案二:用数据库表存储每日序列(无需定时任务)

如果不想依赖PostgreSQL的序列对象,也可以建一张专门的表来记录每天的序列值,每次生成ID时自动判断日期,新的一天就从1开始,完全不需要定时任务。

1. 创建序列记录表

先建一张表,存储日期和当前序列值:

CREATE TABLE daily_sequence (
    record_date DATE PRIMARY KEY,
    current_seq BIGINT NOT NULL DEFAULT 1
);

2. 自定义ID生成器(基于表操作)

修改生成器类,用PostgreSQL的UPSERT语法(INSERT ... ON CONFLICT)来原子性地获取并更新序列值:

import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class DailyTableSequenceIdGenerator implements IdentifierGenerator {

    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) {
        LocalDate today = LocalDate.now();
        
        // UPSERT:如果今日记录存在则序列+1,不存在则插入初始值1,返回当前序列值
        String upsertQuery = """
            INSERT INTO daily_sequence (record_date, current_seq)
            VALUES (:today, 1)
            ON CONFLICT (record_date) DO UPDATE SET current_seq = daily_sequence.current_seq + 1
            RETURNING current_seq
            """;
        
        Long seqValue = session.createNativeQuery(upsertQuery, Long.class)
                .setParameter("today", today)
                .uniqueResult();
        
        String dateStr = today.format(DATE_FORMAT);
        return dateStr + String.format("%04d", seqValue);
    }
}

然后实体类的ID配置和方案一一样,只是把生成器换成这个类就行。

关键注意事项

  • 并发安全:两种方案都是线程安全的——PostgreSQL的序列本身是原子性的,方案二的UPSERT语句也是原子操作,不用担心并发场景下的序列重复问题。
  • 时区一致性:一定要确保应用服务器和数据库的时区一致,避免出现日期错位的情况。可以在Spring Boot配置文件中设置spring.jpa.properties.hibernate.jdbc.time_zone=Asia/Shanghai(替换成你们业务用的时区)。
  • 序列位数调整:根据你们每日的最大业务量,调整String.format("%04d", seqValue)中的位数——比如如果每天最多9999条数据就用4位,要是业务量更大就改成5位(%05d)。

内容的提问来源于stack exchange,提问作者robert trudel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:13:56