如何重置休眠序列生成器? [英] How to reset Hibernate sequence generators?

查看:91
本文介绍了如何重置休眠序列生成器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using Hibernate 3.5.6-Final with an Oracle database for production and a H2 database for integration tests. The Hibernate mapping for ID creation looks like this with every entity extending 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;

在每次JUnit集成测试之前,我将使用

Before each JUnit integration test I am removing all data from the database with

new SchemaExport(configuration).create(false, true);

一切正常,直到我增加用于序列生成的分配大小为止.提高到例如10会在插入测试数据时破坏带有UniqueKeyConstraintViolations的多个测试.

Everything works fine until I increment the allocationSize for sequence generation. Raising this to e.g. 10 will break several tests with UniqueKeyConstraintViolations when inserting test data.

例如:

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

所以我的问题是:是否还可以在每次测试之前重置Hibernate分配的ID?

添加: 我不是从JPA使用PersistenceManager,而是使用像这样创建的纯Hibernate SessionFactory:

Addition: I am not using PersistenceManager from JPA but pure Hibernate SessionFactory which is created like this:

@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;
}

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

Do I really need to reload my whole Spring context to get the Hibernate ID generators reseted?

推荐答案

我遇到了同样的问题,但没有找到一种内置的方法来实现.我们正在使用休眠4.2.7.

I was facing the same Problem and didn't find an inbuild way to do it. We're using hibernate 4.2.7.

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

After debugging deeply into hibernate I ended up extending the sequence generator. We create entities using the standard sequence generator:

@SequenceGenerator(name = "SomeSeq", sequenceName = "DB_SEQ", allocationSize = 50)

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

Hibernate creates an org.hibernate.id.SequenceHiLoGenerator for each entity. The SequnceHiLoGenerator delegates to an OptimizerFactory.LegacyHiLoAlgorithmOptimizer instance.

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

In order to reset the sequence counters and force syncing with the database sequence you have to reset internal variables in the LegacyHiLoAlgorithmOptimizer. Unfortunately these variables are private and not modifyable. I tried to find a way using inheritance but I didn't find an elegant solution. Finally I
created a source copy of the SequenceHiLoGenerator and extended it with a simple reset functionality:

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;
            }
[....]

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

Modify the entities to use the custom generator:

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

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

Reset the sequence generator inbetween your test (@before or @after):

// reset Hibernate Sequences
ResetableIdGenerator.cycle++;

我知道这不是一个好的解决方案-这是骇客.但这有效,也许有助于找到更好的解决方案.

I know this isn't a good solution - It's a hack. But it works and maybe it helps to find a better solution.

EDIT 20170504:我的第一篇文章包含一个错误:参数"sequenceName"和"allocationSize"是JPA参数. GenericGenerator来自休眠状态. 代替"sequenceName",您必须使用"sequence",而不是"allocationSize",您必须使用"max_lo"并将其设置为allocationSize-1. 我更新了代码示例.抱歉!

EDIT 20170504: My initial post contained a mistake: The parameters "sequenceName" and "allocationSize" are JPA parameters. The GenericGenerator is from hibernate. Instead of "sequenceName" you have to use "sequence", instead of "allocationSize" you have to use "max_lo" and set it to allocationSize-1. I updated the code example. Sorry!

这篇关于如何重置休眠序列生成器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