4.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 { // (1) calling a method creating a nested transaction methodB(); // (4) at this point an exception will be thrown, because transaction // is marked as rollback only tx.commit(); } finally { tx.end(); } } void methodB() { Transaction tx = persistence.getTransaction(); try { // (2) let us assume the exception occurs here tx.commit(); } catch (Exception e) { // (3) handle it and exit return; } finally { tx.end(); } }
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(); // (1) loading an entity with name == "old name" Employee employee = em.find(Employee.class, id); assertEquals("old name", employee.getName()); // (2) setting new value to the field employee.setName("name A"); // (3) calling a method creating a nested transaction methodB(); // (8) the changes are committed to DB, and // it will contain "name B" tx.commit(); } finally { tx.end(); } } void methodB() { Transaction tx = persistence.getTransaction(); try { // (4) retrieving the same instance of EntityManager as methodA EntityManager em = persistence.getEntityManager(); // (5) loading an entity with the same identifier Employee employee = em.find(Employee.class, id); // (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 assertEquals("name A", employee.getName()); employee.setName("name B"); // (7) no actual commit is done at this point tx.commit(); } finally { tx.end(); } }
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(); // (1) loading an entity with name == "old name" Employee employee = em.find(Employee.class, id); assertEquals("old name", employee.getName()); // (2) setting new value to the field employee.setName("name A"); // (3) calling a method creating a nested transaction methodB(); // (8) an exception occurs due to optimistic locking // and commit will fail tx.commit(); } finally { tx.end(); } } void methodB() { Transaction tx = persistence.createTransaction(); try { // (4) creating a new instance of EntityManager, // as this is a new transaction EntityManager em = persistence.getEntityManager(); // (5) loading an entity with the same identifier Employee employee = em.find(Employee.class, id); // (6) the field value is old because an old instance of the entity // has been loaded from DB assertEquals("old name", employee.getName()); employee.setName("name B"); // (7) the changes are commited to DB, and the value of // "name B" will now be in DB tx.commit(); } finally { tx.end(); } }
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.