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 CustomerLock
和 add()
和 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 的标记调用。
我开发了一个典型的企业应用程序,负责将客户提供给第 3 方系统。该系统有一个限制,即只有一个线程可以处理某个客户。因此,我们添加了一个简单的锁定机制,该机制由 @Singleton
组成,其中包含 Set
当前正在进行的 customerId。每当有新的配置请求出现时,它都会首先检查此 Set
。如果存在 cusotomerId,它会等待,否则会将其添加到 Set
并进入处理过程。
最近决定,这个应用程序将部署在集群中,这意味着这种锁定方法不再有效。我们想出了一个解决方案,使用 DB 进行锁定。我们创建了一个包含 customerIds 的单列 table(它也有一个唯一约束)。当一个新的供应请求到来时,我们开始一个事务并尝试使用 SELECT FOR UPDATE
锁定带有 customerId 的行(如果 customerId 尚不存在,我们将其插入)。之后我们开始配置客户,完成后,我们提交交易。
概念有效,但我在交易方面遇到问题。目前我们有一个 class CustomerLock
和 add()
和 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 的标记调用。