Start/end 单独 EJB 方法中的事务

Start/end transaction in separate EJB methods

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

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

客户锁 class:

@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 class 摘录:

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 class:

@Stateless
public class StandardCustomerProvisioner extends CustomerProvisioner {

    ...

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

正如@Gimby 指出的那样,您不应混合使用容器管理的事务和 bean 管理的事务。由于您的 StandardCustomerProvisioner 没有像“@TransactionManagement(TransactionManagementType.BEAN)”这样的注释 - 它使用容器管理的事务,默认情况下是必需的。

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

1) 使用 UserTransaction 调用和 运行 CMT

删除“@TransactionManagement(TransactionManagementType.BEAN)”

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