Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA thinks I'm deleting a detached object

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.

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:

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) 

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?

My DAO code looks like this:

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?

like image 531
Steve Avatar asked Mar 11 '10 21:03

Steve


People also ask

What is detached object in JPA?

A detached entity (a.k.a. a detached object) is an object that has the same ID as an entity in the persistence store but that is no longer part of a persistence context (the scope of an EntityManager session).

How do I delete a detached object in hibernate?

If you have a detached entity, you should invoke Object managed = em. merge(detached) and then em. remove(managed) .

What does EntityManager detach do?

detach. Remove the given entity from the persistence context, causing a managed entity to become detached. Unflushed changes made to the entity if any (including removal of the entity), will not be synchronized to the database. Entities which previously referenced the detached entity will continue to reference it.

How does JPA delete work?

In JPA, to delete an entity, the entity itself must be managed, meaning that it is present in the persistence context. This means that the calling application should have already loaded or accessed the entity and is now issuing a command to remove it.


2 Answers

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 detached instance (so JPA is right and you ARE deleting a detached object).

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 the EntityManager.find method outside of a transaction. The EntityManager 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.

like image 155
Pascal Thivent Avatar answered Oct 02 '22 13:10

Pascal Thivent


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

The above code is similar to that proposed by zawhtut

like image 33
Sagar R. Kapadia Avatar answered Oct 02 '22 13:10

Sagar R. Kapadia