如何使用spring-boot和JPA持久化包含另一个未持久实体的多个相同实例的新实体? [英] How to persist a new entity containing multiple identical instances of another unpersisted entity with spring-boot and JPA?

查看:175
本文介绍了如何使用spring-boot和JPA持久化包含另一个未持久实体的多个相同实例的新实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

概述:

我正在构建一个spring-boot应用程序,该应用程序部分地从外部REST服务中检索一些实体,并将其与数据库中本地存储的实体的先前版本进行比较.

我将EntityManager@PersistenceContext一起注入,并使用它与数据库一起使用,因为存在许多实体类型,并且该类型最初是模块未知的.我可以从工厂获得JpaRepository,但是不同实体类型的数量容易增加,我宁愿完全不依赖它.

问题:

当模块检索它不在数据库中的实体时,它会执行一些业务逻辑,然后尝试保留新实体.

Person类是所讨论的实体之一,包含三个类型为Site的字段,这些字段通常包含相同的对象.

当我尝试使用CascadeType.PERSIST在多个字段中保留具有相同Site对象的新Person时,我得到了EntityExistsException(请参阅stacktrace(1)).

当我从Site字段中删除CascadeType.PERSIST并尝试在多个字段中保留具有相同Site对象的新Person时,我得到了TransientPropertyValueException(请参阅stacktrace(2) ).

我想我理解两个异常都发生的原因:

  • 在第一种情况下,是因为在第一个站点字段被级联持久存储之后,它无法在第二个字段中被重新持久存储.

  • 我认为的第二种情况是因为@Transactional注释试图在不持久化站点实例的情况下刷新事务.

我尝试删除@Transactional注释并自己开始并提交EntityTransaction,但是我得到了IllegalStateException(请参阅stacktrace(3)),尽管我认为这是可以预期的,因为spring应该自己处理事务本身

我查看了类似问题的答案(例如),但所有人都建议更改CascadeType.

在另一个问题中,有人建议确保使用equals()方法正确评估所讨论的实体,因此我在调试器中签入,并且((Person)newEntity).currentSite.equals(((Person)newEntity).homeSite)评估为true.

如何在多个字段中一致地持久化/合并具有相同对象的实体?


编辑:我还尝试了使用fetch = FetchType.EAGER的级联类型的各种组合,但这并没有改变它们各自的异常.


我尝试使用JpaRepository而不是使用EntityManager,并根据我使用的级联类型有效地获得了相同的异常集.

