我的界面中有这个方法,它扩展了CrudRepository:
@Lock(LockModeType.PESSIMISTIC_WRITE)
Voucher findTop1ByUsedFalseAndProductOrderByIdAsc(Product product);
这个组成部分:
@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;
}
}
问题是同时执行fetch()方法。 (假设我的数据库中只剩下1张优惠券)
我的期望:
1-服务A和B调用VoucherFetchComponent的fetch()方法
2-服务A(或B)锁定行,更新其中一个字段并返回
3-其他服务现在可以访问被锁定的行。但是现在查询与给定的条件(used = false)不匹配,因此返回null。
我得到了什么
1和2就像上面那样
3-其他服务返回旧对象,该对象具有参数(used = false)但之前已更新过!
答案 0 :(得分:0)
如果仔细查看@Lock的javadoc,它会声明:
用于指定执行时要使用的LockModeType的注释 查询。
因此锁仅在查询执行期间处于活动状态..查询完成后...锁被释放。它在整个交易期间没有活动,正如您所期望的那样。
我建议使用标准锁定机制:
<强> VoucherRepository 强>
public void lock(Voucher voucher, LockModeType lockType){
entityManager.lock(voucher, lockType);
<强> VoucherFetchComponent 强>
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;
}
交易完成后,锁定将被释放。