3.4.5.3. Examples of Transactions Interaction
- Rollback of a Nested Transaction
-
If a nested transaction was created via
getTransaction()
and rolled back, then commit of the enclosing transaction will be impossible. For example:void methodA() { Transaction tx = persistence.createTransaction(); try { methodB(); (1) tx.commit(); (4) } finally { tx.end(); } } void methodB() { Transaction tx = persistence.getTransaction(); try { tx.commit(); (2) } catch (Exception e) { return; (3) } finally { tx.end(); } }
1 calling a method creating a nested transaction 2 let us assume the exception occurs here 3 handle it and exit 4 at this point an exception will be thrown, because transaction is marked as rollback only If the transaction in
methodB()
is created withcreateTransaction()
instead, then rolling it back will have no influence on the enclosing transaction inmethodA()
. - Reading and Modifying Data in a Nested Transaction
-
Let us first have a look at a dependent nested transaction created using
getTransaction()
:void methodA() { Transaction tx = persistence.createTransaction(); try { EntityManager em = persistence.getEntityManager(); Employee employee = em.find(Employee.class, id); (1) assertEquals("old name", employee.getName()); employee.setName("name A"); (2) methodB(); (3) tx.commit(); (8) } finally { tx.end(); } } void methodB() { Transaction tx = persistence.getTransaction(); try { EntityManager em = persistence.getEntityManager(); (4) Employee employee = em.find(Employee.class, id); (5) assertEquals("name A", employee.getName()); (6) employee.setName("name B"); tx.commit(); (7) } finally { tx.end(); } }
1 loading an entity with name == "old name" 2 setting new value to the field 3 calling a method creating a nested transaction 4 retrieving the same instance of EntityManager as methodA 5 loading an entity with the same identifier 6 the field value is the new one since we are working with the same persistent context, and there are no calls to DB at all 7 no actual commit is done at this point 8 the changes are committed to DB, and it will contain "name B" Now, let us have a look at the same example with an independent nested transaction created with
createTransaction()
:void methodA() { Transaction tx = persistence.createTransaction(); try { EntityManager em = persistence.getEntityManager(); Employee employee = em.find(Employee.class, id); (1) assertEquals("old name", employee.getName()); employee.setName("name A"); (2) methodB(); (3) tx.commit(); (8) } finally { tx.end(); } } void methodB() { Transaction tx = persistence.createTransaction(); try { EntityManager em = persistence.getEntityManager(); (4) Employee employee = em.find(Employee.class, id); (5) assertEquals("old name", employee.getName()); (6) employee.setName("name B"); (7) tx.commit(); } finally { tx.end(); } }
1 loading an entity with name == "old name" 2 setting new value to the field 3 calling a method creating a nested transaction 4 creating a new instance of EntityManager, as this is a new transaction 5 loading an entity with the same identifier 6 the field value is old because an old instance of the entity has been loaded from DB 7 the changes are committed to DB, and the value of "name B" will now be in DB 8 an exception occurs due to optimistic locking and commit will fail In the last example, the exception at point (8) will only occur if the entity supports optimistic locking, i.e. if it implements
Versioned
interface.