如果我使用PERSIST,但不使用MERGE,则会得到一个EntityNotFoundException(请参阅stacktrace(4)),如果同时使用PERSISTMERGE,则会得到一个InvalidDataAccessApiUsageException`(请参阅stacktrace) (5)).


人员:

@EqualsAndHashCode(callSuper = true)
@javax.persistence.Entity
@XmlDiscriminatorValue("person")
@XmlRootElement(name = "person")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({Subscriber.class})
public class Person extends MobileResource implements Serializable {

    private static final Logger LOG = LogManager.getLogger(Person.class);

    private String firstName;
    private String surname;

    public Person() {
        super();
    }

    public Person(Long id) {
        super(id);
    }

    public Person(Person that) {
        super(that);
        this.firstName = that.firstName;
        this.surname = that.surname;
    }

    // getters && setters
}

移动资源:

@EqualsAndHashCode(callSuper = true)
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@XmlRootElement(name = "resource")
@XmlDiscriminatorNode("@type")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({Vehicle.class, Person.class})
public abstract class MobileResource extends Resource implements Serializable {

    private static final Logger LOG = LogManager.getLogger(MobileResource.class);

    @ManyToOne(cascade = CascadeType.ALL)
    private MobileResourceStatus status;
    private Long                 incidentId;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private Site                 homeSite;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private Site                 currentSite;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private Site                 relocationSite;

    public MobileResource() {
        super();
    }

    public MobileResource(Long id) {
        super(id);
    }

    public MobileResource(MobileResource that) {
        super(that);
        this.status = that.status;
        this.incidentId = that.incidentId;
        this.homeSite = that.homeSite;
        this.currentSite = that.currentSite;
        this.relocationSite = that.relocationSite;
    }

    // getters && setters
}

站点:

@EqualsAndHashCode(callSuper = true)
@javax.persistence.Entity
public class Site extends Resource implements Serializable {

    private static final Logger LOG = LogManager.getLogger(Site.class);

    private String location;

    public Site() {
        super();
    }

    public Site(Long id) {
        super(id);
    }

    public Site(Site that) {
        super(that);
        this.location = that.location;
    }
}

资源:

@EqualsAndHashCode
@MappedSuperclass
@XmlRootElement
@XmlSeeAlso({MobileResource.class})
public abstract class Resource implements Entity, Serializable {

    private static final Logger LOG = LogManager.getLogger(Resource.class);

    @Id
    private Long            id;
    private String          callSign;
    @XmlPath(".")
    private LatLon          latLon;
    private Long            brigadeId;
    private Long            batchId;
    @ManyToMany(cascade = CascadeType.ALL)
    private List<Attribute> attributes;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private ResourceType    type;

    public Resource() {
    }

    public Resource(Long id) {
        this.id = id;
    }

    public Resource(Resource that) {
        this.id = that.id;
        this.callSign = that.callSign;
        this.latLon = that.latLon;
        this.attributes = that.attributes;
        this.batchId = that.batchId;
        this.brigadeId = that.brigadeId;
        this.type = that.type;
    }

    // getters && setters
}

DefaultEntityMessageHandler:

@Component
public class DefaultEntityMessageHandler implements EntityMessageHandler {


    @PersistenceContext
    private EntityManager entityManager;

    @Override
    @Transactional
    public void handleEntityMessage(EntityMessageData data, Message message) {

        // business logic
        if (newEntity != null) {
            if (oldEntity != null)
                entityManager.merge(newEntity);
            else
                entityManager.persist(newEntity);
        }
    }
}

Stacktrace(1):

2018-06-06 12:05:15,975 ERROR ActiveMQMessageConsumer - ID:cpt-9225-1528283097161-1:1:1:1 Exception while processing message: ID:cpt-8919-1528281875592-1:1:1:1:4 
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [my.class.path.entity.resource.site.Site#738]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:118)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:813)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:773)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:467)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:392)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:126)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:414)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:252)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
...

在MobileResource中更改级联类型:

@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE, CascadeType.DETACH})
private Site                 homeSite;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE, CascadeType.DETACH})
private Site                 currentSite;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE, CascadeType.DETACH})

Stacktrace(2):

2018-06-06 12:19:24,084 ERROR ExceptionMapperStandardImpl - HHH000346: Error during managed flush [org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : my.class.path.entity.resource.mobile_resource.person.Person.currentSite -> my.class.path.entity.resource.site.Site]
2018-06-06 12:19:24,093 ERROR ActiveMQMessageConsumer - ID:cpt-9436-1528283955454-1:1:1:1 Exception while processing message: ID:cpt-8919-1528281875592-1:1:1:1:8
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : my.class.path.entity.resource.mobile_resource.person.Person.currentSite -> my.class.path.entity.resource.site.Site; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : my.class.path.entity.resource.mobile_resource.person.Person.currentSite -> my.class.path.entity.resource.site.Site
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:365)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:540)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
...

Stacktrace(3):

2018-06-06 13:29:35,594 ERROR ActiveMQMessageConsumer - ID:cpt-9864-1528288166188-1:1:1:1 Exception while processing message: ID:cpt-8919-1528281875592-1:1:1:1:9
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:254)
    at com.sun.proxy.$Proxy114.getTransaction(Unknown Source)
    at my.class.path.entity_controller.DefaultEntityMessageHandler.handleEntityMessage(DefaultEntityMessageHandler.java:60)
    at my.class.path.entity_listener.listeners.IdExtractorMessageListener.onMessage(IdExtractorMessageListener.java:41)
...

Stacktrace(4)

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-06 15:26:36,143 ERROR SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:793)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
    at my.class.path.OfficerSubscription.main(OfficerSubscription.java:44)
Caused by: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find my.class.path.entity.resource.site.Site with id 738; nested exception is javax.persistence.EntityNotFoundException: Unable to find my.class.path.entity.resource.site.Site with id 738
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:373)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:507)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
...

Stacktrace(5)

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-06 15:31:54,840 ERROR SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:793)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
at my.class.path.OfficerSubscription.main(OfficerSubscription.java:44)
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Multiple representations of the same entity [my.class.path.entity.resource.site.Site#738] are being merged. Detached: [FJE84 - Uckfield]; Detached: [FJE84 - Uckfield]; nested exception is java.lang.IllegalStateException: Multiple representations of the same entity [my.class.path.entity.resource.site.Site#738] are being merged. Detached: [FJE84 - Uckfield]; Detached: [FJE84 - Uckfield]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:365)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:507)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy122.save(Unknown Source)
at my.class.path.OfficerSubscription.run(OfficerSubscription.java:81)
at my.class.path.OfficerSubscription$$FastClassBySpringCGLIB$$705870eb.invoke(<generated>)
    ...

解决方案

经过几天的搜索,我终于在我的spring boot项目中解决了这个问题.

application.yaml文件中添加以下块:

  jpa:
    properties:
      hibernate:
        enable_lazy_load_no_trans: true
        event:
          merge:
            entity_copy_observer: allow

Overview:

I'm building a spring-boot application which, in part, retrieves some entities from an external REST service and compares it to previous versions of the entity held locally in a database.

I'm injecting EntityManager with @PersistenceContext, and using that to work with the database, as there are many entity types, and the type is initially unknown to the module. I could get a JpaRepository from a factory, but the number of different entity types is liable to grow, and I'd rather not rely on that if at all possible.

Problem:

When the module retrieves an entity which it doesn't hold in the database, it does some business logic and then tries to persist the new entity.

The Person class, which is one of the entities in question, contains three fields of type Site, which often hold the same objects.

When I try to persist a new Person which has the same Site object in multiple fields with CascadeType.PERSIST, I get an EntityExistsException (see stacktrace (1)).

When I remove the CascadeType.PERSIST from the Site fields, and try to persist a new Person which has the same Site object in multiple fields, I get a TransientPropertyValueException (see stacktrace (2)).

I think I understand the reasons why both exceptions occur:

  • In the first case it's because after the first site field is cascade-persisted, it cannot be repersisted for the second field.

  • The second case I think is because the the @Transactional annotation is trying to flush the transaction without the site instance(s) being persisted.

I've tried removing the @Transactional annotation and beginning and commiting an EntityTransaction myself, but I get an IllegalStateException (see stacktrace (3)), though I think this is expected as spring should be handling the transactions itself.

I've looked at answers to similar questions (e.g. this, this) but all suggest some variation of changing the CascadeType.

In another question someone suggested to make sure that the entities in question were being evaluated correctly by the equals() method, so I checked in the debugger, and ((Person)newEntity).currentSite.equals(((Person)newEntity).homeSite) evaluates to true.

How can I go about consistently persisting/merging entities with the same object accross multiple fields?


Edit: I've also tried the various combinations of cascade types with fetch = FetchType.EAGER, but this produces no change in their respective exceptions.


Edit 2: I've tried using a JpaRepository instead of using the EntityManager, and get effectively the same set of exceptions depending on what cascade types I'm using.

If I use PERSIST, but no MERGE, I get an EntityNotFoundException (see stacktrace (4)), and if I use both PERSIST and MERGE I get an InvalidDataAccessApiUsageException` (see stacktrace (5)).


