当前位置: 首页 > 知识库问答 >
问题:

在不同线程上运行时,Hibernate会不必要地创建新实体

盛跃
2023-03-14

我正在使用Spring Data JPA,以违反直觉的行为运行以下测试结果

@Test
public void testAsync() throws ExecutionException, InterruptedException {
    Job job = jobRepository.save(new Job());
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    long origJobID = job.getId();
    executor.initialize();
    Future<?> wait = executor.submit(() -> {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Job outcome = jobRepository.save(job.setStopTime(Instant.now()));
        // this assertion fails, Hibernate requested a new ID and persisted a new entity ... even though I am reusing the same instance with an ID already populated
        assertEquals(origJobID, outcome.getId().longValue());
    });
    wait.get();
}

进一步研究Hibernate的代码库,它出现在一个新线程上,persistentContext被清除干净。因此,就DefaultMergeEventListener而言,我的实体变成了分离状态...创建了一系列决策,这些决策莫名其妙地导致生成一个新ID

我引用的特定代码位于:https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/event/internal/defaultmergeeventlistener.java#l109

如果从一个新线程运行,Hibernate的持久性上下文是空的...这很好....但我不明白为什么我的实体现在被认为是超然的...

更多摘自Hibernate源代码的内容:Default https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/event/internal/defaultmergeeventlistener.java#L293

if ( result == null ) {
            //TODO: we should throw an exception if we really *know* for sure
            //      that this is a detached instance, rather than just assuming
            //throw new StaleObjectStateException(entityName, id);

            // we got here because we assumed that an instance
            // with an assigned id was detached, when it was
            // really persistent
            entityIsTransient( event, copyCache );
        }

如果这与事务尚未提交有关,并且新线程没有使用相同的事务,请编辑...是否有方法在代码中强制执行事务?

注意调用jparepository.saveandflush()不能解决此问题

编辑2我将使用embedded h2进行此测试,无论哪种情况,我都希望saveandflush()已经将事务提交到数据库(嵌入的或在世界的另一端),以便多个线程可以使用JPararePository查看其他线程保存的状态,对吗?

编辑3查看其他类似的问题,它似乎将测试方法本身标记为@transaction(propagation=NOT_SUPPORTED)强制底层事务管理器提交jparepository.save()...这仍然令人困惑...测试方法一开始是如何事务性的?即为什么交易没有从一开始就提交?

共有1个答案

孟佑运
2023-03-14

关于它的工作原理的一个非常粗略的想法:

  • 您有多个线程
  • 事务通常是线程绑定的(每个线程都有自己的事务)
  • EntityManager通常是事务绑定的(每个事务都有自己的EntityManager,它根据该事务查看事情)

你正在做的是

    null

通过确保线程1在步骤3时已经提交,您应该能够实现这一工作。被执行。无论如何,我不建议在线程之间传递托管实体引用,因为它非常棘手,非常快。

关于Spring数据事务分界,您可以阅读https://docs.Spring.io/spring-data/jpa/docs/current/reference/html/#transactions了解更多信息,但它基本上涉及将每个事务变成一个方法,然后对该方法进行注释。

 类似资料:
  • 我正在运行RxJava并创建一个主题以使用方法生成数据。我正在使用Spring。 这是我的设置: 在RxJava流上生成新数据的方式是通过Autowire private SubjectObserver SubjectObserver,然后调用SubjectObserver。发布(newDataObjGenerated) 无论我为subscribeOn()指定了什么 Schedulers.io()

  • 线程n:usern:task1->task2->Task3,usern:task1->task2->Task3,usern:task1->task2->Task3,... 然而,我还不知道如何做到这一点。每次我运行测试时,所有线程似乎都在迭代CSV文件并混合用户时选择用户,直到同时在两个不同的线程上找到一个用户。 像这样: 线程n:usern:task1->task2->Task3,...,use

  • 问题内容: 在我拥有的一个小程序(尤其是cgo调用)上,go build和go run非常慢。我想缓存二进制文件,以便仅在源文件较新时才重建。我会使用带有%规则的简单Makefile,但是语言设计人员声称go的构建支持不需要Makefile。 我还有其他选择吗?go社区是否愿意使用另一个构建系统(可能是基于哈希的构建系统)来缓存和重用构建产品? 问题答案: 我写了一个工具来解决这个问题。单独不会检

  • 我正在使用Python和tkinter创建一个游戏,当我试图添加一些音乐时,制作音乐的函数停止了脚本的其余部分(还有窗口)。所以我需要创建一个新的线程来播放音频并保持窗口工作... 我尝试了一些错误、不运行或停止窗口的事情,目前我有: 重要的细节:playSound函数什么也不返回,所以什么都不需要等待它。我也不太懂用Python编程,我更喜欢用Luau。

  • 问题内容: 是否可以在运行时创建hibernate的Entity类(带有注释)?它应与也在运行时中添加的表相关。重要的是,该实体应该对Hibernate可见。一般情况下是否可能,请您给我有关该问题的任何建议吗?我了解,反射是关键字,但需要更多的单词。问候。 问题答案: 在这种情况下,您可能希望在运行时以编程方式创建Hibernate Session Factory,并传入创建的新映射,请访问htt