ApplicationException - Java - Hibernate - откат связан - PullRequest
2 голосов
/ 08 января 2010

Мой вопрос связан с транзакциями и исключениями

Требования:

У меня есть 10 записей для вставки в таблицу базы данных. И после вставки каждой записи я вставляю данные в другую таблицу. Поэтому, если вставка во вторую таблицу не удалась, я хочу откатить эту запись.

Ex. Скажем, обрабатывать денежные переводы (с одного счета на счет) для 10 человек одновременно.

псевдокод: ------------- Запуск метода EJB

for(int i = 0; i < TransferRecords.length; i++)
{
    try
    {
          //Deduct cash from TransferRecord.accountFrom --- Includes use of Hibernate Session
          //Add cash in TransferRecord.accountTo -- Includes use of Hibernate Session
     } catch(AppException exception)
     {
            //Rollback the transaction only for this particular transfer (i)
            // But here when I go for next record it says session is closed
     }
}

--------- Конец метода EJB

Здесь AppException создается с аннотацией @ApplicaitonException (rollback = true).

Функциональность, которую мы хотим: даже если транзакция завершится неудачно для TransferRecord (скажем, 2), я хочу, чтобы данные были зафиксированы для записи 0, записи 1, записи 3, записи 4 (и т. Д.), Но не для записи 2)

Но проблема здесь в следующем: когда происходит сбой TransferRecord 2 и когда я перехожу на TransferRecord 3, я получаю ошибку «Session Closed».

Мои сомнения: 1. Это правильный подход? или я должен запустить цикл for (для каждого TransferRecord) за пределами EJB 2. Как я могу убедиться, что сессия не закрыта, но транзакция откатывается (только для этой конкретной неудачной транзакции)

Заранее спасибо.

Я использую EJB3, Hibernate 3.x, Jboss 4.2.x и использую транзакцию, управляемую контейнером.

Ответы [ 3 ]

2 голосов
/ 08 января 2010

Это правильный подход?

Нет, с CMT ваш метод является вашей транзакционной единицей. Так что здесь все ваши TransferRecord и обрабатываются в одной и той же уникальной транзакции.

Кстати, как вы откатываете транзакцию? Вы пропагандируете RuntimeException или звоните setRollbackOnly()? Мне просто любопытно.

Или я должен запустить цикл for (для каждого TransferRecord) вне EJB?

Почему снаружи? Ничто не заставляет вас делать это. Если вы хотите обработать каждый TransferRecord в своей собственной транзакции, вы должны передать их другому EJB-методу (код ниже вдохновлен этим ответом ):

// supposing processRecords is defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessLocal1 myBean;

    public void processRecords(List<TransferRecord> objs) {
        // No transactional stuff so no need for a transaction here
        for(Object obj : objs) {
            this.myBean.process(obj);
        }
    }

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(TransferRecord transferRecord) {
        // Transactional stuff performed in its own transaction
        // ...
    }
}

Как я могу убедиться, что сеанс не закрыт, но транзакция откатывается (только для этой конкретной неудачной транзакции)

Я думаю, что покрыл эту часть.

1 голос
/ 08 января 2010

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

0 голосов
/ 08 января 2010

Я думаю, что вы можете создать две отдельные транзакции, сначала для TransferRecord (1) (выполнение коммита, когда все в порядке), а затем запуск другого TX для всего TransferRecord (i + 1).

Другой подход - использование точек сохранения, возможность отката и отбрасывания всего, что прошло после этой точки сохранения (но мне больше нравится предпочесть первый подход).

Привет.

...