Person:

@EqualsAndHashCode(callSuper = true)
@javax.persistence.Entity
@XmlDiscriminatorValue("person")
@XmlRootElement(name = "person")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({Subscriber.class})
public class Person extends MobileResource implements Serializable {

    private static final Logger LOG = LogManager.getLogger(Person.class);

    private String firstName;
    private String surname;

    public Person() {
        super();
    }

    public Person(Long id) {
        super(id);
    }

    public Person(Person that) {
        super(that);
        this.firstName = that.firstName;
        this.surname = that.surname;
    }

    // getters && setters
}

MobileResource:

@EqualsAndHashCode(callSuper = true)
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@XmlRootElement(name = "resource")
@XmlDiscriminatorNode("@type")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({Vehicle.class, Person.class})
public abstract class MobileResource extends Resource implements Serializable {

    private static final Logger LOG = LogManager.getLogger(MobileResource.class);

    @ManyToOne(cascade = CascadeType.ALL)
    private MobileResourceStatus status;
    private Long                 incidentId;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private Site                 homeSite;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private Site                 currentSite;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private Site                 relocationSite;

    public MobileResource() {
        super();
    }

    public MobileResource(Long id) {
        super(id);
    }

    public MobileResource(MobileResource that) {
        super(that);
        this.status = that.status;
        this.incidentId = that.incidentId;
        this.homeSite = that.homeSite;
        this.currentSite = that.currentSite;
        this.relocationSite = that.relocationSite;
    }

    // getters && setters
}

Site:

@EqualsAndHashCode(callSuper = true)
@javax.persistence.Entity
public class Site extends Resource implements Serializable {

    private static final Logger LOG = LogManager.getLogger(Site.class);

    private String location;

    public Site() {
        super();
    }

    public Site(Long id) {
        super(id);
    }

    public Site(Site that) {
        super(that);
        this.location = that.location;
    }
}

Resource:

@EqualsAndHashCode
@MappedSuperclass
@XmlRootElement
@XmlSeeAlso({MobileResource.class})
public abstract class Resource implements Entity, Serializable {

    private static final Logger LOG = LogManager.getLogger(Resource.class);

