【问题标题】:Is it possible to catch a runtime exception and throw an application exception instead in the same transaction ? (ejb)是否可以在同一事务中捕获运行时异常并引发应用程序异常? (ejb)
【发布时间】:2013-07-10 10:51:01
【问题描述】:

总结:

有人决定 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:没有事务暂停 调试 10-07 12:20:53,003 (Log4jLogStream.java:debug:81) -TX RequiresNew: 开始事务 org.apache.geronimo.transaction.manager.TransactionImpl@25ef757f 1 坚持1睡10后

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

DEBUG 10-07 12:21:03,009 (Log4jLogStream.java:debug:81) - 在 bcmc-core.be.awl.clearing.bcmc.core.utils.TestService001 上调用方法创建 DEBUG 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,标识为 null 调试 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.TransactionImpl@25ef757f 只得到回滚假 2

DEBUG 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。参数.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] ...

DEBUG 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 遇到了非应用程序异常: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] ...

第一个会话 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();
    }
}

【问题讨论】:

    标签: java ejb-3.0 openejb


    【解决方案1】:

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

    【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 这不是一个解决方案...这只是一个示例...您可以发明任何会在第二个 bean 中引发运行时异常的方法。
    • 知道了。所以在你的例子中,“BcmcDuplicateException”是 RuntimeException 的子类吗?否则,我认为它不应该回滚交易。要检查的另一件事是,第二个 bean 是否抛出了您未能捕获的另一种类型的 RuntimeException?这是一个带有类似问题和公认解决方案的线程:stackoverflow.com/questions/1701750/…
    • BcmcDuplicateException 是我想重新抛出的异常 (@ApplicationException)。我可以通过询问以下问题来简化我的问题:有人认为 EntityExistsException 是 RuntimeException 以避免强制开发人员捕获异常。避免这种异常回滚事务的方法是什么?如果您捕获 EntityExistsException,则该事务已被容器回滚...恕我直言 EntityExistsException 并不是真正的运行时异常...应该可以从此类异常中恢复...如何捕获并重新抛出 ApplicationExc ?
    • @ApplicationException(rollback = false) public class BcmcDuplicateException extends Exception It is not a RuntimeException ;-)
    猜你喜欢
    • 2012-11-02
    • 2012-04-04
    • 2013-11-11
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-05
    相关资源
    最近更新 更多