Начало / конец транзакции в отдельных методах EJB

Я разработал типичное корпоративное приложение, которое отвечает за предоставление клиента сторонней системе. Эта система имеет ограничение, что только один поток может работать на определенного клиента. Поэтому мы добавили простой механизм блокировки, который состоит из @Singleton который содержит Set Идентификатор клиента в настоящее время выполняется. Всякий раз, когда приходит новый запрос на предоставление, он сначала проверяет это Set, Если присутствует cusotomerId, он ждет, в противном случае он добавляет его к Set и переходит в обработку.

Недавно было решено, что это приложение будет развернуто в кластере, что означает, что этот подход блокировки больше не действует. Мы придумали решение использовать БД для блокировки. Мы создали таблицу с одним столбцом, который будет содержать customerIds (он также имеет уникальное ограничение). Когда приходит новый запрос обеспечения, мы запускаем транзакцию и пытаемся заблокировать строку с помощью customerId с помощью SELECT FOR UPDATE (если customerId еще не существует, мы его вставляем). После этого мы начинаем инициализацию клиента и, когда закончим, совершаем транзакцию. Концепция работает, но у меня есть проблемы с транзакциями. В настоящее время у нас есть класс CustomerLock с add() а также remove() методы, которые заботятся о добавлении и удалении customerIds из Set, Я хотел преобразовать этот класс в EJB без состояния с транзакциями, управляемыми компонентами. add() метод запустит транзакцию и заблокирует строку remove() Метод совершит транзакцию и разблокирует строку. Но кажется, что начало и конец транзакции должны происходить одним и тем же способом. Есть ли способ использовать описанный мной подход или мне нужно изменить логику, чтобы транзакция начиналась и заканчивалась одним и тем же методом?

Класс CustomerLock:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CustomerLock {

    @Resource
    private UserTransaction tx;

    public void add(String customerId) throws Exception {
        try {
            tx.begin();
            dsApi.lock()
        } catch (Exception e) {
            throw e;
        }
    }

    public void remove(String customerId) throws Exception {
        try {
            tx.commit();
        } catch (Exception e) {
            throw e
        }
    }
}

Выдержка из класса CustomerProvisioner:

public abstract class CustomerProvisioner {

    ...

    public void execute(String customerId) {
        try {
            customerLock.add(customerId);

            processing....

            customerLock.remove(customerId);
        } catch (Exception e) {
            logger.error("Error", e);
        }
    }

    ...

}

Класс StandardCustomerProvisioner:

@Stateless
public class StandardCustomerProvisioner extends CustomerProvisioner {

    ...

    public void provision(String customerId) {
        // do some business logic
        super.execute(customerId);
    }
}

1 ответ

Решение

Как заметил @Gimby, вы не должны смешивать транзакции, управляемые контейнером и бином. Поскольку ваш StandardCustomerProvisioner не имеет аннотации типа "@TransactionManagement(TransactionManagementType.BEAN)" - он использует транзакции, управляемые контейнером, и по умолчанию ТРЕБУЕТСЯ.

У вас есть 2 варианта, чтобы заставить его работать:

1) Удалить "@TransactionManagement (TransactionManagementType.BEAN)" с помощью вызовов UserTransaction и запустить CMT

2) Добавьте эту аннотацию ("@TransactionManagement(TransactionManagementType.BEAN)") в StandardCustomerProvisioner и используйте вызовы разметки транзакций из этого метода, чтобы все вызываемые методы использовали один и тот же транзакционный контекст. Разметка звонков из CustomerLock должна быть удалена в любом случае.

Другие вопросы по тегам