Java JPA 认为我正在删除一个分离的对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2428706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JPA thinks I'm deleting a detached object
提问by Steve
I've got a DAO that I used to load and save my domain objects using JPA. I finally managed to get the transaction stuff working, now I've got another issue.
我有一个 DAO,用于使用 JPA 加载和保存域对象。我终于设法让交易的东西工作,现在我有另一个问题。
In my test case, I call my DAO to load a domain object with a given id, check that it got loaded and then call the same DAO to delete the object I just loaded. When I do that I get the following:
在我的测试用例中,我调用我的 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.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)
Now given that I'm using the same DAO instance, and I've not changed EntityManagers (unless Spring does so without letting me know), how can this be a detached object?
现在考虑到我使用的是相同的 DAO 实例,并且我没有更改 EntityManagers(除非 Spring 在没有让我知道的情况下这样做),这怎么可能是一个分离的对象?
My DAO code looks like this:
我的 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));
}
}
The test case code looks like:
测试用例代码如下所示:
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);
Can anyone tell me what I'm doing wrong here?
谁能告诉我我在这里做错了什么?
采纳答案by Pascal Thivent
I suspect that you are running your code outside a transaction so your find
and delete
operations occur in a separate persistence context and the find
actually returns a detachedinstance (so JPA is right and you AREdeleting a detached object).
我怀疑你正在运行的事务外的代码,以便您find
和delete
操作发生在一个单独的持久化上下文和find
实际返回一个超然的实例(所以JPA是正确的,你ARE删除分离的对象)。
Wrap your find / delete sequence inside a transaction.
将您的查找/删除序列包装在一个事务中。
Update:Below an excerpt of the chapter 7.3.1. Transaction Persistence Context:
If you use an
EntityManager
with a transaction persistence context model outside of an active transaction, each method invocation creates a new persistence context, performs the method action, and ends the persistence context. For example, consider using theEntityManager.find
method outside of a transaction. TheEntityManager
will create a temporary persistence context, perform the find operation, end the persistence context, and return the detached result object to you. A second call with the same id will return a second detached object.
如果
EntityManager
在活动事务之外使用具有事务持久性上下文模型的 ,则每个方法调用都会创建一个新的持久性上下文,执行方法操作,并结束持久性上下文。例如,考虑在EntityManager.find
事务之外使用该方法。该EntityManager
会创建一个临时的持久化上下文,执行查找操作,最终持久化上下文,将剥离结果对象返还给您。具有相同 id 的第二次调用将返回第二个分离的对象。
回答by zawhtut
+1 to Pascal Thivent's post and just a followup.
+1 对 Pascal Thivent 的帖子和后续跟进。
@Transactional
public void remove(long purchaseId){
Purchase attached = jpaTemplate.find(Purchase.class,purchaseId);
jpaTemplate.remove(attached);
}
回答by Sagar R. Kapadia
public void remove(Object obj){
em.remove(em.merge(obj));
}
The above code is similar to that proposed by zawhtut
上面的代码和zawhtut提出的类似
回答by Chirag Dasani
Get the instance by using em.getReference()
instead of em.find()
.
使用em.getReference()
代替获取实例em.find()
。
For instance, try:
例如,尝试:
em.remove(em.getReference(INTFC.class, id));
回答by Guillermo Mansilla
Here is what I used (based on the previous answers)
这是我使用的(基于以前的答案)
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();
}
回答by App Work
Transaction ensures the ACID properties but not whether the entity is attached or detached.
Even if you are running entityManager.find
and entityManager.remove()
in the same transaction , there is not guarantee that the entity will be attached. So before issuing entityManager.remove()
check if the entity is attached, if not attach it using enitityManger.merge(entity)
and then issue entityManager.remove
on it as follows:
事务确保 ACID 属性,但不确保实体是附加的还是分离的。即使您正在运行entityManager.find
并entityManager.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);
}
回答by Cosmin Mavrichi
What worked for me was calling flush so the changes are made as in the following example:
对我有用的是调用flush,因此更改如下例所示:
@PersistanceContext
EntityManager em;
public SomeObject(...parameters){
repository.save();
em.flush();
repository.delete();
}