前言
我确实太少写笔记了,这样很难成长,今天做个第n次 开始。
最常用的sql语句就是插入和更新。插入暂且不提,在更新时,我以前只会无脑借鉴别人的代码。这次突然把几个理解综合了一下。茅塞顿开。
有一点必须强调,我只使用了mybatis plus,其他辅助sql语句的三方包不甚了解。
更新的核心特性——null不更新
如下语句
public Boolean edit(Long id, AReq req) {
log.info("编辑信息:id:{}, {}", id, req);
ADO oldADO = aMapper.selectById(id);
if (oldADO == null) {
throw new AException(ErrorCodeEnum.SQL_NULL_EXCEPTION);
}
// do something ...
BeanUtils.copyProperties(req, oldADO);
int flag = aMapper.updateById(oldADO);
log.info("编辑: {}", flag);
return 0 != flag;
}
以上代码看似没问题,实际存在一个大bug,就是从前端读取数据后,编辑开始,到最后写入数据库期间,但凡有另一个任务对ADO的某个字段进行了修改,就无法修改成功。
比如:ADO有三个字段name、age、status。编辑只对name和age修改,而有一个定时任务对status每一分钟更新一次。那就存在name被编辑后,保存时把status的旧值覆盖了编辑期间更新的status。
这不是一个并发问题,而是一个逻辑问题,当你明确地知道编辑只修改name和age时,你很难意识到,status被影响了,从而导致发现不了为什么定时任务的status更新不生效。
但我们可以利用null不更新的特点规避这个bug。修复后代码如下:
public Boolean edit(Long id, AReq req) {
log.info("编辑信息:id:{}, {}", id, req);
ADO oldADO = aMapper.selectById(id);
if (oldADO == null) {
throw new AException(ErrorCodeEnum.SQL_NULL_EXCEPTION);
}
// do something ...
ADO newADO = new ADO();
BeanUtils.copyProperties(req, newADO);
newADO.setId(id);
int flag = aMapper.updateById(newEdgeDO);
log.info("编辑: {}", flag);
return 0 != flag;
}
首先明确的是req中不存在status,则new ADO的status一定为null,借助null不更新特性,则不影响定时任务的执行。
更进一步去思考更新status的定时任务是不是也存在这一问题呢。
public Boolean check(Long id) {
ADO aDO = aMapper.selectById(id);
if (aDO == null) {
throw new AException(ErrorCodeEnum.SQL_NULL_EXCEPTION);
}
// do something ...
aDO.setStatus("更新了一次内容: " + (new Date()));
int flag = aMapper.updateById(aDO);
log.info("编辑: {}", flag);
return 0 != flag;
}
同样也需要改成
public Boolean check(Long id) {
if (aMapper.selectById(id) == null) {
throw new AException(ErrorCodeEnum.SQL_NULL_EXCEPTION);
}
// do something ...
ADO aDO = new ADO();
aDO.setId(id);
aDO.setStatus("更新了一次内容: " + (new Date()));
int flag = aMapper.updateById(aDO);
log.info("编辑: {}", flag);
return 0 != flag;
}
当更新的字段比较少或者不需要读取该对象的某些属性时,可以优化成这样:
public Boolean check(Long id) {
// do something ...
int flag = aMapper.update(new LambdaUpdateWrapper<User>()
.eq(User::getId, id)
.set(User::getStatus, "xxx"));
log.info("编辑: {}", flag);
return 0 != flag;
}
不要把一个简单的逻辑问题搞成复杂的并发问题。
未完待续