@Stateless和@Asynchronous EJB之间的JPA事务处理

时间:2018-10-13 21:44:49

标签: jpa queue ejb weblogic ibm-mq

我有一个无状态EJB,它将数据插入数据库,立即发送响应,并在最后一步中调用异步EJB。异步EJB可以运行很长时间(我的意思是5到10分钟,这比JPA事务超时更长)。异步ejb需要读取(并对其进行处理)与无状态EJB所保留的记录树相同的记录树(仅读取)。

似乎异步bean在状态EJB提交或插入(JPA)之前尝试读取记录树,因此异步bean无法看到记录树。

无状态EJB:

@Stateless
public class ReceiverBean {

    public void receiverOfIncomingRequest(data) {
        long id = persistRequest(data);
        sendResponseToJmsBasedOnIncomingData(data);
        processorAsyncBean.calculate(id);
        }
    }
}

异步EJB:

@Stateless
public class ProcessorAsyncBean {

    @Asynchronous
    public void calculate(id) {
        Data data = dao.getById(id); <- DATA IS ALLWAYS NULL HERE!

        // the following method going to send
        // data to external system via internet (TCP/IP)
        Result result = doSomethingForLongWithData(data);

        updateData(id, result);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void updateData(id, result) {
        dao.update(id, result);
}

也许我可以使用JMS队列向处理器bean发送ID为ID的信号,而不用调用asyc ejb(消息驱动的bean从数据库读取数据),但是如果可能的话,我想避免这种情况。

另一种解决方案可以是将整个记录树作为一个分离的JPA对象传递给处理器异步EJB,而不是从数据库中读回数据。

我可以以某种方式使异步EJB在这种结构中正常工作吗?

-更新-

我正在考虑使用Weblogic JMS。这里还有另一个问题。如果负载很大,当队列中有10万个或更多数据(这是正常的)并且没有Internet连接时,队列中的所有数据都将失败。如果在通过Internet(通过doSomethingForLongWithData方法发送数据的过程中出现该异常(或任何异常),则数据将基于Weblogic的redelivery-limitrepetitaion设置回滚到原始队列。此回滚事件将在托管服务器上的Weblogic上生成100000或更多线程,以管理重新交付。大量新的后台进程可以杀死服务器,或至少使服务器变慢。

我也可以使用IBM MQ,因为我们拥有MQ基础结构。 MQ在Weblogic服务器上没有这种影响,但是MQ没有重新交付限制和延迟功能。因此,在发生错误(回滚)的情况下,消息将立即再次立即出现在MQ上,而不会出现延迟,因此我建立了手动铣床。我认为Thread.sleep()条件下的catch不是EE应用程序中的解决方案...

2 个答案:

答案 0 :(得分:2)

  

似乎异步bean在状态EJB提交或插入(JPA)之前尝试读取记录树,因此异步bean无法看到记录树。

这是bean管理的事务的预期行为。您正在使用自己的事务上下文从EJB启动异步EJB。异步EJB从不使用调用者事务上下文(请参阅EJB规范4.5.3)。 只要您不使用具有持久性的“读取未提交”的事务隔离级别,就不会看到来自调用方的仍未提交的数据。

您必须考虑以下情况:什么时候取消提交作业(例如,应用程序服务器关闭或异常中止)。以下计算和更新是否至关重要?如果执行不成功或没有被调用,异步过程是否可以恢复?

您可以考虑使用bean管理的事务,在调用异步EJB之前提交。或者,您可以使用新的transactin上下文将数据更新委派给另一个EJB。这将在调用异步EJB之前提交。通常,对于不重要的内容,丢失或失败的情况,这都是可以的。

将持久性和事务性JMS消息与死信队列一起使用,具有可靠地计算和更新的优点,即使在过程之间停止/启动应用程序服务器或在处理过程中出现时间错误的情况下。

答案 1 :(得分:0)

您只需要在带有事务标记的方法旁边调用异步方法,以便在提交事务时使用。

例如,receiverOfIncomingRequest()方法的调用者可以添加

processorAsyncBean.calculate(id);

在它旁边打电话。

更新:扩展示例

CallerMDB

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public void onMessage(Message message) {
    long id = receiverBean.receiverOfIncomingRequest(data);
    processorAsyncBean.calculate(id);
}

ReceiverBean

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public long receiverOfIncomingRequest(data) {
    long id = persistRequest(data);
    sendResponseToJmsBasedOnIncomingData(data);
    return id;        
}