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

基于Spring Boot、JPA与PostgreSQL实现单列多序列证书编号方案咨询

实现方案说明

该需求完全可以通过Spring Boot + JPA技术栈实现,以下是两种成熟的落地实现方案:


方案一:基于PostgreSQL独立序列实现(推荐,性能高、并发安全)

本方案利用PostgreSQL原生支持多独立序列的特性,为三类产品分别创建专属序列,序列的自增操作是原子性的,天然避免编号重复问题。

实现步骤

  • 第一步:在PostgreSQL中创建3个对应产品的序列,设置不同起始值
-- 第一类产品序列,从001开始
CREATE SEQUENCE IF NOT EXISTS cert_seq_product1 START 1 INCREMENT 1;
-- 第二类产品序列,从2000开始
CREATE SEQUENCE IF NOT EXISTS cert_seq_product2 START 2000 INCREMENT 1;
-- 第三类产品序列,可根据实际需求调整起始值,示例从5000开始
CREATE SEQUENCE IF NOT EXISTS cert_seq_product3 START 5000 INCREMENT 1;
  • 第二步:在Repository中添加查询序列值的原生查询方法
public interface CertRepository extends JpaRepository<CertEntity, Long> {
    @Query(value = "SELECT nextval(:seqName)", nativeQuery = true)
    Long getNextSeqValue(String seqName);
}
  • 第三步:业务层实现编号生成逻辑,在保存数据前赋值证书编号
@Service
public class CertService {
    @Autowired
    private CertRepository certRepository;

    @Transactional(rollbackFor = Exception.class)
    public CertEntity saveCert(CertEntity cert) {
        // 根据产品类型匹配对应序列名
        String seqName = switch (cert.getProductType()) {
            case 1 -> "cert_seq_product1";
            case 2 -> "cert_seq_product2";
            case 3 -> "cert_seq_product3";
            default -> throw new IllegalArgumentException("不支持的产品类型");
        };
        // 获取序列下一个值,可根据需求调整格式补前导零
        Long nextVal = certRepository.getNextSeqValue(seqName);
        // 示例格式为固定3位,不足补前导零,如需4位可改为%04d
        String certNo = String.format("%03d", nextVal);
        cert.setCertNo(certNo);
        return certRepository.save(cert);
    }
}

方案优势

实现逻辑简单,序列自增性能极高,无需额外处理并发冲突,适合高并发场景使用。


方案二:基于序列控制表实现(兼容多数据库、扩展性强)

如果后续存在更换数据库的需求,或者产品类型会持续新增,可通过单独的序列控制表存储各产品的当前最大编号,配合乐观锁控制并发。

实现步骤

  • 第一步:创建序列控制表并初始化数据
CREATE TABLE cert_seq_config (
    id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    product_type INT UNIQUE NOT NULL COMMENT '产品类型',
    current_max_no INT NOT NULL COMMENT '当前最大编号',
    version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号'
);
-- 初始化三类产品的起始值,current_max_no为上次使用的最大值,首次自增后即为起始值
INSERT INTO cert_seq_config (product_type, current_max_no) VALUES (1, 0), (2, 1999), (3, 4999);
  • 第二步:创建序列配置实体类和Repository
@Entity
@Table(name = "cert_seq_config")
public class CertSeqConfig {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Integer productType;
    private Integer currentMaxNo;
    @Version // 乐观锁注解,自动处理版本校验
    private Integer version;
    // 省略getter、setter
}

public interface CertSeqConfigRepository extends JpaRepository<CertSeqConfig, Long> {
    Optional<CertSeqConfig> findByProductType(Integer productType);
}
  • 第三步:业务层添加重试机制,处理乐观锁冲突
@Service
public class CertService {
    @Autowired
    private CertRepository certRepository;
    @Autowired
    private CertSeqConfigRepository seqConfigRepository;

    // 乐观锁冲突时自动重试最多3次,间隔100ms
    @Retryable(value = ObjectOptimisticLockingFailureException.class, maxAttempts = 3, backoff = @Backoff(delay = 100))
    @Transactional(rollbackFor = Exception.class)
    public CertEntity saveCert(CertEntity cert) {
        CertSeqConfig seqConfig = seqConfigRepository.findByProductType(cert.getProductType())
                .orElseThrow(() -> new IllegalArgumentException("不支持的产品类型"));
        // 计算下一个编号
        Integer nextNo = seqConfig.getCurrentMaxNo() + 1;
        // 更新序列最大值
        seqConfig.setCurrentMaxNo(nextNo);
        seqConfigRepository.save(seqConfig);
        // 赋值证书编号
        cert.setCertNo(String.format("%03d", nextNo));
        return certRepository.save(cert);
    }
}

方案优势

兼容所有关系型数据库,后续新增产品类型仅需在cert_seq_config表中新增一条记录即可,无需修改数据库对象。


注意事项

  • 若需修改证书编号的位数或格式,仅需调整String.format的格式化规则即可
  • 并发量极高的场景优先选择方案一,序列的性能远高于乐观锁方案
  • 两种方案的编号生成和数据插入都在同一事务内执行,保证数据一致性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 06:54:06