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 with createTransaction() instead, then rolling it back will have no influence on the enclosing transaction in methodA().

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.