【问题标题】:Spring Programmatic transaction within transaction事务中的 Spring 程序化事务
【发布时间】:2012-12-09 10:59:16
【问题描述】:

我写了下面的代码来做下面的活动 我使用 Spring 类创建了一个事务。 插入一行。 创建了其他交易。 插入另一行。 提交的外部事务。 回滚内部事务。

TransactionStatus trxstsOuter= dsTrxMngr.getTransaction(null);
    jdbcTemplate.update("insert into kau_emp values(6,'xyz' )");
    TransactionStatus trxstsInner= dsTrxMngr.getTransaction(null);
        jdbcTemplate.update("insert into kau_emp values(7,'pqr' )");

dsTrxMngr.commit(trxstsOuter);
System.out.println("trxstsOuter.isCompleted()" + trxstsOuter.isCompleted());
System.out.println("trxstsInner.isCompleted()" + trxstsInner.isCompleted());
dsTrxMngr.rollback(trxstsInner);
    System.out.println("trxstsInner.isCompleted()" + trxstsInner.isCompleted());

我观察到这两行都已提交给 DB ! 输出是

trxstsOuter.isCompleted()true
trxstsInner.isCompleted()false
trxstsInner.isCompleted()true

这是正确的行为吗? 在允许外部事务提交之前,不应该首先提交/回滚内部事务吗? 如果外部事务被提交,内部事务的回滚是否应该抛出错误?

【问题讨论】:

  • 事务管理器(本地事务/容器管理/托管事务/自定义事务管理器、事务传播、逻辑事务/物理事务、内部事务、数据库限制(嵌套事务是不是每个 rdbms 都支持)等等,所以我什至敢于发布任何答案 :-) 这需要多年的学习,我不认为试错法是开始的方式:-)
  • 事务和javadoc说的一样
  • @BorisTreukhov 从调试中可以看出它们是不同的对象,从 isCompleted() 的 Sysouts 中也可以看出它们是单独处理的
  • @BorisTreukhov 你说“每个 rdbms 都不支持嵌套事务”......我正在使用 HSQL DB。会不会有问题?
  • 在大多数应用程序中,您只使用单个事务,嵌套数据库事务是非常高级的东西,很少使用 - 您应该尽可能使用 java 实体对象作为状态。

标签: spring transactions nested


【解决方案1】:

在您的示例事务中,Propagation.REQUIRED 用作默认值,所有逻辑事务都映射到单个物理事务

当传播设置为 PROPAGATION_REQUIRED 时,逻辑 为设置所在的每个方法创建事务范围 应用。每个这样的逻辑事务范围都可以确定 单独回滚状态,具有外部事务范围 在逻辑上独立于内部事务范围。的 当然,在标准 PROPAGATION_REQUIRED 行为的情况下,所有这些 范围将映射到相同的物理事务。所以一个 在内部事务范围内设置的仅回滚标记确实会影响 外部事务实际提交的机会(如您所料 它到)。

因此,在您的示例中,两个逻辑事务映射到一个物理事务。

documentation

【讨论】:

  • 你是对的。创建后我打印了“trxstsInner.isNewTransaction()”。它印有“flase”。后来我用 PROPAGATION_REQUIRES_NEW 创建了内部事务。在那种情况下,“trxstsInner.isNewTransaction()”是“true”,它不允许我在提交外部事务后回滚内部事务。未提交内部事务内部的插入。
【解决方案2】:

在当前代码中,第二个 getTransaction 调用是一个 noop。此行为由名为 PropagationBehavior 的属性控制。默认传播行为是 PROPAGATION_REQUIRED - 这意味着如果不存在则启动新事务,否则加入现有事务。你的情况是这样的。

如果您将第二个事务的传播行为属性更改为 PROPAGATION_REQUIRES_NEW - 您将获得预期的行为。一旦内部事务被提交/回滚,外部事务就会被挂起并创建一个新事务,外部事务会自动恢复。我已经修改了您的代码以包含此行为,当您尝试在内部事务之前提交外部事务时,您现在应该得到一个异常。如果您修复序列,则提交将独立发生。

TransactionStatus trxstsOuter= dsTrxMngr.getTransaction(null);
jdbcTemplate.update("insert into kau_emp values(6,'xyz' )");
    // start a new transaction.
    DefaultTransactionDefinition nestedTransDef = new DefaultTransactionDefinition();
    nestedTransDef.setPropagationBehavior(             
           TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    TransactionStatus trxstsInner= dsTrxMngr.getTransaction(nestedTransDef);
     System.out.println("trxstsInner.isNewTransaction()"+ trxstsInner.isNewTransaction());  
        jdbcTemplate.update("insert into kau_emp values(7,'pqr' )");

dsTrxMngr.commit(trxstsOuter);
    System.out.println("trxstsOuter.isCompleted()" + trxstsOuter.isCompleted());
    System.out.println("trxstsInner.isCompleted()" + trxstsInner.isCompleted());
    dsTrxMngr.rollback(trxstsInner);
    System.out.println("trxstsInner.isCompleted()" + trxstsInner.isCompleted());

顺便说一句,您应该使用注释来管理事务 - 这是在代码中合并事务的一种更简洁/更好的方式。程序化事务仅适用于需要更多控制的极少数情况。此外,建议在使用 TransactionTemplate 进行编程事务时。

【讨论】:

  • 我使用 PROPAGATION_REQUIRES_NEW 创建了内部事务。在那种情况下,“trxstsInner.isNewTransaction()”是“true”,它不允许我在提交外部事务后回滚内部事务。内部事务中发生的插入未提交
  • 我上面提到的代码是不正确的。您必须在外部事务之前提交/回滚内部事务。我原以为如果你用另一种方式它会抛出异常 - 但如果它正在提交外部事务,那么它是错误(但不是关键,因为它是 api 的不正确使用)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 2015-03-26
  • 2019-02-21
相关资源
最近更新 更多