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

替换@GenericGenerator为@IdGeneratorType后生成负序列并抛DuplicateKeyException

问题描述

在Spring Boot 3.3.0环境下,尝试将原有基于@GenericGenerator的自定义主键生成逻辑替换为Hibernate 6+推荐的@IdGeneratorType方式。

原有抽象实体代码(基于@GenericGenerator):

@Data
@Accessors(chain = true)
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {

    @GenericGenerator(name = CustomSequenceGenerator.NAME,
            strategy = CustomSequenceGenerator.STR_NAME,
            parameters = {
                    @org.hibernate.annotations.Parameter(name = "optimizer", value = "hilo"),
                    @org.hibernate.annotations.Parameter(name = "initial_value", value = "1"),
                    @org.hibernate.annotations.Parameter(name = "increment_size", value = "10")
            }
    )

    @Id
    @GeneratedValue(generator = CustomSequenceGenerator.NAME)
    @Column(name = "ID")
    private Long id;

    ...
}

改造后创建了自定义注解@SequenceGenerated,并修改抽象实体:

@IdGeneratorType(CustomSequenceGenerator.class)
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface SequenceGenerated {

    int startWith() default 1;
    int incrementBy() default 50;
    Class<? extends Optimizer> optimizer() default HiLoOptimizer.class;

}
@Data
@Accessors(chain = true)
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {

    @Id
    @SequenceGenerated()
    @GeneratedValue(generator = CustomSequenceGenerator.NAME)
    @Column(name = "ID")
    private Long id;

    ...
}

序列名称在CustomSequenceGenerator内部生成,但现在出现问题:插入10-20条数据时ID出现负数,插入100+条数据时抛出异常:

org.springframework.dao.DuplicateKeyException: A different object with the same identifier value was already associated with the session
问题分析与解决

出现这些问题是因为改造过程中遗漏了关键配置,且存在用法错误:

1. 错误混用@GeneratedValue与@IdGeneratorType

@IdGeneratorType是Hibernate 6+专门用来替代@GenericGenerator的注解,通过自定义注解(这里的@SequenceGenerated)绑定生成器后,不需要再添加@GeneratedValue(generator = ...)。同时使用两个注解会导致Hibernate的主键生成逻辑冲突,这是ID出现负数的核心原因之一。

修正后的抽象实体代码:

@Data
@Accessors(chain = true)
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {

    @Id
    @SequenceGenerated()
    @Column(name = "ID")
    private Long id;

    ...
}

2. 自定义生成器未读取注解参数

@SequenceGenerated中定义的startWith、incrementBy、optimizer参数,需要在CustomSequenceGenerator中主动读取并应用。如果生成器没有读取这些参数,会使用默认的错误配置(比如初始值未设置导致生成负数ID,增量不匹配导致主键重复)。

示例修正后的CustomSequenceGenerator核心逻辑(以实现BeforeExecutionGenerator为例):

public class CustomSequenceGenerator implements BeforeExecutionGenerator {

    private int startWith;
    private int incrementBy;
    private Optimizer optimizer;

    @Override
    public void initialize(IdentifierGeneratorDefinition definition, SessionFactoryImplementor sessionFactory,
                          ServiceRegistry serviceRegistry) {
        // 读取自定义注解中的参数
        SequenceGenerated annotation = definition.getAnnotation(SequenceGenerated.class);
        if (annotation != null) {
            this.startWith = annotation.startWith();
            this.incrementBy = annotation.incrementBy();
            // 实例化指定的优化器
            try {
                this.optimizer = annotation.optimizer().getDeclaredConstructor().newInstance();
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                throw new RuntimeException("Failed to initialize optimizer", e);
            }
        }
        // 初始化序列逻辑:比如根据startWith创建/更新数据库序列,设置增量为incrementBy
        // ... 你的序列初始化代码
    }

    @Override
    public Object generate(SharedSessionContractImplementor session, Object owner) {
        // 使用读取到的参数和优化器生成唯一ID
        // ... 你的ID生成逻辑
    }
}

3. 确保序列增量与优化器配置匹配

使用HiLo优化器时,数据库序列的INCREMENT BY值必须和@SequenceGenerated中incrementBy的值一致。如果序列增量和优化器的分段大小不匹配,会导致短时间内重复生成ID,最终触发主键重复异常。

比如@SequenceGenerated中incrementBy = 50,则数据库序列的定义应该是:

CREATE SEQUENCE your_sequence_name START WITH 1 INCREMENT BY 50;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 00:49:53