Распространение транзакции при вызове методов внутри одного EJB - PullRequest
0 голосов
/ 07 мая 2020

У меня есть простой класс EJB без сохранения состояния с двумя методами, оба без @TransactionAttribute. Первый метод сохраняет пиво и вызывает другой, который сохраняет другое пиво и выдает исключение.

@Stateless
public class EJBContainer {

    @PersistenceContext
    EntityManager em;

    public void testTransaction() {
        em.persist(getBeer("Corona"));
        try {
            saveAndThrowException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void saveAndThrowException() {
        em.persist(getBeer("Heineken"));
        throw new RuntimeException();
    }

    //getBeer() definition
}

Я предполагал, что оба метода будут использовать тип транзакции по умолчанию (обязательно), поэтому новая транзакция будет создана в первый метод, и он будет использоваться во втором методе. Таким образом, генерирование исключения должно откатить эту транзакцию, и EntityManager не будет сохранять пиво. Но оказалось, что оба пива были спасены. Где я ошибаюсь?

Когда я переместил второй метод в другой EJB без сохранения состояния, он работал так, как я предполагал - первый EJB создал новую транзакцию, второй использовал ее, исключение вызвало откат, и пиво не было сохранено.

EJB вызывается из этого клиента JAX-RS:

@ApplicationScoped 
public class BeerController { 

    @Inject 
    private EJBContainer ejbContainer; 

    @POST 
    public void addBeer() { 
        ejbContainer.testTransaction(); 
    } 
}

Ответы [ 2 ]

2 голосов
/ 08 мая 2020

Комментировать пока не могу, недостаточно репутации. Поэтому отдельный пост.

Ареус прав. То, как вы его вызываете, - это просто локальный вызов, поэтому все перехватчики вокруг saveAndThrowException, которые выполняются, когда вы вызываете его извне, сервер приложений опускает. Вы генерируете исключение и напрямую перехватываете его перед возвратом управления серверу приложений. Сервер приложений ничего не знает об исключении, er go все в порядке, и транзакция завершается.

2 голосов
/ 08 мая 2020

Поскольку вы используете представление без интерфейса, возможно, инъекция работает некорректно. Я бы попытался использовать аннотацию @EJB вместо @Inject.

@EJB 
private EJBContainer ejbContainer; 

@POST 
public void addBeer() { 
    ejbContainer.testTransaction(); 
}

Таким образом вы должны получить ожидаемые результаты. Но одно из ваших предположений не совсем верно. Когда вы вызываете saveAndThrowException из testTransacion, это не вызов EJB, это просто вызов локального метода, если вы объявили другой @TransactionAttribute для saveAndThrowException, если это не имеет значения.

Если вам нужно вызвать транзакционный метод из другого транзакционного метода вашего EJB, вы можете сделать это, добавив ссылку на сам EJB, и вызвать его, как если бы это был другой EJB:

@Stateless
public class EJBContainer {

    @PersistenceContext
    EntityManager em;

    @EJB
    private EJBContainer ejbContainer;

    public void testTransaction() {
        em.persist(getBeer("Corona"));
        try {
            ejbContainer.saveAndThrowException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void saveAndThrowException() {
        em.persist(getBeer("Heineken"));
        throw new RuntimeException();
    }

    //getBeer() definition
}
...