@Transactional(rollbackFor = Exception.class)
public void add(User user) {
log.info("add user params user:{}", JSON.toJSONString(user));
Assert.isTrue(StringUtils.isNotBlank(user.getIdCard()), "身份证号不允许null");
String key = "key";
RLock lock = redissonClient.getLock(key);
lock.lock();
try {
long count = userMapper.selectCount(user);
if (count == 0) {
userMapper.insert(user);
}
} catch (Exception e) {
log.error("add user error", e);
} finally {
lock.unlock();
}
System.out.println("并发执行,同时插入了两条");
}
问题分析:
如果有两个线程a,b。如果a线程释放锁后,退出方法前,让出时间片,由于方法未执行完,此时事务没有提交,那么b线程在去数据库查询的时候仍然出来count为0,执行了insert操作,两个线程执行完该方法提交事务,此时数据库中会增加两条数据,幂等性失效。
解决方式:
第一种:
//@Transactional(rollbackFor = Exception.class)
public void add(User user) {
log.info("add user params user:{}", JSON.toJSONString(user));
Assert.isTrue(StringUtils.isNotBlank(user.getIdCard()), "身份证号不允许null");
String key = "key";
RLock lock = redissonClient.getLock(key);
lock.lock();
try {
long count = userMapper.selectCount(user);
if (count == 0) {
userMapper.insert(user);
}
} catch (Exception e) {
log.error("add user error", e);
} finally {
lock.unlock();
}
System.out.println("并发执行,同时插入了两条");
}
去掉@Transactional注解,这样在a线程在insert时候就会自动提交事务,a释放锁后,b在查询时候,count不等0,不执行插入操作。
方法二:
public class Test {
@Resource
private UserMapper userMapper;
@Resource
private UserService userService;
@Resource
private RedissonClient redissonClient;
public void test(User user) {
String key = "key";
RLock lock = redissonClient.getLock(key);
lock.lock();
try {
userService.addOk(user);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
@Transactional(rollbackFor = Exception.class)
public void addOk(User user) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.eq(User::getIdCard, user.getIdCard());
long count = userMapper.selectCount(wrapper);
if (count == 0) {
userMapper.insert(user);
}
}
}
将锁放到到事务提交后