悲观锁
准备工作
本文所讨论的悲观锁,并非普通查询的悲观锁,而是保存指令内部的悲观锁。
面对一个保存操作,在不同的情况下,保存指令会采取两种截然不同的行为:
-
直接利用数据库本身的能力进行upsert操作
-
先通过select操作检查被保存的数据是否存在,再根据查询结果决定后续操作应该是insert还是update
Jimmer会尽可能执行1,如果无法做到,则执行2并向用户报告QueryReason。
当Jimmer不得不执行2时,存在一个问题:先执行的select操作和后执行insert或update操作之间存在一个时间窗口,其他事务可能挤进这个时间窗口对相同的数据进行修改。
为了解决这个问题,保存指令允许用户启用悲观锁,在执行select操作时加上for update选项,直到事务提交为止。
在展示悲观锁之前,我们有必要让保存指令执行上面的2,而非1。一个有效的办法是启用事务内触发器:
事务内触发器是Jimmer所支持的触发器的一种,Jimmer总是通过select查询修改前的旧数据,并以此模拟出触发器的效果。
备注
事务内触发器是当前尚未介绍的功能,读者不用太在意它,这里,我们只是用它来确保Jimmer会采用先查再改的策略而已。
采用以下任何一种方法开启事务内触发器
-
利用Jimmer的Spring Boot Starter
修改
application.yml或application.properties文件,完成配置application.ymljimmer:
trigger-type: TRANSACTION_ONLY -
使用Jimmer的核心API
- Java
- Kotlin
JSqlClient sqlClient = JSqlClient
.newBuilder()
.setTriggerType(TriggerType.TRANSACTION_ONLY)
...省略其他配置...
.build();val sqlClient = sqlClient {
setTriggerType(TriggerType.TRANSACTION_ONLY)
...省略其他配置...
}
通过这样的配置,强迫Jimmer总是先查询再修改,以便于演示悲观锁。