Spring 数据 PESSIMISTIC_WRITE 返回旧的 DB 值

Moe*_*Kav 5 java spring persistence spring-data

我在扩展 CrudRepository 的接口中有这个方法:

@Lock(LockModeType.PESSIMISTIC_WRITE)
Voucher findTop1ByUsedFalseAndProductOrderByIdAsc(Product product);
Run Code Online (Sandbox Code Playgroud)

而这个组件:

@Component
@Transactional
public class VoucherFetchComponent {
    @Autowired
    private VoucherRepository voucherRepository;
public Voucher fetch(Product product) {
    Voucher voucher=voucherRepository.findTop1ByUsedFalseAndProductOrderByIdAsc(product);
    if(voucher==null)
       return null;
    voucher.setUsed(true);
    voucherRepository.save(voucher);
    return voucher;
}
}
Run Code Online (Sandbox Code Playgroud)

问题是同时执行 fetch() 方法。(假设我的数据库中只剩下 1 张优惠券)

我的期望:

1- 服务 A 和 B 调用 VoucherFetchComponent 的 fetch() 方法

2- 服务 A(或 B)锁定行,更新其中一个字段并返回

3- 其他服务现在可以访问被锁定的行。但是现在查询与给定的条件不匹配(used=false),因此它返回 null。

我得到的

1 和 2 就像上面一样

3- 其他服务返回具有参数(used=false)但之前已更新的旧对象!

Mac*_*ski 2

如果您仔细查看 @Lock 的 javadoc,它会指出:

用于指定执行查询时要使用的 LockModeType 的注释。

因此,锁仅在查询执行期间处于活动状态。查询完成后。锁被释放。它在整个事务期间并不像您预期​​的那样处于活动状态。

我建议使用标准锁定机制:

凭证库

public void lock(Voucher voucher, LockModeType lockType){

entityManager.lock(voucher, lockType);
Run Code Online (Sandbox Code Playgroud)

凭证获取组件

public Voucher fetch(Product product) {
    Voucher voucher=voucherRepository.findTop1ByUsedFalseAndProductOrderByIdAsc(product);
    if(voucher==null)
       return null;
    voucherRepository.lock(voucher, LockModeType.PESSIMISTIC_WRITE);
    voucher.setUsed(true);
    voucherRepository.save(voucher);
    return voucher;
}
Run Code Online (Sandbox Code Playgroud)

一旦事务完成,锁就会被释放。