如何使用spring-boot和JPA持久化包含另一个未持久实体的多个相同实例的新实体? [英] How to persist a new entity containing multiple identical instances of another unpersisted entity with spring-boot and 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)),如果同时使用PERSIST
和MERGE
,则会得到一个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屋!