在 EJB 中获取多个事务的干净、标准的方法是什么?
What's a clean, standard way to get multiple transactions in an EJB?
我有一个 batchEdit(List<E> entity)
循环调用 edit(E entity)
函数,而每个 edit() 都有自己的事务,因此失败的编辑不会回滚好的编辑。我目前是这样实现的:
选项 1
@Stateless
@TransactionManagement( value = TransactionManagementType.CONTAINER )
public class Service<E> {
@Resource
private SessionContext context;
@Override
@TransactionAttribute( value = TransactionAttributeType.REQUIRES_NEW )
public E edit( E entity ) {
//edit code
}
@Override
public List<E> bulkEdit( List<E> entities ) {
for(E entity : entities){
//case 1: Regular edit, Does not create a new transaction!
//edit(entity);
//case 2: Hacky edit, creates a new transaction
context.getBusinessObject( Service.class ).editPersistNulls( entity );
}
}
}
根据 this Whosebug discussion,@TransactionAttribute
在我的案例 1 中被忽略,因为它不跨越任何 EJB 边界,所以 batchEdit()
调用 edit()
就好像它没有注释。在案例 2 中使用 context.getBusinessObject()
函数来获取 bean 的引用会导致 TransactionManagement 注释起作用,但完成所有这些操作似乎真的很奇怪。
选项 2
我的另一个选择是更改为 bean 管理的事务:
@TransactionManagement( value = TransactionManagementType.BEAN )
但是那样的话我会丢失 "JPA Magic" 并且不得不管理所有地方的交易。我认为我团队中的其他人不会愿意经历这些,所以如果有更好或标准的方法来做到这一点,我们将不胜感激。
我们正在使用 OpenJPA 和 EJB,但我们正在努力接近 JPA 标准。
估计"hacky"是仁者见仁智者见智吧。 context.getBusinessObject
正是为了你能做这种事而存在的
替代方法是使用第二个 class:
@Stateless
public class BulkService<E> {
@EJB
private Service<E> service;
public List<E> bulkEdit( List<E> entities ) {
for(E entity : entities) {
service.editPersistNulls( entity );
}
}
}
但要注意大型实体列表,因为包含事务可能会超时。
如果您使用的是 Java EE 7 兼容服务器实现,请考虑使用 JSR-352 Batch Applications 支持。
你可以自己注入EJB。
@Stateless
public class Service<E> {
@EJB
private Service<E> self;
@TransactionAttribute(REQUIRES_NEW)
public void edit(E entity) {
// ...
}
@TransactionAttribute(NOT_SUPPORTED)
public void bulkEdit(List<E> entities) {
for (E entity : entities) {
self.edit(entity);
}
}
}
更好的做法是@Asynchronous
。速度更快。
@Stateless
public class Service<E> {
@EJB
private Service<E> self;
public void edit(E entity) {
// ...
}
@Asynchronous
@TransactionAttribute(REQUIRES_NEW)
public void asyncEdit(E entity) {
// ...
}
@TransactionAttribute(NOT_SUPPORTED)
public void bulkEdit(List<E> entities) {
for (E entity : entities) {
self.asyncEdit(entity);
}
}
}
它还使原始 edit()
方法免受潜在不需要的 REQUIRES_NEW
事务属性的影响,因为它可能会被其他服务调用,当然应该保留在同一事务中。对于 @Asynchronous
,每次调用都需要一个新事务更有意义。
我有一个 batchEdit(List<E> entity)
循环调用 edit(E entity)
函数,而每个 edit() 都有自己的事务,因此失败的编辑不会回滚好的编辑。我目前是这样实现的:
选项 1
@Stateless
@TransactionManagement( value = TransactionManagementType.CONTAINER )
public class Service<E> {
@Resource
private SessionContext context;
@Override
@TransactionAttribute( value = TransactionAttributeType.REQUIRES_NEW )
public E edit( E entity ) {
//edit code
}
@Override
public List<E> bulkEdit( List<E> entities ) {
for(E entity : entities){
//case 1: Regular edit, Does not create a new transaction!
//edit(entity);
//case 2: Hacky edit, creates a new transaction
context.getBusinessObject( Service.class ).editPersistNulls( entity );
}
}
}
根据 this Whosebug discussion,@TransactionAttribute
在我的案例 1 中被忽略,因为它不跨越任何 EJB 边界,所以 batchEdit()
调用 edit()
就好像它没有注释。在案例 2 中使用 context.getBusinessObject()
函数来获取 bean 的引用会导致 TransactionManagement 注释起作用,但完成所有这些操作似乎真的很奇怪。
选项 2
我的另一个选择是更改为 bean 管理的事务:
@TransactionManagement( value = TransactionManagementType.BEAN )
但是那样的话我会丢失 "JPA Magic" 并且不得不管理所有地方的交易。我认为我团队中的其他人不会愿意经历这些,所以如果有更好或标准的方法来做到这一点,我们将不胜感激。
我们正在使用 OpenJPA 和 EJB,但我们正在努力接近 JPA 标准。
估计"hacky"是仁者见仁智者见智吧。 context.getBusinessObject
正是为了你能做这种事而存在的
替代方法是使用第二个 class:
@Stateless
public class BulkService<E> {
@EJB
private Service<E> service;
public List<E> bulkEdit( List<E> entities ) {
for(E entity : entities) {
service.editPersistNulls( entity );
}
}
}
但要注意大型实体列表,因为包含事务可能会超时。
如果您使用的是 Java EE 7 兼容服务器实现,请考虑使用 JSR-352 Batch Applications 支持。
你可以自己注入EJB。
@Stateless
public class Service<E> {
@EJB
private Service<E> self;
@TransactionAttribute(REQUIRES_NEW)
public void edit(E entity) {
// ...
}
@TransactionAttribute(NOT_SUPPORTED)
public void bulkEdit(List<E> entities) {
for (E entity : entities) {
self.edit(entity);
}
}
}
更好的做法是@Asynchronous
。速度更快。
@Stateless
public class Service<E> {
@EJB
private Service<E> self;
public void edit(E entity) {
// ...
}
@Asynchronous
@TransactionAttribute(REQUIRES_NEW)
public void asyncEdit(E entity) {
// ...
}
@TransactionAttribute(NOT_SUPPORTED)
public void bulkEdit(List<E> entities) {
for (E entity : entities) {
self.asyncEdit(entity);
}
}
}
它还使原始 edit()
方法免受潜在不需要的 REQUIRES_NEW
事务属性的影响,因为它可能会被其他服务调用,当然应该保留在同一事务中。对于 @Asynchronous
,每次调用都需要一个新事务更有意义。