2010-03-11 19 views
40

Tengo un DAO que utilicé para cargar y guardar mis objetos de dominio usando JPA. Finalmente logré que las cosas de la transacción funcionaran, ahora tengo otro problema.JPA cree que estoy eliminando un objeto separado

En mi caso de prueba, llamo a mi DAO para cargar un objeto de dominio con una identificación dada, verifico que se cargó y luego invoco el mismo DAO para eliminar el objeto que acabo de cargar. Cuando lo hago me sale el siguiente:

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) 

da ahora que estoy usando la misma instancia de DAO, y yo no he cambiado EntityManagers (a menos que la primavera hace sin avisarme), cómo puede ser esto un objeto separado?

Mi código DAO se parece a esto: un código de caja

La prueba se parece a:

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

Puede alguien decirme lo que estoy haciendo mal aquí?

Respuesta

66

Sospecho que está ejecutando su código fuera una transacción por lo que sus find y delete operaciones ocurren en un contexto de persistencia separado y el find realidad devuelve un unifamiliar instancia (por lo APP es correcto y que SON borrar a Independiente objeto).

Envuelva su secuencia de búsqueda/eliminación dentro de una transacción.

Actualización: A continuación un extracto del capítulo 7.3.1. Transaction Persistence Context:

Si utiliza un EntityManager con un modelo de contexto de persistencia de transacciones fuera de una transacción activa, cada invocación método crea un nuevo contexto de persistencia, realiza la acción de método, y finaliza el contexto de persistencia. Por ejemplo, considere usar el método EntityManager.find fuera de una transacción. El EntityManager creará un contexto de persistencia temporal, realizará la operación de búsqueda, finalizará el contexto de persistencia y le devolverá el objeto de resultado desconectado. Una segunda llamada con la misma identificación devolverá un segundo objeto separado.

+7

Eso realmente parece contra-intuitivo para mí. ¿Realmente necesito ajustar una operación que no afecte a la base de datos (find()) en una transacción, solo para poder eliminarla (o guardarla o actualizarla)? – Steve

+0

Por muy intuitivo que sea, funciona. Esto parece implicar que necesito reintegrar por completo mi diseño DAO. Parece que * cada * operación que eventualmente modificará una entidad tendrá que buscar primero (en la misma transacción que se utilizará para escribir la entidad). – Steve

+0

@Steve Puede encontrarlo contrario a la intuición, pero así es como funcionan las cosas. Si usa 'find' fuera de una transacción, obtendrá una entidad separada. –

14

+1 a la publicación de Pascal Thivent y solo un seguimiento.

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

El código anterior es similar a la propuesta por zawhtut

+1

Muy buena solución para eliminación genérica, porque no necesita conocer la clave. –

+0

me salvaste :) – vinod

6

Obtener el ejemplo utilizando em.getReference() en lugar de em.find().

Por ejemplo, trate de:

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

¿Así que evita consultar la base de datos? – Pawan

3

Aquí es lo que he utilizado (basado en las respuestas anteriores)

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

Transacción asegura las propiedades ACID, pero no si la entidad está conectada o desconectada. Incluso si está ejecutando entityManager.find y entityManager.remove() en la misma transacción, no hay garantía de que la entidad se adjuntará. Así que antes de emitir entityManager.remove() de verificación si la entidad está unido, si no fijarla por medio de enitityManger.merge(entity) y luego emitir entityManager.remove en ella de la siguiente manera:

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