    @Id
    private Long            id;
    private String          callSign;
    @XmlPath(".")
    private LatLon          latLon;
    private Long            brigadeId;
    private Long            batchId;
    @ManyToMany(cascade = CascadeType.ALL)
    private List<Attribute> attributes;
    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.DETACH})
    private ResourceType    type;

    public Resource() {
    }

    public Resource(Long id) {
        this.id = id;
    }

    public Resource(Resource that) {
        this.id = that.id;
        this.callSign = that.callSign;
        this.latLon = that.latLon;
        this.attributes = that.attributes;
        this.batchId = that.batchId;
        this.brigadeId = that.brigadeId;
        this.type = that.type;
    }

    // getters && setters
}

DefaultEntityMessageHandler:

@Component
public class DefaultEntityMessageHandler implements EntityMessageHandler {


    @PersistenceContext
    private EntityManager entityManager;

    @Override
    @Transactional
    public void handleEntityMessage(EntityMessageData data, Message message) {

        // business logic
        if (newEntity != null) {
            if (oldEntity != null)
                entityManager.merge(newEntity);
            else
                entityManager.persist(newEntity);
        }
    }
}

Stacktrace (1):

2018-06-06 12:05:15,975 ERROR ActiveMQMessageConsumer - ID:cpt-9225-1528283097161-1:1:1:1 Exception while processing message: ID:cpt-8919-1528281875592-1:1:1:1:4 
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [my.class.path.entity.resource.site.Site#738]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:118)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:813)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:773)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:467)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:392)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:126)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:414)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:252)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
...

Changing the cascade type in MobileResource:

@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE, CascadeType.DETACH})
private Site                 homeSite;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE, CascadeType.DETACH})
private Site                 currentSite;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE, CascadeType.DETACH})

Stacktrace (2):

2018-06-06 12:19:24,084 ERROR ExceptionMapperStandardImpl - HHH000346: Error during managed flush [org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : my.class.path.entity.resource.mobile_resource.person.Person.currentSite -> my.class.path.entity.resource.site.Site]
2018-06-06 12:19:24,093 ERROR ActiveMQMessageConsumer - ID:cpt-9436-1528283955454-1:1:1:1 Exception while processing message: ID:cpt-8919-1528281875592-1:1:1:1:8
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : my.class.path.entity.resource.mobile_resource.person.Person.currentSite -> my.class.path.entity.resource.site.Site; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : my.class.path.entity.resource.mobile_resource.person.Person.currentSite -> my.class.path.entity.resource.site.Site
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:365)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:540)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
...

Stacktrace (3):

2018-06-06 13:29:35,594 ERROR ActiveMQMessageConsumer - ID:cpt-9864-1528288166188-1:1:1:1 Exception while processing message: ID:cpt-8919-1528281875592-1:1:1:1:9
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:254)
    at com.sun.proxy.$Proxy114.getTransaction(Unknown Source)
    at my.class.path.entity_controller.DefaultEntityMessageHandler.handleEntityMessage(DefaultEntityMessageHandler.java:60)
    at my.class.path.entity_listener.listeners.IdExtractorMessageListener.onMessage(IdExtractorMessageListener.java:41)
...

Stacktrace (4)

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-06 15:26:36,143 ERROR SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:793)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
    at my.class.path.OfficerSubscription.main(OfficerSubscription.java:44)
Caused by: org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find my.class.path.entity.resource.site.Site with id 738; nested exception is javax.persistence.EntityNotFoundException: Unable to find my.class.path.entity.resource.site.Site with id 738
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:373)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:507)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
...

Stacktrace (5)

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-06 15:31:54,840 ERROR SpringApplication - Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:793)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:774)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
at my.class.path.OfficerSubscription.main(OfficerSubscription.java:44)
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Multiple representations of the same entity [my.class.path.entity.resource.site.Site#738] are being merged. Detached: [FJE84 - Uckfield]; Detached: [FJE84 - Uckfield]; nested exception is java.lang.IllegalStateException: Multiple representations of the same entity [my.class.path.entity.resource.site.Site#738] are being merged. Detached: [FJE84 - Uckfield]; Detached: [FJE84 - Uckfield]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:365)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:227)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:507)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy122.save(Unknown Source)
at my.class.path.OfficerSubscription.run(OfficerSubscription.java:81)
at my.class.path.OfficerSubscription$$FastClassBySpringCGLIB$$705870eb.invoke(<generated>)
    ...

解决方案

After days of search, I finally solved this in my spring boot project.

Add follwing blocks in the application.yaml file:

  jpa:
    properties:
      hibernate:
        enable_lazy_load_no_trans: true
        event:
          merge:
            entity_copy_observer: allow

这篇关于如何使用spring-boot和JPA持久化包含另一个未持久实体的多个相同实例的新实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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