目录
业务描述:新增一个对象,将数据保存到数据库中,且限制最多只能添加15条。
常规做法:
@Transactional(rollbackFor = Exception.class)
public Result add(XXObject object) {
// 先从数据库中获取总数
int count = xxService.getCount(xxParam);
// 达到或超过15条返回提示信息
if (count >= 15) {
return Result.fail("已超过上限");
}
// 未超过15条进行新增
xxService.insert(object);
return Result.success();
}
这种做法可以在只有一个用户操作的情况下,实现限制15条。
但是如果是多个用户同时添加,或者稍微极端一些,进行并发压测都会出现超过15条上限的问题
这时就需要对业务代码进行加锁,
常规加锁:
代码如下:
@Transactional(rollbackFor = Exception.class)
public Result add(XXObject object) {
// 锁的名称
String redisKey = "lockName";
// 上锁
String lockValue = lock.lock(redisKey, 5000, 5000);
try {
// 先从数据库中获取总数
int count = xxService.getCount(xxParam);
// 达到或超过15条返回提示信息
if (count >= 15) {
return Result.fail("已超过上限");
}
// 未超过15条进行新增
xxService.insert(object);
return Result.success();
} catch (Exception e) {
return Result.fail("添加失败,请稍后重试");
} finally {
// 解锁
lock.unlock(redisKey, lockValue);
}
}
虽然加上了锁,但是还有一个问题,就是锁释放后,事务才进行提交,这就导致锁先释放了,别的线程拿到锁读取到旧的数据,然后上一个事务才提交,导致多插入了数据。
解决办法:
1、去掉事务注解,仅限业务中只有单表插入,避免多表插入发生异常时,没有事务无法回滚的问题。
2、在事务提交后再释放锁,代码如下:
public Result add(XXObject object) {
// 锁的名称
String redisKey = "lockName";
// 上锁
String lockValue = lock.lock(redisKey, 5000, 5000);
try {
// 通过spring 上下文获取当前service对象,然后执行方法,且开启了事务
XXService xxService = SpringContextUtil.getBean(QiStrategyService.class);
return xxService.add(object);
} catch (Exception e) {
return Result.fail("添加失败,请稍后重试");
} finally {
// 解锁
lock.unlock(redisKey, lockValue);
}
}
@Transactional(rollbackFor = Exception.class)
public Result add(XXObject object) {
// 先从数据库中获取总数
int count = xxService.getCount(xxParam);
// 达到或超过15条返回提示信息
if (count >= 15) {
return Result.fail("已超过上限");
}
// 未超过15条进行新增
xxService.insert(object);
return Result.success();
}
还有一种解决方法,可以利用spring 切面进行实现,定一一个注解,然后实现注解的处理器,在处理器中控制事务的开启、提交和回滚,减少加锁和释放锁的代码重复使用。(有时间再补上这块的代码)
如有问题,欢迎指正,感谢!撒花!