记录JPA并发save时遇到的坑

前言

在JPA中,使用save方法时是这样的:如果我们save的对象指定了主键,那么会根据主键先进行一次查询,如果查询记录不存在则执行insert语句,如果查询记录存在则执行update语句。

问题现象

业务场景是这样的:当某个用户钱包流水发生变化时,我们会先查询用户钱包是否存在(新注册的用户一开始没有钱包),如果存在则直接更新钱包余额,并添加一条流水,如果用户钱包不存在,则新生成钱包,再更新钱包余额,最后添加流水。

当同一个用户同时产生多条流水时,现在的流程则可能出现问题。

伪代码

// 查询用户钱包是否存在
User u = select(userId);
if(u == null){
	// 不存在则生成钱包,初始化钱包余额为0
	jpa.save(u);
}
// 添加流水
addFlow(u);
// 更新钱包余额
update(u);

问题分析

记录JPA并发save时遇到的坑_第1张图片

问题就发生在当第一个线程save成功后,第二个线程再执行时,save就会变成update,并且会覆盖第一个线程所执行的操作。

解决方法

只要让自定义save方法,就是insert操作就可以了,当第2个线程save时,会报主键冲突,然后第2个线程再重试一次,再次查询钱包是否存在时,就可以查询到了,然后就不用再生成钱包了,直接更新余额即可(更新余额幂等性)。

你可能感兴趣的:(JPA,jpa,java)