Можно ли перехватить исключение во время выполнения и создать исключение для приложения в той же транзакции? (EJB)

Резюме:

Кто-то решил, что EntityExistsException является RuntimeException, чтобы избежать принуждения разработчика к перехвату исключения. Как избежать этого исключения для отката транзакции? Если вы перехватываете EntityExistsException, транзакция уже откатывается контейнером... imho EntityExistsException на самом деле не является исключением времени выполнения... Должно быть возможным восстановление после такого исключения... Как перехватить исключение времени выполнения и сбросить исключение приложения (проверено). Он был протестирован на 2 сеансовых бинах без сохранения состояния.

Подробно:

В моем примере у меня есть 2 сессионных компонента без сохранения состояния.

Первый сессионный компонент начнет новую транзакцию (REQUIRES_NEW). Объекты сохранят новую сущность. Затем вызывается второй сессионный компонент без сохранения состояния.

Второй сессионный компонент не запускает новую транзакцию (ОБЯЗАТЕЛЬНО или ОБЯЗАТЕЛЬНО), потому что он был вызван компонентом, который начал транзакцию. Второй сессионный компонент будет сохраняться во второй раз той же сущностью. JPA генерирует исключение javax.persistence.EntityExistsException, но на самом деле это исключение javax.ejb.EJBTransactionRolledbackException. (Это только пример, imho, любой метод, который сгенерирует RuntimeException, откатит транзакцию. Я нашел способ искусственно создать исключение для сущности, существующей...)

Я создал ApplicationException. Мы знаем, что по умолчанию ApplicationException не выполняет откат транзакции.

Во втором ejb я ловлю EJBTransactionRolledbackException (или EntityExistsException) и вместо этого выкидываю исключение приложения. Транзакция не должна откатываться!

Второй компонент присоединился к транзакции, и он влияет на транзакцию, потому что транзакция ВСЕГДА откатывается.

Есть ли способ избежать откат этой транзакции из-за RuntimeException (EntityExistsException)? Я перехватываю исключение EntityExistsException или EJBTransactionRolledbackException и перебрасываю исключение приложения.

ИМХО этого можно избежать, если вторая транзакция также требует новой транзакции (REQUIRES_NEW). Но я бы хотел избежать этого и оставить только 1 транзакцию... Вы можете мне помочь?

Вход из openejb:

первый сессионный компонент и первый метод

DEBUG 10-07 12:20:53,002 (Log4jLogStream.java:debug:81) -TX NotSupported: транзакция не приостанавливается DEBUG 10-07 12: 20: 53,002 (Log4jLogStream.java:debug:81) -TX ТребуетсяНовинка: транзакция отсутствует Приостановить DEBUG 10-07 12:20:53,003 (Log4jLogStream.java:debug:81) -TX Требуется Новое: Запущена транзакция org.apache.geronimo.transaction.manager.TransactionImpl@25e f757f1 после сохранения 1 спящего режима 10

второй сессионный компонент и второй метод

Отладка 10-07 12: 21: 03,009 (Log4jLogStream.java:debug:81) - метод вызова вызывать на bcmc-core.be.awl.clearing.bcmc.core.utils. TestService001 ДЕБАГ 10-07 12: 21: 03,011 (Log4jLogStream.java:debug:81) -конечный метод вызова create. Возвращаемое значение: proxy = be.awl.clearing.bcmc.core.utils. TestService001; deploy = bcmc-core.be.awl.clearing.bcmc.core.utils. TestService001; pk = null ОТЛАДКА 10-07 12:21: 03,012 (Log4jLogStream.java:debug:81) - вызывать метод writeToDatabase для bcmc-core.be.awl.clearing.bcmc.core.utils. TestService001 с идентификатором ноль DEBUG 10-07 12:21:03,014 (Log4jLogStream.java:debug: 81) -TX NotSupported: приостановленная транзакция org.apache.geronimo.transaction.manager.TransactionImpl@25e f757f DEBUG 10-07 12:21:03,014 (Log4jLogStream.java:debug:81) -TX NotSupported: возобновление транзакции org.apache. geronimo.transaction.manager.TransactionImpl@25e f757f получить откат только false 2

DEBUG 10-07 12:21:03,018 (Log4jLogStream.java:debug:85) - Бизнес-метод экземпляра компонента обнаружил системное исключение: другой объект с таким же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] javax.persistence.EntityExistsException: другой объект с таким же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] ...

DEBUG 10-07 12:21:03,020 (Log4jLogStream.java:debug:81) -конечный метод вызова writeToDatabase с исключением org.apache.openejb.core.transaction.TransactionRolledbackException: транзакция была помечена как откатная только потому, что компонент не обнаружил исключение -application:javax.persistence.EntityExistsException: другой объект с тем же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam # be.awl.clearing.bcmc. core.model.parameters.RepBcmcParamId@aed63e f8]

DEBUG 10-07 12:21:03,021 (Log4jLogStream.java:debug:85) - Бизнес-метод экземпляра компонента обнаружил системное исключение: транзакция была помечена как откатная только потому, что компонент обнаружил исключение, не связанное с приложением: javax.persistence. EntityExistsException: другой объект с тем же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] javax.ejb.EJBTransactionRolledbackException: транзакция была помечена как откатная только потому, что компонент обнаружил исключение, не связанное с приложением:javax.persistence.EntityExistsException: другой объект с тем же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] ... Вызывается:javax.persistence.EntityExistsException: другой объект с тем же идентификатором значение ва s, уже связанные с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] ...

