1

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

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

客户锁类:

@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);
    }
}
4

1 回答 1

1

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

您有 2 个选项可以使其工作:

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

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

于 2017-01-06T09:55:49.180 回答