【问题标题】:Spring JPA @Transactional AtomicSpring JPA @Transactional Atomic
【发布时间】:2013-05-22 11:43:43
【问题描述】:

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

  • 订购
  • Orderline(包含对订单的引用)
  • 客户

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

  1. 坚持订单
  2. 坚持订单线
  3. 坚持客户

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

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

在第 3 步如果有任何 Exception,我希望 JPA 回滚整个事务,以及在第 1 步和第 2 步中完成的所有事情。

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

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

最好的方法是什么?

更新

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

【问题讨论】:

    标签: java spring hibernate jpa spring-transactions


    【解决方案1】:

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

    save(order);
    flush();
    for(Orderline line : orderlines) {
        try {
            orderlineService.save(line); 
            flush();   
        } catch(RuntimeException rte) {
            continue;
        }
    }
    save(customer);
    

    【讨论】:

    • @Bhasit 在这种情况下我遇到了上面提到的问题 2。约束冲突(例如实体已经存在)可能仅在提交完成时才会被注意到。此提交仅在主服务结束时完成,因为那是事务的结束。
    • 您想忽略 condtraint-violations 吗?另外,您使用的是哪个事务管理器?
    • 基本上我希望能够忽略由持久化 Orderline 引起的任何(运行时)异常。我们正在使用 Atomikos。
    • 好吧,在持久化order 之后以及在每次save 调用orderline 之后添加一个flush
    • 或者,由于您不关心 orderlines 上的约束违规,请从其表中删除数据库级约束。然后代码将按您的预期工作,无需 flush() 调用。由于orderlines 表上没有任何约束,因此事务提交后的约束违反不应出现任何异常。如果任何其他表存在约束冲突,事务将按照您的预期回滚。
    【解决方案2】:

    在第 2 步(保留 Orderlines)我想忽略任何错误 在订单线的持续期间。所以如果我有 10 条订单线并且 1 因任何原因失败(例如违反约束)我想 继续其他人。

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

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

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

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

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

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

    【讨论】:

    • 使用此设置,我在第 3 步遇到问题。如果出现任何问题,我需要回滚。但由于订单线已经在单独的事务中提交(因为 Propagation.REQUIRES_NEW),我无法回滚这些订单线。除此之外,Orderline 还引用了处于未提交事务中的 Order。所以我相信我会违反参照完整性约束。
    【解决方案3】:

    需要订购吗?第二步可以变成第三步吗?

    如果是这样,请将步骤 1 和 3 放入单个事务中,并将每个 persist Orderline 放入其自己的事务中,然后再运行。当然,如果您需要在 all Orderlines 无法持续​​存在的情况下进行回滚,这将不起作用...

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

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

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

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

    【讨论】:

    • 您的第一种方法适用于这种特殊情况。但我可以想象我有另一组实体,我想坚持并忽略它的异常。然后我又会遇到同样的问题。关于另一种方法,那将是一个完全不同的事务,所以它会破坏我的原子性,不是吗?
    • 如果忽略某些实体的异常,它仍然是事务性的吗?它会使 ACID 的 C 失败。我想知道一个解决方案是否适用于保存点,但是是的,JPA 不能很好地解决这个问题。
    • @Luciano - 如果它是 C 根据用户的要求/代码... ACID 的重点是确保系统不会给您带来任何意外。
    猜你喜欢
    • 2018-06-27
    • 2020-02-03
    • 2014-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    相关资源
    最近更新 更多