3

我的问题与事务和异常有关

要求:

我有 10 条记录要插入到数据库表中。插入每条记录后,我将数据插入另一个表。因此,如果插入第二个表失败,我想回滚该记录。

前任。假设一次处理 10 个人的现金转账(从一个账户到另一个账户)。

伪代码:------------EJB方法的开始

for(int i = 0; i < TransferRecords.length; i++)
{
    try
    {
          //Deduct cash from TransferRecord.accountFrom --- Includes use of Hibernate Session
          //Add cash in TransferRecord.accountTo -- Includes use of Hibernate Session
     } catch(AppException exception)
     {
            //Rollback the transaction only for this particular transfer (i)
            // But here when I go for next record it says session is closed
     }
}

---------EJB方法结束

这里 AppException 是使用 @ApplicaitonException(rollback=true) 注释创建的。

我们想要的功能是:即使 TransferRecord 的事务失败(比如 2),我希望为记录 0、记录 1、记录 3、记录 4 提交数据(等等......但不是记录 2)

但这里的问题是:当 TransferRecord 2 失败并且当我移动到 TransferRecord 3 时,我收到“会话关闭”错误。

我的疑问是: 1. 这是一个正确的方法吗?或者我应该在 EJB 2 之外运行 for 循环(对于每个 TransferRecord)。如何确保会话未关闭但事务回滚(仅适用于特定失败的事务)

先感谢您。

我正在使用 EJB3、Hibernate 3.x、Jboss 4.2.x,并且我正在使用容器管理事务。

4

3 回答 3

3

这是一个正确的方法吗?

不,对于 CMT,您的方法就是您的交易单位。因此,在这里,您所有的一切都TransferRecord在同一个独特的交易中处理。

顺便问一下,你如何回滚事务?你传播一个RuntimeException还是你打电话setRollbackOnly()?我只是好奇。

或者我应该在 EJB 之外运行 for 循环(对于每个 TransferRecord)?

为什么在外面?没有什么能强迫你这样做。如果您想TransferRecord在自己的事务中处理每个,您应该将它们传递给另一个EJB 方法(下面的代码受此答案的启发):

// supposing processRecords is defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessLocal1 myBean;

    public void processRecords(List<TransferRecord> objs) {
        // No transactional stuff so no need for a transaction here
        for(Object obj : objs) {
            this.myBean.process(obj);
        }
    }

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(TransferRecord transferRecord) {
        // Transactional stuff performed in its own transaction
        // ...
    }
}

如何确保会话未关闭但事务回滚(仅适用于特定失败的事务)

我想我涵盖了那部分。

于 2010-01-08T19:24:55.177 回答
1

您在这里唯一的选择是使用用户事务而不是 bean 外部循环的容器管理事务,以便每次进入 bean 时都会获得具有关联事务和连接(基本上是会话)的新实体管理器

于 2010-01-08T19:30:25.713 回答
0

我认为您可以创建两个单独的事务,第一个用于 TransferRecord(1)(一旦一切正常就进行提交),然后为所有 TransferRecord(i+1) 启动其他 TX。

另一种方法是使用保存点,能够回滚并丢弃超过该保存点的所有内容(但我更喜欢第一种方法)。

问候。

于 2010-01-08T17:55:56.370 回答