springdatajpa多对多关系报detachedentitypassedtopersist错误

编程

    最近使用spring data jpa维护表和表的多对多关系时,出现了一个奇怪的问题。当将一个new出来的对象放到实体类维护多对多关系的set中,然后使用jpa进行保存时,会出现如下异常。

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.infiai.webmessenger.dao.ProductTag; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.infiai.webmessenger.dao.ProductTag

at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:299)

at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)

at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:488)

at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)

at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)

at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)

at com.sun.proxy.$Proxy127.saveAndFlush(Unknown Source)

at com.infiai.webmessenger.service.UserService.addUser(UserService.java:38)

at com.infiai.webmessenger.task.UpdateM3OrderDataTask.updateM3OrderDataTask(UpdateM3OrderDataTask.java:128)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)

at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)

at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)

at java.util.concurrent.FutureTask.run(FutureTask.java:266)

at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)

at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

at java.lang.Thread.run(Thread.java:748)

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.infiai.webmessenger.dao.ProductTag

at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124)

at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:765)

at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:758)

at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80)

at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:398)

at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)

at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162)

at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:431)

at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:363)

at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)

at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162)

at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:111)

at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)

at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:278)

at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178)

at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:109)

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)

at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)

at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775)

at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748)

at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)

at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)

at com.sun.proxy.$Proxy116.persist(Unknown Source)

at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:508)

at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:522)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504)

at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489)

at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)

at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)

at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)

     实体类User和ProductTag之间使用jpa维护一个双向多对多关系如下所示

@Entity

@Table(name = "user",

indexes = {@Index(name="unique_index", columnList="name,psid", unique = true)})

public class User

{

@Id

@GeneratedValue

private Integer id;

private String name;

private String psid;

@ManyToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)

@JoinTable(name = "user_tag",

joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},

inverseJoinColumns = {@JoinColumn(name = "tag_id", referencedColumnName = "id")}

)

private Set<ProductTag> tagSet = new HashSet<ProductTag>();

//get set方法省略

}

@Entity

@Table(name = "product_tag",

indexes = {@Index(name="unique_index", columnList="tagName", unique = true)})

public class ProductTag

{

@Id

@GeneratedValue

private Integer id;

private String tagName;

@ManyToMany(mappedBy = "tagSet", fetch=FetchType.EAGER)

private Set<User> userSet = new HashSet<User>();

//get set方法省略

}

     使用如下方法向数据库插入数据是会报detached entity passed to persist错误

User user = userService.findByNameAndPsid(orderData.getClientName(), orderData.getPsid());

tag = new ProductTag();

tag.setTagName(data.getAsin());

user.getTagSet.add(tag);

userService.addUser(user);

    其中user对象是用jpa从数据库中查询出来的,所以为持久态。而tag为new出来的,是一个游离态的对象。

    将该游离态对象加入到user一端维护多对多关系的set集合中,再保存会粗线上述错误。

    后经分析,出现该错误的原因主要有两点:

    1.tag对象为一个游离态对象,需要首先将其加入数据库变为持久态,再加入user对象的tagSet中去。所以会报游离态的错误。

    2.由于实体类是一个双向的多对多关系,所以再保存时需要在两方, user方,productTag方都维护多对多的关系。所以在productTag的userSet集合中也需要加入user。

    经修改,如下代码成功保存

@Entity

@Table(name = "user",

indexes = {@Index(name="unique_index", columnList="name,psid", unique = true)})

public class User

{

@Id

@GeneratedValue

private Integer id;

private String name;

private String psid;

@ManyToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)

@JoinTable(name = "user_tag",

joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},

inverseJoinColumns = {@JoinColumn(name = "tag_id", referencedColumnName = "id")}

)

private Set<ProductTag> tagSet = new HashSet<ProductTag>();

public void addTag(ProductTag tag, boolean setTag)

{

tagSet.add(tag);

if(setTag)

{

tag.addUser(this, false);

}

}

//忽略get set方法

}

@Entity

@Table(name = "product_tag",

indexes = {@Index(name="unique_index", columnList="tagName", unique = true)})

public class ProductTag

{

@Id

@GeneratedValue

private Integer id;

private String tagName;

@ManyToMany(mappedBy = "tagSet", fetch=FetchType.EAGER)

private Set<User> userSet = new HashSet<User>();

public void addUser(User user, boolean setUser)

{

userSet.add(user);

if(setUser)

{

user.addTag(this, false);

}

}

//忽略get set方法

}

 

User user = userService.findByNameAndPsid(orderData.getClientName(), orderData.getPsid());

tag = new ProductTag();

tag.setTagName(data.getAsin());

//先将游离态对象保存

tag = tagService.addTag(tag);

user.addTag(tag, true);

userService.addUser(user);

    其中addUser方法维护了两端的多对多关系,addTag方法也是一样。boolean变量setUser,setTag是为了防止无限循环保存。

   最后总结:出现该问题的原因为,双向实体类关系,两端都要维护,保存多对多关系时,两方需要为持久态。

    有关实体类多对多关系与数据库一致性问题在如下链接有详细讨论

    https://stackoverflow.com/questions/13370221/jpa-hibernate-detached-entity-passed-to-persist

    https://notesonjava.wordpress.com/2008/11/03/managing-the-bidirectional-relationship/

    

 

 

 

 

 

 

 

 

 

 

 

以上是 springdatajpa多对多关系报detachedentitypassedtopersist错误 的全部内容, 来源链接: utcz.com/z/512813.html

回到顶部