如何重置 Hibernate 序列生成器?

Gre*_*tle 8 java junit spring hibernate

我将 Hibernate 3.5.6-Final 与用于生产的 Oracle 数据库和用于集成测试的 H2 数据库一起使用。ID 创建的 Hibernate 映射看起来像这样,每个实体都扩展了 EasyPersistentObject:

@MappedSuperclass
public class EasyPersistentObject implements Serializable {

@Id
@SequenceGenerator(name = "hibernate_seq", sequenceName = "hibernate_id_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hibernate_seq")
protected Integer id;
Run Code Online (Sandbox Code Playgroud)

在每次 JUnit 集成测试之前,我都会从数据库中删除所有数据

new SchemaExport(configuration).create(false, true);
Run Code Online (Sandbox Code Playgroud)

一切正常,直到我为序列生成增加了 allocationSize。将其提高到例如 10 将在插入测试数据时使用 UniqueKeyConstraintViolations 破坏多个测试。

例如:

  1. Test1:创建 8 个测试对象(Hibernate 仍然分配了 id 值 9 和 10)
  2. 重新创建数据库
  3. Test2:创建 12 个测试对象(Hibernate 使用 9 和 10 作为 ID,然后从同时重置(1-10)的数据库序列中加载新 ID,并在尝试插入 ID 为 9 的第二个实体时失败)

所以我的问题有没有办法在每次测试之前重置 Hibernates 分配的 ID?

另外: 我没有使用 JPA 中的 PersistenceManager,而是使用纯 Hibernate SessionFactory,它是这样创建的:

@Bean(name = "easySF")
public SessionFactory easySessionFactory(@Qualifier("easyConfig") AnnotationConfiguration configuration) {
    configuration.setInterceptor(new HibernateInterceptor());
    return configuration.buildSessionFactory();
}

@Bean(name = "easyConfig")
protected AnnotationConfiguration easyHibernateConfiguration() {
    AnnotationConfiguration configuration = new AnnotationConfiguration();
    configuration.setProperties(createHibernateProperties());   
    for (Class annotatedClass : getAnnotatedClasses()) {
        configuration.addAnnotatedClass(annotatedClass);
    }
    return configuration;
}
Run Code Online (Sandbox Code Playgroud)

我真的需要重新加载整个 Spring 上下文才能重置 Hibernate ID 生成器吗?

raj*_*lli 9

也许解决方案之一是在新的 sessionfactory 中运行每个 JUNIT 测试。@Before所以使用和打开和关闭会话工厂@After

优点

  • 你首先得到序列生成器

缺点

  • 所有 JUNIT 测试用例还需要几秒钟

更新

根据注释,另一种方法是在 @Before 方法中的每个 JUNIT 测试中重置序列

ALTER SEQUENCE Test.sequence RESTART WITH 1
Run Code Online (Sandbox Code Playgroud)


Ole*_*leG 6

我面临着同样的问题,但没有找到内置的方法来做到这一点。我们使用的是 hibernate 4.2.7。

在深入调试休眠状态后,我最终扩展了序列生成器。我们使用标准序列生成器创建实体:

@SequenceGenerator(name = "SomeSeq", sequenceName = "DB_SEQ", allocationSize = 50)
Run Code Online (Sandbox Code Playgroud)

Hibernate 为每个实体创建一个 org.hibernate.id.SequenceHiLoGenerator。SequnceHiLoGenerator 委托给 OptimizerFactory.LegacyHiLoAlgorithmOptimizer 实例。

为了重置序列计数器并强制与数据库序列同步,您必须重置 LegacyHiLoAlgorithmOptimizer 中的内部变量。不幸的是,这些变量是私有的并且不可修改。我试图找到一种使用继承的方法,但没有找到一个优雅的解决方案。最后,我
创建了 SequenceHiLoGenerator 的源副本,并通过简单的重置功能对其进行了扩展:

public class ResetableIdGenerator extends SequenceGenerator {
               public static int cycle = 0; // global, indicating current test cycle
               protected int startingCycle = 0; // instance, indicating the test cycle the LegacyHiLoAlgorithmOptimizer was used the last time
    [...]
        public synchronized Serializable generate(final SessionImplementor session, Object obj) {
            // create a new HiLoOptimizer if there's a new test cycle
            if (startingCycle < cycle) {
                hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer(getIdentifierType().getReturnedClass(),
                        maxLo);
                startingCycle = cycle;
            }
[....]
Run Code Online (Sandbox Code Playgroud)

修改实体以使用自定义生成器:

@GenericGenerator(name = "SomeSeq", strategy = "yourpackage.ResetableIdGenerator", parameters = {
        @Parameter(name = "sequence", value = "DB_SEQ"), @Parameter(name = "max_lo", value = "49") })
Run Code Online (Sandbox Code Playgroud)

在测试之间重置序列生成器(@before 或 @after):

// reset Hibernate Sequences
ResetableIdGenerator.cycle++;
Run Code Online (Sandbox Code Playgroud)

我知道这不是一个好的解决方案 - 这是一个 hack。但它确实有效,也许有助于找到更好的解决方案。

编辑20170504:我最初的帖子包含一个错误:参数“sequenceName”和“allocationSize”是JPA参数。GenericGenerator 来自 hibernate。您必须使用“sequence”而不是“sequenceName”,您必须使用“max_lo”而不是“allocationSize”并将其设置为allocationSize-1。我更新了代码示例。对不起!