jpa mysql乐观锁_JPA @Lock(value = LockModeType.PESSIMISTIC_WRITE) 悲观锁防坑

前提:

由于业务需要在entity Product已经实现了@version的乐观锁的基础上再加上了悲观锁的控制

@Lock(value = LockModeType.PESSIMISTIC_WRITE)

Product findByType(String type);

详情描述:

但是当进行多请求的并发测试的时候发现程序第一个抢占findByType的请求能正常上锁,其它并发请求也正常进入等待,可是当第一个请求修改product并且提交事务释放锁的时候,其它等待请求准备上锁的时候却抛出了乐观锁的错误,为什么会出现这种错误呢?

解答:

JPA 提供的@Lock 不是直接对findByType生成的对应查询语句后面加for update上锁,而是按照下面的顺序来上锁的:

执行查询语句: select * from product where type = ?

查询结果(id, type, version): [1001, 'NEW', 1]

通过查询出来的id, version在执行以下查询

select * from product where product id = 1001 and version = 1

如果步骤2查询有数据返回的话就生成下面的for update的查询SQL上锁, 如果没有就抛出乐观锁异常。

select * from product where product id = 1001 and version = 1 for update

根据@Lock的上述执行原理,当触发多请求并发的时候,所有请求都同步执行了上述1的查询操作,当执行步骤2,3的时候先上锁的请求修改了数据和版本后,后续请求在旧的version就查询不出来数据就抛出了乐观锁异常了。

建议:

一般情况下尽量避免使用悲观锁,对系统开销很大,如果不得已需要用到的话还是直接写for update的SQL直接上锁。

@Data @AllArgsConstructor @NoArgsConstructor @Builder @Table(name = "advert") public class Advert { /** * 主键 / @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @ApiModelProperty(value = "广告唯一id") private Integer id; /* * 广告名称 / @ApiModelProperty(value = "广告名称") @NotBlank(message = "广告名称不可为空", groups = {AddGroup.class, UpdateGroup.class}) private String name; /* * 广告位置ID / @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "position_id") private AdPosition position; /* * 广告图片ID / @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "media_id") private MediaFile media; /* * 开始日期 / @Column(name = "start_date") @ApiModelProperty(value = "开始日期") @NotNull(message = "开始日期不可为空", groups = {AddGroup.class}) private Date startDate; /* * 结束日期 / @Column(name = "end_date") @ApiModelProperty(value = "结束日期") @NotNull(message = "结束日期不可为空", groups = {AddGroup.class}) private Date endDate; /* * 广告链接 / @ApiModelProperty(value = "广告链接") @NotBlank(message = "广告链接不可为空", groups = {AddGroup.class}) private String link; /* * 广告状态:0:未启用,1:已启用,2:已结束 / @ApiModelProperty(value = "广告状态") @NotNull(message = "广告状态不可为空", groups = {UpdateGroup.class}) private Integer status; /* * 创建时间 / @Column(name = "create_time") @ApiModelProperty(value = "创建时间") private Date createTime; /* * 更新时间 */ @Column(name = "update_time") @ApiModelProperty(value = "更新时间") private Date updateTime; },对这个类的对象进行添加和修改时,因为在server层有大量重复代码,如何简化代码并避免并发问题
06-01
可以使用Spring Data JPA中的`CrudRepository`或`JpaRepository`接口来简化代码,并避免并发问题。 首先,定义一个通用的BaseService类,其中包含常用的CRUD操作: ``` public abstract class BaseService<T, ID extends Serializable> { @Autowired protected JpaRepository<T, ID> repository; public T save(T entity) { return repository.save(entity); } public T findById(ID id) { return repository.findById(id).orElse(null); } public void delete(T entity) { repository.delete(entity); } public void deleteById(ID id) { repository.deleteById(id); } public List<T> findAll() { return repository.findAll(); } public Page<T> findAll(Pageable pageable) { return repository.findAll(pageable); } } ``` 然后,定义一个AdvertService类,继承BaseService,并添加一些特定的业务方法: ``` @Service public class AdvertService extends BaseService<Advert, Integer> { @Autowired private AdvertRepository advertRepository; public List<Advert> findByStatus(Integer status) { return advertRepository.findByStatus(status); } public List<Advert> findByPositionId(Integer positionId) { return advertRepository.findByPositionId(positionId); } // add other business methods here } ``` 在AdvertRepository接口中定义需要的查询方法: ``` public interface AdvertRepository extends JpaRepository<Advert, Integer> { List<Advert> findByStatus(Integer status); List<Advert> findByPositionId(Integer positionId); // add other query methods here } ``` 最后,在controller层中使用AdvertService来进行增删改查操作: ``` @RestController @RequestMapping("/advert") public class AdvertController { @Autowired private AdvertService advertService; @PostMapping("/add") public Result add(@RequestBody Advert advert) { advertService.save(advert); return Result.success(); } @PostMapping("/update") public Result update(@RequestBody Advert advert) { advertService.save(advert); return Result.success(); } // add other controller methods here } ``` 这样做可以避免重复的代码,同时也可以有效地避免并发问题。由于Spring Data JPA会自动处理事务,因此在进行增删改操作时,可以确保数据的一致性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值