2010-03-11 147 views
40

我有一個DAO,用於使用JPA加載和保存我的域對象。我終於設法讓事務工作,現在我又遇到了另一個問題。JPA認爲我正在刪除一個分離的對象

在我的測試案例中,我打電話給我的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(除非春這樣做沒有讓我知道了),怎麼能這樣一個分離的對象?

吾道代碼如下所示:

測試用例代碼如下:

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); 

誰能告訴我,我做錯了什麼嗎?

回答

66

我懷疑你正在運行的代碼中的事務外讓你finddelete操作發生在一個單獨的持久化上下文和find的實際返回分離實例(所以JPA是正確的,你ARE刪除分離目的)。

將您的查找/刪除序列封裝在一個事務中。

更新:下面的章節7.3.1. Transaction Persistence Context的摘錄:

如果使用EntityManager與活動事務以外的事務持久化上下文模型中,每個方法調用創建一個新的持久化上下文,執行方法操作,並結束持久化上下文。例如,考慮在事務外使用EntityManager.find方法。 EntityManager將創建一個臨時持久化上下文,執行查找操作,結束持久化上下文,並將分離的結果對象返回給您。具有相同ID的第二次調用將返回第二個分離的對象。

+7

這確實似乎是違反直覺的給我。我是否真的需要在事務中包裝一個不影響數據庫的操作(find()),以便我可以刪除(或保存或更新)它? – Steve 2010-03-11 21:37:21

+0

雖然可能會違反直覺,但確實有效。這似乎暗示我需要完全重新考慮我的DAO設計。這聽起來像*每個*操作,最終將修改一個實體將不得不首先找到(在相同的交易,將用於寫實體)。 – Steve 2010-03-11 21:44:35

+0

@Steve你可能會發現它與直覺相反,但這是事情的方式。如果你在事務之外使用'find',你會得到一個分離的實體。 – 2010-03-11 21:49:53

14

+1 Pascal Thivent的帖子,只是一個後續。

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

上面的代碼是類似於由zawhtut

+1

通用刪除非常好的解決方案,因爲你不需要知道密鑰。 – 2012-03-20 10:30:32

+0

你救了我:) – vinod 2017-05-22 06:50:23

6

提出了利用em.getReference()代替em.find()獲取實例。

例如,嘗試:

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

那麼它避免查詢數據庫? – Pawan 2017-02-14 20:04:56

3

下面是我用什麼(基於以前的答案)

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(); 
} 
0

交易保證了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); 
    } 
相關問題