JPA/EclipseLink: ¿EntityManager.getTransaction() crea una nueva transacción o devuelve la activa?

Estoy usando EclipseLink 2.3.0. Tengo un método al que llamo desde una prueba de unidad (por lo tanto, fuera de un contenedor, sin JTA) que se ve así:

EntityManager em = /* get an entity manager */;
em.getTransaction().begin();
// make some changes
em.getTransaction().commit();

Los cambios NO se persistieron en la base de datos, y analicé esto durante mucho tiempo y finalmente me di cuenta de que EntityManager.getTransaction() en realidad está devolviendo una NUEVA EntityTransaction, en lugar de la misma en ambas llamadas. El efecto es que la primera llamada crea una nueva transacción y la inicia, y la segunda llamada crea OTRA transacción y la confirma. Debido a que nunca se confirmó la primera transacción, los cambios no se guardan. Verificamos esto así:

log.info(em.getTransaction().toString());
log.info(em.getTransaction().toString());

Lo que resultó en estos mensajes de registro:

INFO: org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl@1e34f445
INFO: org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl@706a4d1a

Los dos ID de objeto diferentes verifican que hay dos instancias diferentes. Cambiando el código a esto:

EntityManager em = /* get an entity manager */;
EntityTransaction tx = em.getTransaction();
tx.begin();
// make some changes
tx.commit();

... solucionado el problema. Ahora, cuando ejecuto el código, veo las declaraciones SQL generadas para hacer el trabajo de la base de datos, y al buscar en la base de datos, los datos han cambiado.

Me sorprendió un poco este resultado, ya que he visto numerosos ejemplos de código en línea (para JPA en general y para EclipseLink específicamente) que recomiendan el código que usamos para administrar transacciones. He buscado por todas partes información específica sobre esto, pero no he encontrado nada. Entonces, ¿qué está pasando?

Busqué en la especificación JPA algo que especifique exactamente lo que hace getTransaction() y no era específico si la transacción es nueva o igual. ¿Hay alguna configuración en persistence.xml que controle esto? ¿El comportamiento es específico para cada implementación de la especificación JPA?

Muchas gracias por cualquier información u orientación.

preguntado el 12 de junio de 12 a las 22:06

3 Respuestas

El uso de getTransaction() funciona en JPA y en EclipseLink (así es como funcionan nuestras propias pruebas).

Supongo que estás haciendo otra cosa muy rara.

¿Estás usando Spring u otra capa? Incluya el código completo y persistence.xml para su prueba. Asegúrese de no estar usando JTA en su archivo persistence.xml.

Respondido el 13 de junio de 12 a las 14:06

getTransaction() debe lanzar una IllegalStateException si se invoca en un administrador de entidades JTA. - JB Nizet

Sin Spring, sin JTA, esto se llama desde una prueba JUnit fuera de un contenedor. El código en "hacer algunos cambios" es de 4 líneas, creando un nuevo objeto, estableciéndole un valor entero, fusionado y vaciado. Crearé un ejemplo destilado cuando tenga un minuto. Quizás este sea un error de EclipseLink. - Tio pelo largo

La especificación JPA (ver párrafo 7.5.4) tiene ejemplos explícitos que muestran el uso de getTransaction() para comenzar y confirmar la transacción. Así que tu código debería estar bien.

Su prueba muestra que obtiene dos objetos diferentes, pero eso no significa que no se use la misma transacción. Tal vez el objeto devuelto es solo un proxy para un único objeto de transacción real.

O tal vez la transacción se comprometió o se revirtió dentro del código oculto debajo // make some changes.

Respondido el 12 de junio de 12 a las 22:06

Tal vez debería haber sido más claro: el código que publiqué NO funciona. Hice ediciones a la pregunta para aclarar. - Tio pelo largo

Entiendo que. Mi punto es que debemos trabajo, ya que la especificación JPA contiene ejemplos de código cuando em.getTransaction() se utiliza para comenzar y confirmar la transacción. Entonces debe ser un error en EclipseLink, a menos que, como dije, el código entre el inicio y el compromiso ya confirme o revierta la transacción. Mi respuesta no es una solución a su problema, sino una confirmación de que el código debería funcionar. Sugiero presentar un error en EclipseLink. - JB Nizet

¿Has intentado usar persistir antes de cometer: ?

  Employee employee = new Employee("Samuel", "Joseph", "Wurzelbacher");
  em.getTransaction().begin();
  em.persist(employee);
  em.getTransaction().commit();

Respondido 04 Abr '17, 08:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.