Preface
前两天在运行代码插入数据的时候总是报索引重复,数据死活插入不了。
情景再现
环境:Springboot,JPA。
数据库中有一张表,姑且称为table_a
,结构如下:
对应dao
内容如下:
有两个 Service 实现,分别称为ServiceAImpl
和ServiceBImpl
吧。
ServiceAImpl
中的add
方法首先判断要插入的商品是否已在数据库中了,存在则更新数据,不存在则插入数据。
在ServiceBImpl
中调用ServiceAImpl
的add
方法。
发生的异常情况是在调用handleGoods
方法传入数据库中不存在的spuId
时,在save
时会报错,提示重复的spuId
,无法插入数据。
这就很奇怪了,明明插入的spuId
是数据库中不存在的,为什么会报spuId
重复呢?
经过一番简单的思考,发现是add
方法上的
在作祟。add
方法中开启了一个新的事务,在插入新的数据时,就会导致在别的事务中查不到新插入的数据,因为此时数据还没有真正插入到数据库中。所以
save
方法由于事务不同,查不到刚刚在ServiceAImpl
的add
方法添加的商品信息,也会执行insert
语句。最终在提交事务时add
方法中的save
和deal
方法中的save
都会执行insert
语句,就导致报spuId
重复的错误。
解决方法就是将add
方法上的
改为
这也是 Spring 默认的事务传播行为,表示没有就创建事务,有就加入事务。
至于之前为什么这么写,那是之前设计的问题了😂。
Spring 事务传播行为
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 例如:methodA 事务方法调用 methodB 事务方法时,methodB 是继续在调用者 methodA 的事务中运行呢,还是为自己开启一个新事务运行,这就是由 methodB 的事务传播行为决定的。
Spring 目前共有 7 种事务传播行为。
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
评论区