0

概括 :

有人决定 EntityExistsException 是 RuntimeException 以避免强制开发人员捕获异常。避免这种异常回滚事务的方法是什么?如果您捕获 EntityExistsException,则该事务已被容器回滚...恕我直言 EntityExistsException 并不是真正的运行时异常...应该可以从此类异常中恢复...如何捕获运行时异常和重新抛出应用程序异常(选中)。它在 2 个无状态会话 bean 中进行了测试。

详细说明:

在我的示例中,我有 2 个无状态会话 bean。

第一个会话 bean 将启动一个新事务 (REQUIRES_NEW) bean 将保持一个新实体。然后调用第二个无状态会话 bean。

第二个会话 bean 不会启动新事务(MANDATORY 或 REQUIRED),因为它是由启动事务的 bean 调用的。第二个会话 bean 将第二次保持相同的实体。JPA 抛出 javax.persistence.EntityExistsException 但实际抛出的是 javax.ejb.EJBTransactionRolledbackException。(这只是一个例子,恕我直言,任何会抛出 RuntimeException 的方法都会回滚事务。我找到了一种人工产生实体存在异常的方法......)

我创建了一个 ApplicationException。我们知道默认情况下 ApplicationException 不会回滚事务。

在第二个 ejb 中,我捕获了 EJBTransactionRolledbackException(或 EntityExistsException),然后抛出了一个应用程序异常。事务不应该回滚!

第二个 bean 加入了事务,它对事务有影响,因为事务总是回滚。

有没有办法避免由于 RuntimeException (EntityExistsException) 而回滚该事务?我捕获了 EntityExistsException 或 EJBTransactionRolledbackException 并重新抛出了一个应用程序异常。

恕我直言,如果第二笔交易也需要新交易(REQUIRES_NEW),则可以避免这种情况。但我想避免这种情况,只保留一笔交易……你能帮我吗?

从 openejb 记录:

第一个会话 bean 和第一个方法

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 RequiresNew: 没有事务暂停 DEBUG 10-07 12:20:53,003 (Log4jLogStream.java:debug:81) -TX RequiresNew: 在坚持 1 睡眠 10 后启动事务 org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f 1

第二个会话 bean 和第二种方法

调试 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) - 完成调用方法创建。返回值:proxy=be.awl.clearing.bcmc.core.utils.TestService001;deployment=bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001;pk=null DEBUG 10-07 12:21: 03,012 (Log4jLogStream.java:debug:81) - 在 bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001 上调用方法 writeToDatabase,标识为空 DEBUG 10-07 12:21:03,014 (Log4jLogStream.java:debug :81)-TX NotSupported:暂停事务 org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f 调试 10-07 12:21:03,014(Log4jLogStream.java:debug:81)-TX NotSupported:恢复事务 org.apache。 geronimo.transaction.manager。

调试 10-07 12:21:03,018 (Log4jLogStream.java:debug:85) - bean 实例业务方法遇到系统异常:具有相同标识符值的不同对象已与会话关联:[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:事务被标记回滚只是因为 bean 遇到了一个非-应用程序异常: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,021 (Log4jLogStream.java:debug:85) - bean 实例业务方法遇到系统异常:事务被标记回滚只是因为 bean 遇到了非应用程序异常:javax.persistence。 EntityExistsException:具有相同标识符值的不同对象已与会话关联:[be.awl.clearing.bcmc.core.model.parameters.RepBcmcParam#be.awl.clearing.bcmc.core.model.parameters.RepBcmcParamId@ aed63ef8] javax.ejb.EJBTransactionRolledbackException:事务被标记为回滚只是因为 bean 遇到了非应用程序异常:javax.persistence.EntityExistsException:具有相同标识符值的不同对象已与会话关联:[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] ...

调试 10-07 12:21:03,022 (Log4jLogStream.java:debug:81) -TX RequiresNew:回滚事务 org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f

DEBUG 10-07 12:21:03,024 (Log4jLogStream.java:debug:81) -TX RequiresNew:没有要恢复的事务

DEBUG 10-07 12:21:03,024 (Log4jLogStream.java:debug:81) - 完成调用 writeToDatabase 方法并出现异常 java.rmi.RemoteException:bean 遇到非应用程序异常;嵌套异常是:javax.ejb.EJBTransactionRolledbackException:事务被标记回滚只是因为 bean 遇到了非应用程序异常: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:遇到的 bean非应用异常;嵌套异常是:javax.ejb.EJBTransactionRolledbackException:

第一个会话 bean:

@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");
    }                   
}

第二个会话 bean:

@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();
    }
}
4

2 回答 2

1

客户端无法在事务回滚之前捕获系统异常(非@ApplicationException RuntimeException)并将其转换为应用程序异常。唯一的选择是以某种方式调整 EJB:要么将其更改为首先抛出应用程序异常,添加一个执行 catch/rethrow 的拦截器,要么将系统异常更改为应用程序异常(通过注释或 XML )。

于 2013-07-12T10:50:45.730 回答
0

退后一步.. 如果第一个 bean 已经存在,为什么要让第二个 bean 持久化同一个对象.. 并且在同一个事务中也是如此?也许您应该在弄乱容器处理此异常的方式之前重新考虑原始解决方案?

回到您的问题,您的 Application 异常是否是 RuntimeException 的子类?另外,如果你必须这样做,你可以在这个异常情况下手动提交事务吗?

于 2013-07-10T12:24:04.193 回答