4.4.5.3.2. Чтение и изменение данных во вложенной транзакции

Рассмотрим сначала зависимую вложенную транзакцию, создаваемую через getTransaction():

void methodA() {
    Transaction tx = persistence.createTransaction();
    try {
        EntityManager em = persistence.getEntityManager();

        // (1) загружаем сущность, в которой name == "old name"
        Employee employee = em.find(Employee.class, id);
        assertEquals("old name", employee.getName());

        // (2) присваиваем новое значение полю
        employee.setName("name A");

        // (3) вызываем метод, создающий вложенную транзакцию
        methodB();

        // (8) здесь происходит коммит изменений в БД, и в ней
        //     окажется значение "name B"
        tx.commit();

    } finally {
        tx.end();
    }
}

void methodB() {
    Transaction tx = persistence.getTransaction();
    try {
        // (4) получаем тот же экземпляр EntityManager, что и methodA
        EntityManager em = persistence.getEntityManager();

        // (5) загружаем сущность с тем же идентификатором
        Employee employee = em.find(Employee.class, id);

        // (6) значение поля новое, т.к. мы работаем с тем же
        //     персистентным контекстом, и обращения к БД вообще
        //     не происходит
        assertEquals("name A", employee.getName());
        employee.setName("name B");

        // (7) в этот момент реально коммита не происходит
        tx.commit();
    } finally {
        tx.end();
    }
}

Теперь рассмотрим тот же самый пример с независимой вложенной транзакцией, создаваемой через createTransaction():

void methodA() {
    Transaction tx = persistence.createTransaction();
    try {
        EntityManager em = persistence.getEntityManager();

        // (1) загружаем сущность, в которой name == "old name"
        Employee employee = em.find(Employee.class, id);
        assertEquals("old name", employee.getName());

        // (2) присваиваем новое значение полю
        employee.setName("name A");

        // (3) вызываем метод, создающий вложенную транзакцию
        methodB();

        // (8) здесь возникнет исключение из-за оптимистичной блокировки
        //     и коммит не пройдет вообще
        tx.commit();

    } finally {
        tx.end();
    }
}

void methodB() {
    Transaction tx = persistence.createTransaction();
    try {
        // (4) создается новый экземпляр EntityManager, т.к. это
        //     новая транзакция
        EntityManager em = persistence.getEntityManager();

        // (5) загружаем сущность с тем же идентификатором
        Employee employee = em.find(Employee.class, id);

        // (6) значение поля старое, т.к. произошла загрузка из БД
        //     старого экземпляра сущности
        assertEquals("old name", employee.getName());

        employee.setName("name B");

        // (7) здесь происходит коммит изменений в БД, и в ней
        //     окажется значение "name B"
        tx.commit();

    } finally {
        tx.end();
    }
}

В последнем случае исключение в точке (8) возникнет, только если сущность является оптимистично блокируемой, т.е. если она реализует интерфейс Versioned.