1

使用 Hibernate JPA 和 Spring @Transactional(使用 Atomikos JTA 实现)我的系统中有以下实体:

  • 命令
  • Orderline(保存对订单的引用)
  • 顾客

addOrder在一个带有注释的服务类方法中,@Transactional我想在一个事务中执行以下步骤(它是一个原子功能块)。

  1. 坚持订单
  2. 坚持秩序
  3. 坚持客户

在第 1 步(保持订单),我希望 JPA 回滚任何Exception.

在第 2 步(持久化订单线),我想忽略持久化订单线期间的任何错误。因此,如果我有 10 条订单线并且 1 条因任何原因(例如违反约束)失败,我想继续使用其他订单线。

在第 3 步中,如果Exception我希望 JPA 回滚整个事务,那么在第 1 步和第 2 步中完成的所有事情也是如此。

到目前为止我遇到的问题:

  • JPA 将事务标记为“仅回滚”,以防Exception. 因此,在此之后(和之前)的所有内容都将回滚,但我想忽略Exception第 2 步。
  • flush()JPA 仅在调用or之后才知道约束冲突commit(),这通常是在@Transactional方法完成之后。我需要在我的方法中知道它。
  • 试图以单独的@Transactional方法拆分每个步骤,但由于他们需要使用相同的方法,Transaction这不会改变前两个问题。

最好的方法是什么?

更新

例如,我是否应该将所有验证都放在 Java 中并手动检查记录是否已经存在?

4

3 回答 3

1

将第二部分放在一个try-catch块中。例如:方法体可能看起来像这样。

save(order);
flush();
for(Orderline line : orderlines) {
    try {
        orderlineService.save(line); 
        flush();   
    } catch(RuntimeException rte) {
        continue;
    }
}
save(customer);
于 2013-05-22T12:27:44.970 回答
0

在第 2 步(持久化订单线),我想忽略持久化订单线期间的任何错误。因此,如果我有 10 条订单线并且 1 条因任何原因(例如违反约束)失败,我想继续使用其他订单线。

@Transactional 默认是必需的。每个方法调用 g 加入当前事务,使所有 3 个操作都是原子的。

但在这种情况下,您应该使用 REQUIRES_NEW 创建一个可重入方法,指示用于持久化每个 Orderline 的新事务:

@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void persistMyOrderLine(OrderLine o) throws Exception{}

因为持久化一个 orderline 的失败不应该影响另一个,所以必须为每个 orderline 创建一个新事务。因此,中止 TransactionA-OrderLineA 不会影响 TransactionB-OrderlineB。

问题是:根据您的需求,从本质上讲,操作不再是 ATOMIC 了。因为您有一个失败(订单线)被忽略的场景,所以该操作不是 ATOMIC 并且不会中止角色流程(3 个步骤)。

也许你应该回顾一下这些需求。

于 2013-05-22T12:37:34.693 回答
0

订单有必要吗?第 2 步可以变成第 3 步吗?

如果是这样,请将步骤 1 和 3 放入单个事务中,然后将每个人persist Orderline放入其自己的事务中以稍后运行。当然,如果您需要回滚,如果所有订单行都无法持久,这将不起作用......

另一种方法可能是创建一个非托管的EntityManager 来处理您的Orderline实体;为它们启动事务、捕获异常并自行管理提交/回滚。

理想情况下,您会使用JPA / JTA 不直接支持的嵌套事务。通过使外部事务的提交/回滚取决于内部事务的成功/失败,您可以有一些相似之处 - 或忽略(在您的情况下)内部事务。

“原子性”在实践中往往是一种相对的野兽 - 很大程度上取决于您的“事务隔离”的强度和选择的“锁定级别”(乐观或悲观的变化)。

我对此的最初想法是,如果您将其视为线程同步问题,并在嵌套顺序上保持某种一致性(“外部”总是处理 X,而“内部”总是处理“Y”) - 你应该没关系,因为应用更改的序列化顺序将保持一致。

于 2013-05-22T12:54:24.587 回答