JPA认为我正在删除一个分离的对象

我有一个使用JPA加载和保存域对象的DAO。 我终于设法得到交易的东西工作,现在我有另一个问题。

在我的testing案例中,我打电话给我的DAO加载一个给定id的域对象,检查它是否被加载,然后调用相同的DAO删除刚加载的对象。 当我这样做时,我得到以下内容:

java.lang.IllegalArgumentException: Removing a detached instance mil.navy.ndms.conops.common.model.impl.jpa.Group#10 at org.hibernate.ejb.event.EJB3DeleteEventListener.performDetachedEntityDeletionCheck(EJB3DeleteEventListener.java:45) at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:108) at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:74) at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:794) at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:772) at org.hibernate.ejb.AbstractEntityManagerImpl.remove(AbstractEntityManagerImpl.java:253) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:48) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37) at java.lang.reflect.Method.invoke(Method.java:600) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:180) at $Proxy27.remove(Unknown Source) at mil.navy.ndms.conops.common.dao.impl.jpa.GroupDao.delete(GroupDao.java:499) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:48) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37) at java.lang.reflect.Method.invoke(Method.java:600) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:304) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at $Proxy28.delete(Unknown Source) at mil.navy.ndms.conops.common.dao.impl.jpa.GroupDaoTest.testGroupDaoSave(GroupDaoTest.java:89) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:48) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37) at java.lang.reflect.Method.invoke(Method.java:600) at junit.framework.TestCase.runTest(TestCase.java:164) at junit.framework.TestCase.runBare(TestCase.java:130) at junit.framework.TestResult$1.protect(TestResult.java:106) at junit.framework.TestResult.runProtected(TestResult.java:124) at junit.framework.TestResult.run(TestResult.java:109) at junit.framework.TestCase.run(TestCase.java:120) at junit.framework.TestSuite.runTest(TestSuite.java:230) at junit.framework.TestSuite.run(TestSuite.java:225) at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196) 

现在考虑到我正在使用相同的DAO实例,而且我还没有更改EntityManagers(除非Spring没有让我知道),那么这怎么可能是一个分离的对象呢?

我的DAO代码如下所示:

 public class GenericJPADao<INTFC extends IAddressable, VO extends BaseAddressable> implements IWebDao, IDao<INTFC>, IDaoUtil<INTFC> { private static Logger logger = Logger.getLogger (GenericJPADao.class); protected Class<?> voClass; @PersistenceContext(unitName = "CONOPS_PU") protected EntityManagerFactory emf; @PersistenceContext(unitName = "CONOPS_PU") protected EntityManager em; public GenericJPADao() { super ( ); ParameterizedType genericSuperclass = (ParameterizedType) getClass ( ).getGenericSuperclass ( ); this.voClass = (Class<?>) genericSuperclass.getActualTypeArguments ( )[1]; } ... public void delete (INTFC modelObj, EntityManager em) { em.remove (modelObj); } @SuppressWarnings("unchecked") public INTFC findById (Long id) { return ((INTFC) em.find (voClass, id)); } } 

testing用例代码如下所示:

 IGroup loadedGroup = dao.findById (group.getId ( )); assertNotNull (loadedGroup); assertEquals (group.getId ( ), loadedGroup.getId ( )); dao.delete (loadedGroup); // - This generates the above exception loadedGroup = dao.findById (group.getId ( )); assertNull(loadedGroup); 

有谁能告诉我我在做什么错在这里?

我怀疑你是在一个事务之外运行你的代码,所以你的finddelete操作发生在一个单独的持久化上下文中,而find实际上返回一个分离的实例(所以JPA是正确的,你正在删除一个分离的对象)。

将事务中的查找/删除序列封装起来。

更新:下面7.3.1节的摘录。 事务持久性上下文 :

如果在活动事务之外使用带有事务持久性上下文模型的EntityManager ,则每个方法调用将创build一个新的持久性上下文,执行方法操作并结束持久性上下文。 例如,考虑在事务外使用EntityManager.find方法。 EntityManager将创build一个临时持久化上下文,执行查找操作,结束持久化上下文,并将分离的结果对象返回给您。 第二次调用相同的ID将返回第二个分离的对象。

 public void remove(Object obj){ em.remove(em.merge(obj)); } 

以上代码与zawhtut提出的代码类似

+1帕斯卡Thivent的职位,只是一个后续行动。

  @Transactional public void remove(long purchaseId){ Purchase attached = jpaTemplate.find(Purchase.class,purchaseId); jpaTemplate.remove(attached); } 

通过使用em.getReference()而不是em.getReference()来获取实例。

例如,尝试:

 em.remove(em.getReference(INTFC.class, id)); 

这是我用的(根据以前的答案)

 public void deleteTask(int taskId) { Task task = getTask(taskId); //this is a function that returns a task by id if (task == null) { return; } EntityManager em = emf.createEntityManager(); EntityTransaction et = em.getTransaction(); et.begin(); em.remove(em.merge(task)); et.commit(); em.close(); } 

事务确保ACID属性,但不确定实体是否连接或分离。 即使您在同一事务中运行了entityManager.findentityManager.remove() ,也不能保证该实体将被连接。 所以在发出entityManager.remove()之前检查实体是否连接,如果没有用enitityManger.merge(entity)附加它,然后发出entityManager.remove就可以了,如下:

 @Transactional public void delete (long id) { ModelObj modelObj=entityManager.find(ModelObj.class,id); modelObj=entityManager.contains(modelObj)?modelObj:entityManager.merge(modelObj); em.remove (modelObj); }