如何在Java EE环境中使用JMS和JPA处理事务回滚?

时间:2015-05-06 16:32:34

标签: java jpa transactions jms weblogic

CMT MDB的默认回滚行为是将邮件返回到目标,以便再次处理。

即使事务被回滚,是否可以避免重新传递托管MDB处理的消息? (或者可以配置容器处理的确认行为)。

到目前为止,我想出了以下替代方案:

  1. 将业务事务与消息事务隔离 - 我可以在业务方法上使用TransactionAttributeType.REQUIRES_NEW,但它会创建业务方案 事件可以被处理两次,因为消息可能是 商业交易成功后不予承认。
  2. 使用BMT - 与上述问题相同,因为事务将与邮件事务分开。
  3. 使用JMS服务器专有配置处理传递失败 - 如果可能,我希望将此逻辑保留在应用程序中。 此外,自从WebLogic默认以来,我必须为所有队列处理它 config将永远重新发送消息。
  4. 阅读this tutorial后,我仍然不确定如何解决这个问题,但到目前为止,使用专有WebLogic Console控制邮件传递失败似乎是正确的选择。在这种情况下,为队列上的重新传递设置限制 - 例如:3次尝试。它会产生处理开销,因为无效的业务事件可能会失败3次,但我可以保证系统的完整性。

    你们有什么想法?

    详细

    我有一个与业务事务集成的MDB,它使用JPA(WebLogic 10.3.6中的EclipseLink)。一切都在运行CMT并且事务是分发的。事务和消息确认由容器控制。

    如果JPA提供程序中发生异常(例如:非空列的null值),则正在重新传递消息,因为提供程序正在回滚事务并且消息未得到确认。如果我捕获异常并不重要,EclipseLink无论如何都会回滚事务。我知道这是JPA的正确行为。

    此外,使用MessageDrivenContext.getRollbackOnly()会返回 false 。我希望它是真的。

    如果我使用TransactionAttributeType.REQUIRES_NEW执行我的业务方法,则回滚事务并且不重新传递消息但是消息处理事务将是独立的,这也是不希望的。我确实建立了一个JDBC存储来将消息保存在数据库中。

    我会留下一些虚拟课来说明我的观点。

    MDB消息处理

    在提取有效负载后,我将其转发到会话bean以处理持久性逻辑。

    public void onMessage(Message message) {
    
        try {
            // Extract the payload
            TextMessage txtMsg = (TextMessage) message;
            String employeeName = txtMsg.getText();
    
            // Call service
            service.createEmployee(employeeName);
    
        } catch (Exception e) {
            e.printStackTrace();            
        } finally {
            // When the JPA provider rollbacks back the transaction, this value
            // is still "false"
            log.info(String.format("Rollback only: [%s]", mdContext.getRollbackOnly()));
        }
    }
    

    强制向JPA提供程序发送例外

    null留在非空字段中来强制执行错误。

    // Message and business will run in the same transaction
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void createEmployee(String name) {
    
        Employee employee = new Employee();
        employee.setName(null); // Null value to force constraint error
    
        try {
            // This part triggers the exception within the JPA provider, and the
            // Java EE transaction is rolledback and forces the JMS message to be
            // redelivered.
            em.persist(employee);
    
        } catch (Exception e) {
            // Capturing the exception does not affect the rollback behavior
            e.printStackTrace();
        }
    }
    

    这是EclipseLink抛出的错误。它包含在RuntimeException中,因此它是一个系统异常,事务将回滚。

    javax.ejb.EJBTransactionRolledbackException: EJB Exception: ; nested exception is: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.DatabaseException
    

2 个答案:

答案 0 :(得分:2)

拥有2 tx的恕我直言,正是您所寻找的。

有一个事务处理由EJB容器启动的MDB,然后在MDB中,您在使用Transaction.REQUIRES_NEW注释的无状态会话Bean(SLSB)中调用业务方法,因此您的业务逻辑处理数据库在其自己的孤立事务中被隔离。

当您从SLSB的方法返回时,无论您的业务逻辑方法发生什么,MDB tx将由MDB的onMessage()方法结束时的容器提交,并且消息将被消耗/从Q中移除。重新处理的可能性为零......

唯一的例外是如果MDB本身出现问题,但您可以轻松处理它

干杯

答案 1 :(得分:0)

您使用的是哪个Q提供商?

似乎你已经涵盖了可能性

恕我直言最好的解决方案是在单独的外观SLSB中使用JPA隔离您的业务逻辑,并使用“事务新”属性并仍然使用CMT语义,因此如果此SLSB中发生了错误,您仍然可以提交tx其中onMessage tx由Application Server自动启动

这听起来类似于你的第一个解决方案。

对我来说,第二个选择是配置Q管理器,这样当消息在Q上“重新传送”一定次数(即由于tx回滚而返回Q)时,消息被移动到另一个Q(死信队列()并在此Q ...上配置一些监控/警报系统

Denis(JMSToolBox的作者)