java 如何重置 Hibernate 序列生成器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43586360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 07:34:19  来源:igfitidea点击:

How to reset Hibernate sequence generators?

javaspringhibernatejunit

提问by GreenTurtle

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:

我将 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;

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

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

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

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.

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

For example:

例如:

  1. Test1: Create 8 test objects (Hibernate has id values 9 and 10 still allocated)
  2. Recreate database
  3. Test2: Create 12 test objects (Hibernate uses 9 and 10 for IDs, then loads new IDs from database sequence which was meanwhile reseted (1-10) and fails when trying to insert a 2nd entity with ID 9)
  1. Test1:创建 8 个测试对象(Hibernate 仍然分配了 id 值 9 和 10)
  2. 重新创建数据库
  3. Test2:创建 12 个测试对象(Hibernate 使用 9 和 10 作为 ID,然后从数据库序列中加载新的 ID,该序列同时重置 (1-10) 并在尝试插入 ID 为 9 的第二个实体时失败)

So my question: Is there a way to reset Hibernates allocated IDs before each test as well?

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

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

另外:我没有使用 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;
}

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

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

采纳答案by OleG

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

我面临着同样的问题,但没有找到内置的方法来做到这一点。我们正在使用休眠 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 creates an org.hibernate.id.SequenceHiLoGenerator for each entity. The SequnceHiLoGenerator delegates to an OptimizerFactory.LegacyHiLoAlgorithmOptimizer instance.

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

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:

为了重置序列计数器并强制与数据库序列同步,您必须重置 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;
            }
[....]

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") })

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

在测试之间重置序列生成器(@before 或 @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: 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!

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

回答by rajadilipkolli

Perhaps one of the solution is to run each JUNIT test in new sessionfactory. so open and close session factory using @Beforeand @After

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

Pros

优点

  • you get sequence generators from first
  • 你首先得到序列生成器

Cons

缺点

  • It takes a few more seconds to all JUNIT testcases
  • 所有 JUNIT 测试用例都需要几秒钟的时间

Update

更新

Based on comment another way is to reset sequence in every JUNIT test in @Before method

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

ALTER SEQUENCE Test.sequence RESTART WITH 1

回答by Pilosa

Today I faced the same problem. Since I couldn't find another solution, I tried to take the solution from OleG. Unfortunately meanwhile org.hibernate.id.SequenceGeneratoris marked deprecated. Therefore, I used the org.hibernate.id.enhanced.SequenceStyleGenerator. If anyone else needs it, here is my customized solution:

今天我遇到了同样的问题。由于找不到其他解决方案,我尝试从OleG那里获取解决方案。不幸的是,同时org.hibernate.id.SequenceGenerator被标记为已弃用。因此,我使用了org.hibernate.id.enhanced.SequenceStyleGenerator. 如果其他人需要它,这是我的定制解决方案:

public class ResettableSequenceStyleGenerator extends SequenceStyleGenerator {

    private static int cycle = 0;
    private int instanceCycle = cycle;

    private Type configure_type = null;
    private Properties configure_params = null;
    private ServiceRegistry configure_serviceRegistry = null;

    private Database registerExportables_database = null;

    @Override
    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {

        configure_type = type;
        configure_params = params;
        configure_serviceRegistry = serviceRegistry;

        super.configure(type, params, serviceRegistry);
    }

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {

        if (instanceCycle != cycle) {
            super.configure(configure_type, configure_params, configure_serviceRegistry);
            super.registerExportables(registerExportables_database);
            instanceCycle = cycle;
        }

        return super.generate(session, object);
    }

    @Override
    public void registerExportables(Database database) {

        registerExportables_database = database;

        super.registerExportables(database);
    }

    public static void resetAllInstances() {
        cycle++;
    }
}

Set the ResettableSequenceStyleGeneratoras strategy in the GenericGeneratorannotation as described in OleG's article:

如 OleG 的文章所述,ResettableSequenceStyleGeneratorGenericGenerator注释中设置as 策略:

@GenericGenerator(name = "SomeSeq", strategy = "yourpackage.ResettableSequenceStyleGenerator", parameters = ...)

In my IntegrationTest class I then reset the sequences before each test method:

在我的 IntegrationTest 类中,我然后在每个测试方法之前重置序列:

@Before
public void resetSequences() {
    ResettableSequenceStyleGenerator.resetAllInstances();
}

回答by Pankaj Mandale

This will force hibernate to reload its ID generators:

这将强制休眠重新加载其 ID 生成器:

myEntityManagerFactory.close();

myEntityManagerFactory.close();

myEntityManagerFactory = Persistence.createEntityManagerFactory(...);

myEntityManagerFactory = Persistence.createEntityManagerFactory(...);