DEBUG 10-07 12:21:03,022 (Log4jLogStream.java:debug:81) -TX Требуется новый: откат транзакции org.apache.geronimo.transaction.manager.TransactionImpl@25e f757f

DEBUG 10-07 12:21:03,024 (Log4jLogStream.java:debug:81) -TX ТребуетсяНовое: транзакция не возобновляется

DEBUG 10-07 12:21:03,024 (Log4jLogStream.java:debug:81) -конечный метод вызова writeToDatabase с исключением java.rmi.RemoteException: бин обнаружил исключение, не связанное с приложением; Вложенное исключение: javax.ejb.EJBTransactionRolledbackException: транзакция была помечена как откатная только потому, что компонент обнаружил исключение, не связанное с приложением:javax.persistence.EntityExistsException: другой объект с таким же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] Исключение в потоке "Thread-49" javax.ejb.EJBException: обнаружен бин исключение не в приложении; Вложенное исключение: javax.ejb.EJBTransactionRolledbackException: транзакция была помечена как откатная только потому, что компонент обнаружил исключение, не связанное с приложением:javax.persistence.EntityExistsException: другой объект с таким же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] ... Причина: javax.ejb.EJBTransactionRolledbackException: транзакция отмечена откат только потому, что компонент обнаружил исключение, не относящееся к приложению:javax.persistence.EntityExistsException: другой объект с тем же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam # be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63e f8]... Причина:javax.persistence.EntityExistsException: другой объект с таким же значением идентификатора уже был связан с сеансом: [be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@aed63ef8] ...

бин первого сеанса:

@Stateless(name = "bcmc-core.be.awl.clearing.bcmc.core.utils.TestService")
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class TestServiceImpl implements TestService {
@PersistenceContext(unitName = "bcmc-core")
private EntityManager em;       
@Override
public void writeToDatabase() {         
    try {

        RepBcmcParam bcmcParam = new RepBcmcParam();
        RepBcmcParamId bcmcParamId = new RepBcmcParamId();
        bcmcParamId.setProcname("procname");
        bcmcParamId.setParid("parid");
        Date date = new Date();
        bcmcParamId.setDtbeg(date);
        bcmcParam.setId(bcmcParamId);
        bcmcParam.setParval("parval");
        System.out.println("1");
        em.persist(bcmcParam);
        System.out.println("after persist 1 sleeping 10");
        try {
            Thread.currentThread().sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        TestService001 testService001 = net.atos.xa.resourcelocator.ResourceLocator.lookup(TestService001.class);
        testService001.writeToDatabase(date);
        System.out.println("after write to database 2 sleeping 10");
        try {
            Thread.currentThread().sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }           
    } catch(BcmcDuplicateException be) {
        System.out.println("facade");
    }                   
}

бин второй сессии:

@Stateless(name = "bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001")
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class TestService001Impl implements TestService001 { 
@PersistenceContext(unitName = "bcmc-core")
private EntityManager em;   
private static final Logger LOGGER = Logger.getLogger(Constants.APP_NAME);  
@Resource
private SessionContext context;
    try {           
        System.out.println("get rollback only " + context.getRollbackOnly());
        RepBcmcParam bcmcParam = new RepBcmcParam();
        RepBcmcParamId bcmcParamId = new RepBcmcParamId();
        bcmcParamId.setProcname("procname");
        bcmcParamId.setParid("parid");
        bcmcParamId.setDtbeg(date);         
        //bcmcParamId.setDtbeg(new Date());
        bcmcParam.setId(bcmcParamId);
        bcmcParam.setParval("parval");
        System.out.println("2");
        em.persist(bcmcParam);
        System.out.println("get rollback only " + context.getRollbackOnly());
        try {
            System.out.println("1 sec");
            Thread.currentThread().sleep(1000);             
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }           

    } catch(EJBTransactionRolledbackException e) {
        System.out.println("get rollback only " + context.getRollbackOnly());
        throw new BcmcDuplicateException();
    }
}

2 ответа

Решение

Клиент не может перехватить системное исключение (не-ApplicationException RuntimeException) и преобразовать его в исключение приложения до отката транзакции. Единственный вариант - это каким-то образом настроить EJB: либо изменить его, чтобы вначале выдать исключение приложения, либо добавить перехватчик, выполняющий перехват / повторную передачу, либо изменить системное исключение на исключение приложения (посредством аннотации или XML).).

Сделав шаг назад... зачем вам второму бину сохранять тот же объект, если первый уже делает... и это тоже в той же транзакции? Может быть, вам следует переосмыслить оригинальное решение, прежде чем связываться с тем, как контейнер обрабатывает это исключение?

Возвращаясь к вашей проблеме, является ли ваше исключение Application подклассом RuntimeException случайно? Кроме того, если вам нужно сделать это таким образом, можете ли вы вручную зафиксировать транзакцию в этом исключении?

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