在单独的EJB方法中启动/结束事务

时间:2017-01-05 14:33:19

标签: java database ejb cluster-computing bean-managed-transactions

我开发了一个典型的企业应用程序,负责将客户配置到第三方系统。该系统有一个限制,即只有一个线程可以对某个客户起作用。因此,我们添加了一个由@Singleton组成的简单锁定机制,其中包含当前正在进行的Set个customerId。每当有新的请求进行配置时,它首先检查此Set。如果cusotomerId存在,则等待,否则将其添加到Set并进入处理。

最近决定将此应用程序部署在集群中,这意味着此锁定方法不再有效。我们想出了一个使用DB进行锁定的解决方案。我们创建了一个包含customerIds的单列表(它还有一个唯一约束)。当新的供应请求到来时,我们启动一个事务并尝试用SELECT FOR UPDATE的customerId锁定行(如果customerId尚不存在,我们将其插入)。之后我们开始配置客户,完成后我们提交交易。 概念有效,但我的交易有问题。目前,我们有一个CustomerLock类,其add()remove()方法负责从Set添加和删除customerIds。我想将此类转换为具有bean管理事务的无状态EJB。 add()方法将启动事务并锁定行,而remove()方法将提交事务,从而解锁该行。但似乎交易的开始和结束必须以相同的方法发生。有没有办法使用我描述的方法或我必须修改逻辑,以便事务以相同的方法开始和结束?

CustomerLock类:

@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CustomerLock {

    @Resource
    private UserTransaction tx;

    public void add(String customerId) throws Exception {
        try {
            tx.begin();
            dsApi.lock()
        } catch (Exception e) {
            throw e;
        }
    }

    public void remove(String customerId) throws Exception {
        try {
            tx.commit();
        } catch (Exception e) {
            throw e
        }
    }
}

CustomerProvisioner类摘录:

public abstract class CustomerProvisioner {

    ...

    public void execute(String customerId) {
        try {
            customerLock.add(customerId);

            processing....

            customerLock.remove(customerId);
        } catch (Exception e) {
            logger.error("Error", e);
        }
    }

    ...

}

StandardCustomerProvisioner类:

@Stateless
public class StandardCustomerProvisioner extends CustomerProvisioner {

    ...

    public void provision(String customerId) {
        // do some business logic
        super.execute(customerId);
    }
}

1 个答案:

答案 0 :(得分:0)

正如@Gimby所说,你不应该混合容器管理和bean管理的事务。由于您的StandardCustomerProvisioner没有像" @TransactionManagement(TransactionManagementType.BEAN)" - 它使用容器管理的事务,默认情况下为REQUIRED。

您有两个选项可以让它发挥作用:

1)删除" @TransactionManagement(TransactionManagementType.BEAN)"使用UserTransaction调用并运行CMT

2)将此注释(" @TransactionManagement(TransactionManagementType.BEAN)")添加到StandardCustomerProvisioner并使用此方法的事务标记调用,因此所有调用的方法都使用相同的事务上下文。无论如何,应该删除来自CustomerLock的标记调用。