para example dummies curso java jpa jpa-2.0 java-ee-6

java - example - jpa para dummies



JPA anidado transacciones y bloqueo (4)

Considere el escenario dos métodos existen en diferentes frijoles sin estado

public class Bean_A { Bean_B beanB; // Injected or whatever public void methodA() { Entity e1 = // get from db e1.setName("Blah"); entityManager.persist(e1); int age = beanB.methodB(); } } public class Bean_B { //Note transaction @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodB() { // complex calc to calculate age } }

La transacción iniciada por BeanA.methodA se suspendería y la nueva transacción se iniciaría en BeanB.methodB. ¿Qué pasa si el método B necesita acceder a la misma entidad que fue modificada por el método A. Esto resultaría en un punto muerto. ¿Es posible prevenirlo sin depender de los niveles de aislamiento?


Aquí hay un artículo reciente sobre el uso de la demarcación de transacciones REQUIRES_NEW.

Desde mi experiencia, no debería haber ningún bloqueo con código estándar: consultas con cláusula de restricción y pocas inserciones. En algunos casos específicos, algunos motores de base de datos pueden realizar una escalada de bloqueo si hay muchas filas leídas o insertadas en una sola tabla durante la transacción ... y en ese caso, sí puede producirse un bloqueo.

Pero en ese caso, el problema no proviene de REQUIRES_NEW sino del diseño de SQL. Si ese diseño no se puede mejorar, entonces no tiene otra opción para cambiar el nivel de aislamiento a un nivel más flexible.


Hm, vamos a enumerar todos los casos.

REQUIRES_NEW no anida realmente las transacciones, pero como mencionó, detiene la actual. Entonces hay simplemente dos transacciones que acceden a la misma información. (Esto es similar a dos transacciones concurrentes regulares, excepto que no son concurrentes sino en el mismo hilo de ejecución).

T1 T2 T1 T2 ― ― | | | ― | ― | | | | = | | ― | ― | | | ― ―

Entonces debemos considerar el bloqueo optimista frente al pesimista .

Además, debemos considerar los enjuagues operados por ORM. Con los ORM, no tenemos un control claro cuando se producen las escrituras, ya que el marco controla la flush . Por lo general, una descarga implícita ocurre antes de la confirmación, pero si se modifican muchas entradas, el marco también puede hacer descargas intermedias.

1) Consideremos el bloqueo optimista, donde la lectura no adquiere bloqueos, pero la escritura adquiere bloqueos exclusivos.

La lectura por T1 no adquiere un bloqueo.

1a) Si T1 eliminó los cambios prematuramente, adquirió un bloqueo exclusivo. Cuando T2 se compromete, intenta adquirir el bloqueo pero no puede. El sistema está bloqueado. Esto puede ser de un tipo particular de punto muerto. La finalización depende de cómo se agotan las transacciones o los bloqueos

1b) Si T1 no eliminó los cambios prematuramente, no se adquirió ningún bloqueo. Cuando T2 se compromete, lo adquiere y lo libera y tiene éxito. Cuando T1 intenta comprometerse, advierte un conflicto y falla.

2) Consideremos el bloqueo pesimista, donde la lectura adquiere bloqueos compartidos y escribe bloqueos exclusivos.

La lectura por T1 adquiere un bloqueo compartido.

2a) Si T1 se enjuaga prematuramente, gira el bloqueo en un bloqueo exclusivo. La situación es similar a 1a).

2b) Si T1 no se lavó prematuramente, T1 mantiene un bloqueo compartido. Cuando T2 se compromete, intenta adquirir un bloqueo y bloqueos exclusivos. El sistema está bloqueado de nuevo .

Conclusión: está bien con el bloqueo optimista si no se producen descargas prematuras, que no puede controlar estrictamente.


mediante la entityManager.persist(e1); programática de la transacción después de entityManager.persist(e1); y antes de int age = beanB.methodB(); ?

public class Bean_A { Bean_B beanB; // Injected or whatever public void methodA() { EntityManager em = createEntityManager(); Entity e1 = // get from db e1.setName("Blah"); entityManager.persist(e1); em.getTransaction().commit(); int age = beanB.methodB(); } } public class Bean_B { //Note transaction @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodB() { // complex calc to calculate age } }

EDITAR : CMT

Si tiene CMT, aún puede comprometerse programáticamente, solo obtiene la Transacción del EJBContext . por ejemplo: http://geertschuring.wordpress.com/2008/10/07/how-to-use-bean-managed-transactions-with-ejb3-jpa-and-jta/

o puede agregar un @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodC() que haría e1.setName("Blah"); entityManager.persist(e1); e1.setName("Blah"); entityManager.persist(e1); , es decir, persistiría e1 en una transacción. entonces tu methodA() llamaría

methodC(); beanB.methodB();


Pasa la entidad y fusiona ...

Puede pasar su nueva entidad a methodB() y fusionarla con el nuevo EntityManager . Cuando el método regresa, actualiza tu entidad para ver los cambios:

public class Bean_A { Bean_B beanB; // Injected or whatever public void methodA() { Entity e1 = // get from db e1.setName("Blah"); entityManager.persist(e1); int age = beanB.methodB(e1); entityManager.refresh(e1); } } public class Bean_B { //Note transaction @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodB(Entity e1) { e1 = entityManager.merge(e1); // complex calc to calculate age } }

Tenga en cuenta que esto comprometerá a su entidad cuando la nueva transacción se cierre después del methodB .

... o guárdelo antes de llamar a methodB

Si usa el método anterior, la entidad se guarda por separado de su transacción principal, por lo que no Bean_A nada si lo guarda de Bean_A antes de llamar a methodB() :

public class Bean_A { Bean_B beanB; // Injected or whatever @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void createEntity() { Entity e1 = // get from db e1.setName("Blah"); entityManager.persist(e1); } public void methodA() { createEntity() int age = beanB.methodB(); } } public class Bean_B { //Note transaction @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodB() { // complex calc to calculate age } }