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

Spring Boot+MySQL环境下如何重置Hibernate序列,每年财年从1开始?

解决方案

一、先明确核心问题

如果序列生成的是数据库主键,绝对不能让其每年从1重启——主键必须全局唯一,否则必然出现重复冲突。你之前的错误就源于此:业务表中已有历史主键值,重置序列到1后,新插入的记录会重复使用已存在的主键。

如果序列生成的是财年维度的业务编号(比如FY2024-001这类),那可以按财年重置序列,同时数据库主键用独立的自增列保证全局唯一。

下面分两种场景给出具体实现:


二、场景1:生成财年维度的业务编号(推荐)

1. 数据库层处理

MySQL没有原生序列,Hibernate默认用hibernate_sequence表模拟。我们可以创建按财年区分的序列表,比如fy_sequence,结构如下:

CREATE TABLE fy_sequence (
    fiscal_year VARCHAR(4) PRIMARY KEY, -- 财年,比如"2024"
    next_val BIGINT NOT NULL DEFAULT 1
);

2. 自定义序列生成器

在Spring Boot中自定义Hibernate序列生成器,实现按财年获取/重置序列值:

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

public class FiscalYearSequenceGenerator implements IdentifierGenerator {

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object obj) {
        // 获取当前财年(若财年为非自然年,比如4月到次年3月,需调整此处逻辑)
        String fiscalYear = String.valueOf(LocalDate.now().getYear());
        
        // 插入或更新序列值:不存在则初始化,存在则自增
        String sql = "INSERT INTO fy_sequence (fiscal_year, next_val) VALUES (:fy, 1) " +
                     "ON DUPLICATE KEY UPDATE next_val = next_val + 1";
        session.createNativeQuery(sql)
               .setParameter("fy", fiscalYear)
               .executeUpdate();
        
        // 获取更新后的序列值
        Long nextVal = (Long) session.createNativeQuery("SELECT next_val FROM fy_sequence WHERE fiscal_year = :fy")
                                   .setParameter("fy", fiscalYear)
                                   .uniqueResult();
        // 返回带财年的业务编号,比如"2024-001"
        return fiscalYear + "-" + String.format("%03d", nextVal);
    }
}

3. 实体类中使用

在实体类的业务编号字段上指定自定义生成器:

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import org.hibernate.annotations.GenericGenerator;

@Entity
public class BusinessRecord {
    // 全局唯一主键
    @Id
    @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY)
    private Long id;
    
    // 财年业务编号
    @GeneratedValue(generator = "fy-sequence")
    @GenericGenerator(name = "fy-sequence", strategy = "com.yourpackage.FiscalYearSequenceGenerator")
    private String fiscalYearNumber;
    
    // 其他字段...
}

三、场景2:必须用序列作为主键(需复合主键)

如果业务要求主键是财年+序列值的组合(全局唯一),则需要使用复合主键:

1. 定义复合主键类

import jakarta.persistence.Embeddable;
import java.io.Serializable;

@Embeddable
public class FiscalYearId implements Serializable {
    private String fiscalYear;
    private Long sequence;
    
    // 构造函数、getter、setter、equals和hashCode方法
}

2. 实体类使用复合主键

import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;

@Entity
public class BusinessRecord {
    @EmbeddedId
    private FiscalYearId id;
    
    // 其他字段...
}

3. 插入时生成主键

插入记录时,先获取当前财年的最大sequence值,加1后作为新的sequence值:

@Service
public class BusinessRecordService {
    @Autowired
    private EntityManager entityManager;
    
    public void createRecord(BusinessRecord record) {
        String fiscalYear = String.valueOf(LocalDate.now().getYear());
        // 获取当前财年的最大sequence值
        Long maxSeq = (Long) entityManager.createQuery("SELECT MAX(r.id.sequence) FROM BusinessRecord r WHERE r.id.fiscalYear = :fy")
                                         .setParameter("fy", fiscalYear)
                                         .getSingleResult();
        Long nextSeq = maxSeq == null ? 1 : maxSeq + 1;
        
        FiscalYearId id = new FiscalYearId();
        id.setFiscalYear(fiscalYear);
        id.setSequence(nextSeq);
        record.setId(id);
        
        entityManager.persist(record);
    }
}

四、避免主键重复的关键注意事项

  • 绝对不要在全局唯一的主键列上使用按财年重置的序列,这违反主键唯一性约束。
  • 若要重置序列,必须确保当前财年的业务表中没有该序列值的记录,或者序列值是财年维度的局部唯一(配合财年组成复合主键)。
  • 不要手动截断序列表,正确的做法是:
    1. 若使用财年序列表,直接更新对应财年的next_val为1(仅在财年切换时执行)。
    2. 若使用复合主键,财年切换时无需额外操作,插入时自动从1开始计数。

内容的提问来源于stack exchange,提问作者param desai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:05:27