为啥 JPA 提交在应用程序中失败,但在 JUnit 中有效?

Posted

技术标签:

【中文标题】为啥 JPA 提交在应用程序中失败,但在 JUnit 中有效?【英文标题】:Why JPA commit fails in Application, but works in JUnit?为什么 JPA 提交在应用程序中失败,但在 JUnit 中有效? 【发布时间】:2014-11-26 19:26:50 【问题描述】:

我正在尝试在 JUnit 中重现我在 Spring Boot 应用程序中的 JPA 异常。

申请(提交失败):

@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
@ActiveProfiles("dev")
public class Application implements CommandLineRunner 

    public static void main(final String[] args) 
        SpringApplication.run(Application.class, args);
    

    @Inject
    Repository1 repository1;
    ...    

    @Inject
    RepositoryN repositoryN;

    @Override
    public void run(final String... args) throws Exception 
        // Populating database with demo data
        repository1.save(entity1);
        ...

        // fails on save entityN
        repositoryN.save(entityN);
    

JUnit(有效 - 为什么?):

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =  PersistenceConfig.class, DataConfig.class ,
        loader = AnnotationConfigContextLoader.class)
@Transactional("transactionManager.data")
@ActiveProfiles("dev")
public class BuildingFloorsRoomsRepositoryTest 
    @Inject
    Repository1 repository1;
    ...    

    @Inject
    RepositoryN repositoryN;

    @Test
    @Rollback(false)
    public void saveEntities() 
        repository1.save(entity1);
        ...
        repositoryN.save(entityN);
        // works - why?
    

例外:

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.archibus.domain.hierarchy.Node
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:801)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:794)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:97)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:432)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:265)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:801)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:794)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:97)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:432)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:265)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:194)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:389)

【问题讨论】:

【参考方案1】:

我可以看到的区别是测试是在事务中运行的。尝试在@Transactional 或类似的事务中执行run

【讨论】:

以上是关于为啥 JPA 提交在应用程序中失败,但在 JUnit 中有效?的主要内容,如果未能解决你的问题,请参考以下文章

合并有时在 JPA Hibernate 中失败,但在同一事务中 PERSIST 有效

无法提交 JPA 事务:事务标记为 rollbackOnly

简单的多行正则表达式在 .NET 中失败,但在 ECMAScript 中成功 - 为啥?

JPA 为啥使用 createNamedQuery

为啥我的程序在发布模式下运行良好,但在调试模式下失败? [关闭]

为啥提交在 github 上可见但在本地代表中不可见?