EJB TransactionRequiredException в GlassFish 2.1 - PullRequest
0 голосов
/ 24 июля 2011

В моем Stateful бине у меня есть следующие строки:

@Stateful(mappedName = "ejb/RegistrationBean")
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 30)
@TransactionManagement(value=TransactionManagementType.CONTAINER)
public class RegistrationStateful implements RegistrationStatefulRemote {

    @PersistenceContext
    EntityManager em;

    private List<Event> reservedSessions = new ArrayList<Event>();
    private boolean madePayment = false;

    ...
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    private void cancelReservation() {
        if (reservedSessions.size() != 0) {
            Teacher theTeacher;
            for (Event session : reservedSessions) {
                if ((theTeacher = session.teacher) == null) theTeacher = bestTeacher.teacher;
                theTeacher = em.merge(theTeacher) //The exception is thrown here

                //Make changes to theTeacher

                em.flush(); //The exception is also thrown here
            }

            //Clear the reservedSessions list
            reservedSessions.clear();
        }
    }

    @Remove
    public void endRegistration() {}

    @PreDestroy
    public void destroy() {
        //Cancel outstanding reservations if payment has not been made
        if (!madePayment) cancelReservation(); 
    }
}

Строка em.merge(someEntity) выбрасывает TransactionRequiredException.Может ли кто-нибудь сказать мне, почему это происходит?Я думал, что с TransactionAttribute.REQUIRED транзакция будет автоматически создана, если она не активна.Я пытался использовать em.joinTransaction(), но выдает тот же Exception.Я новичок в этой сделке вещь.Буду очень признателен, если кто-нибудь сможет мне это объяснить.

ОБНОВЛЕНИЕ: Я хотел бы добавить немного больше информации

Бин Stateful на самом деле также имеет следующую функцию:

@TransactionAttribute(TransactionAttributeType.REQUIRED)
private void reserveSession(List<Event> sessions) throws ReservationException {
    //Reserve the sessions
    Teacher theTeacher;

    for (Event session : sessions) {
        if ((theTeacher = session.teacher) == null) theTeacher = bestTeacher.teacher;
        theTeacher = em.merge(theTeacher);

        //Make changes to theTeacher            

        em.flush();
    }   
}

Порядок следующий: пользователь сообщает мне свое свободное время, и я оставляю за ним несколько мест.После этого я показываю ему его зарезервированные места, и он может выбрать оплату или отменить бронирование.

Функция reserved() работала отлично, как и ожидалось, но cancelReservation() - нет.

ОБНОВЛЕНИЕ 2: Я устранил проблему вчера вечером, закомментировав строки "@TransactionAttribute (TransactionAttributeType.REQUIRED)", "em.merge (theTeacher)" и "em.flush ()" в "Функция cancelReservation () ".Результат идеален.Было бы безопасно, если бы я отрезал эти строки?Я боялся, что получу исключение «отдельная сущность», когда я вначале использовал «em.merge ()».

1 Ответ

0 голосов
/ 25 июля 2011

Единственное, что приходит на ум (если вы извините за каламбур), это то, что если вы вызываете cancelReservation() из другого метода внутри компонента, то я не уверен, что аннотация транзакции будет соблюдена.В конечном итоге аннотация работает, вызывая перехватчик, и я полагаю, что перехватчики применяются только к вызовам между различными классами (это то, что я действительно должен проверить).

Итак, если у вас есть нетранзакционный метод в бинекоторый вызывает транзакционный метод, тогда транзакция не будет запущена при вызове транзакционного метода.

Я могу быть совершенно неправ в этом.Я пойду и прочитаю спецификацию и вернусь к вам.

РЕДАКТИРОВАТЬ: Я прочитал спецификацию, и она напомнила мне, что за зона бедствия - спецификации J2EE.Ужасающий.Однако раздел о транзакциях, по-видимому, подразумевает, что атрибуты транзакции применяются только к вызовам, сделанным в бизнес-интерфейсе EJB.Я считаю, что вызовы от одного метода к другому внутри компонента не считаются проходящими через бизнес-интерфейс, даже когда вызываемый метод является частью этого интерфейса.Поэтому вы не ожидаете, что они привлекут транзакции.

Что-то, что вы могли бы попробовать, это направить их через интерфейс;нет хорошего способа сделать это, но вы должны иметь возможность внедрить собственную ссылку на бизнес-интерфейс следующим образом:

public class RegistrationStateful implements RegistrationStatefulRemote {
    @EJB
    private RegistrationStatefulRemote self;

Затем вы можете изменить свой метод @PreDestroy, чтобы он выглядел так:

@PreDestroy
public void destroy() {
    self.cancelReservation();
}

И я считаю, что это должно считаться обычным вызовом бизнес-интерфейса с транзакциями и т. Д.

На самом деле я никогда не пробовал этого, так что это может быть полным мусором.Если вы попробуете, дайте мне знать, как это работает!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...