事务嵌套使用注意点

博客讲述了在Spring中遇到的事务嵌套问题,方法A和B都需要独立事务。作者发现PROPAGATION_REQUIRES_NEW并未按预期工作。经过研究发现,当A和B在同一个类中时,事务无法生效。解决方案是将方法B移到另一个类中,以利用Spring的动态代理和事务管理。博客强调了理解事务传播行为对于正确配置事务管理的重要性。
摘要由CSDN通过智能技术生成

个人博客:DoubleFJ の Blog

最近在项目中需要用到事务嵌套使用,即方法 A 中调用方法 B,A 和 B 都有事务控制,且方法 B 的事务独立于 A,为一个独立新事务。

刚开始用默认的事务类型 PROPAGATION_REQUIRED,结果当然是失败了。后来查找了事务的类型以及事务传播机制,简单列一下如下:

  • PROPAGATION_REQUIRED 表示如果当前事务存在,则支持当前事务。否则,会启动一个新的事务。默认的事务类型。
  • PROPAGATION_SUPPORTS 表示如果当前事务存在,则支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY 表示如果当前事务存在,则支持当前事务,如果当前没有事务,则返回事务嵌套错误。
  • PROPAGATION_REQUIRES_NEW 表示新建一个全新 Session 开启一个全新事务,如果当前存在事务,则把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED 表示以非事务方式执行操作,如果当前存在事务,则新建一个Session以非事务方式执行操作,把当前事务挂起。
  • PROPAGATION_NEVER 表示以非事务方式执行操作,如果当前存在事务,则返回事务嵌套错误。
  • PROPAGATION_NESTED 表示如果当前事务存在,则在嵌套事务内执行,如嵌套事务回滚,则只会在嵌套事务内回滚,不会影响当前事务。如果当前没有事务,则进行与 PROPAGATION_REQUIRED 类似的操作。
  • PROPAGATION_NOT_REQUIRED 表示如果当前没有事务,就新建一个事务,否则返回错误。

了解了事务类型以及其传播机制之后,结合业务我当然选择了 PROPAGATION_REQUIRES_NEW

当我以为一切就将迎刃而解之时,debug…,what!!为什么没有生效!!

然后我又在知识海洋中继续搜寻蛛丝马迹,渴求那些曾与我一同有此境遇的可怜人的良方。

最终在几年前的帖子中终于找到了我想要的解决方案:

方法 B 需要写在另一个类中,不能跟方法 A 写在一个类里面,不然就不会生效!

详情可看该文章:spring事务@Transactional在同一个类中的方法调用不生效

简单来说就是: Spring 采用动态代理(AOP)实现对 bean 的管理和切片,它为我们的每个 class 生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。而触发切面逻辑才会进入事务拦截器从而加入到事务管理器中让事务生效!

记录一下。

使用Golang中的事务时,事务嵌套是一个常见的需求。然而,Golang的数据库/ORM库并不直接支持嵌套事务。但是,你可以通过一些技巧来模拟实现事务嵌套的效果。 一种常用的方法是使用保存(SavePoint)。保存允许你在一个事务中创建一个标记,并且可以在需要的时候回滚到这个标记。通过使用保存,你可以模拟实现事务嵌套的效果。 下面是一个使用Golang和MySQL数据库的示例代码,演示了如何使用保存来实现事务嵌套: ```go import ( "database/sql" "fmt" "log" _ "github.com/go-sql-driver/mysql" ) func main() { db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname") if err != nil { log.Fatal(err) } defer db.Close() // 开始事务 tx, err := db.Begin() if err != nil { log.Fatal(err) } // 第一层事务 _, err = tx.Exec("INSERT INTO table1 (column1) VALUES (?)", "value1") if err != nil { log.Fatal(err) } // 创建保存 savepoint := "savepoint1" _, err = tx.Exec(fmt.Sprintf("SAVEPOINT %s", savepoint)) if err != nil { log.Fatal(err) } // 第二层事务 _, err = tx.Exec("INSERT INTO table2 (column2) VALUES (?)", "value2") if err != nil { // 回滚到保存 _, rollbackErr := tx.Exec(fmt.Sprintf("ROLLBACK TO %s", savepoint)) if rollbackErr != nil { log.Fatal(rollbackErr) } log.Fatal(err) } // 提交事务 err = tx.Commit() if err != nil { log.Fatal(err) } } ``` 在上面的示例中,我们使用了`database/sql`包来连接MySQL数据库。首先,我们开始一个事务并执行第一层的插入操作。然后,在第二层事务之前,我们创建了一个保存。如果第二层事务失败,我们可以回滚到保存,并取消第一层事务对数据库的影响。 需要注意的是,这种模拟的事务嵌套并不是真正的嵌套事务。在回滚到保存时,只会回滚到保存之前的操作,而不会影响到保存之后的操作。因此,需要在使用保存时仔细考虑数据的一致性。 另外,不同的数据库/ORM库可能有不同的实现方式来模拟事务嵌套。如果你使用的是其他数据库或ORM库,可以参考其文档或查阅相关资料来了解如何实现事务嵌套
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值