1、修改个人信息,在后端来看,就是对数据库表里面对应的字段进行更新,修改操作,我们用BO把前端的这些参数封装起来,统一传入我们的接口,同时用 @Valid 注解进行统一的参数校验
API接口编写
// 修改/完善账户信息 1:
@ApiOperation(value = "修改/完善用户信息",notes = "修改/完善用户信息",httpMethod = "POST")
@PostMapping("/updateUserInfo")
public GraceJSONResult updateUserInfo(@RequestBody @Valid UpdateUserInfoBO updateUserInfoBO
// feign的统一参数校验 1: 不可以写两个对象参数,认为传了两个body,统一异常处理进行封装
);
2、 controller层代码编写,首先还是对前端传入进来的参数进行校验,判空
@Override
public GraceJSONResult updateUserInfo(@Valid UpdateUserInfoBO updateUserInfoBO) {
// BindingResult bindingResult) {
//
// // 0. 检验bo的前端参数
// if (bindingResult.hasErrors()){
// Map<String, String> map = getErrors(bindingResult);
// return GraceJSONResult.errorMap(map);
// }
// 1. 执行更新操作 sevice层编写业务代码
// 修改/完善账户信息 5:
userService.updateUserInfo(updateUserInfoBO);
return GraceJSONResult.ok();
}
3、我们在service层进而编写我们的业务代码,由于用户的信息,在我们的网站里面,是经常访问到的信息,是一个高并发的数据,当网站的用户数量庞大的时候,频繁的获取用户的信息,对我们的数据库,存在很大的压力,所以我们会把用户的一个基本信息放到我们的redis里面进行缓存,查询用户信息的时候,首先查询我们的redis,redis查不到了再查询我们的数据库,在保存到redis里面,当涉及到更新用户的信息时,我们会先删掉我们redis里面的缓存信息,然后执行更新操作,在更新操作完成后,让这个更新的线程休眠一下,再次删掉我们的redis里面缓存
当有用户再次访问到这个用户信息的时候,就会先查询我们的redis,这些内容已在我们的查询用户的服务里面实现。
service 里面 对更新操作进行缓存延时双删的操作
// 修改/完善账户信息 4:
@Override
public void updateUserInfo(UpdateUserInfoBO updateUserInfoBO) {
// 0. 更新的话,肯定是根据用户的主键id做更新的, 同时用持久层的对象做更新\
String userId = updateUserInfoBO.getId();
/**
* 双写一致,缓存双删 1: 修改.更新时,先删除redis,后更新数据库
*/
redis.del(REDIS_USER_INFO + ":" + userId);
// 1. 同时用持久层的对象做更新, 把bo中的参数对象拷贝进入我们的持久层对象里面
AppUser userInfo = new AppUser();
BeanUtils.copyProperties(updateUserInfoBO,userInfo);
// 2. 数据持久层对象里面的 更新时间需要做一个更新,同时信息完善后,对应激活状态做更改
userInfo.setUpdatedTime(new Date());
userInfo.setActiveStatus(UserStatus.ACTIVE.type);
// 3. 更新到数据库里面
// .updateByPrimaryKeySelective 会对为空的参数进行忽略,不会让空数据覆盖数据库里面原有的数据
int result = appUserMapper.updateByPrimaryKeySelective(userInfo);
if (result != 1){
// 不为1 说明更新操作出了问题
// 这样写的好处: 以往出了问题会会回传一个 FALSE ,在controller又进行判断,返回一个错误信息
// 使用自定义异常,在这进行一个解耦,没有必要把一些多余的参数返回controller,在进行处理 详见 "统一异常处理 2"
GraceException.display(ResponseStatusEnum.USER_UPDATE_ERROR);
}
// redis 缓存用户信息 5: 存在更新操作的地方,再次查询用户信息,更新覆盖到redis中
AppUser user = getUser(userId);
redis.set(REDIS_USER_INFO + ":" + userId, JsonUtils.objectToJson(user));
/**
* 双写一致,缓存双删 2: 更新完数据后,休眠一下,再删一次缓存
*/
try {
Thread.sleep(100);
redis.del(REDIS_USER_INFO + ":" + userId);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
总结 :
// redis 缓存用户信息 6: 解决分布式并发访问的问题
/**
* 缓存双写不一致解决方案:
* 用户做更新操作的时候,先去redis里面删除旧的数据,然后去数据库更新
* 更新完数据库,其他用户来访问的时候,redis里面没有数据,去数据库查找,查到新的数据后更新到redis里面
*
* 缓存双删: 设置之前删除,设置之后删除
* 上面的方案也有漏洞,有可能,用户删除redis的旧数据到更新数据库的这段时间里面,
* 其他的用户,访问的时候,新的数据还没存入数据库,查到了旧的数据,然后把旧的放入了redis,这样新的数据就不能写入数据库了
* 解决办法: 延时双删,当我们把新的数据放入数据库时,让这个线程休眠1秒钟,再次对redis的key进行删除
* 就有可能把其他用户放入的redis旧数据删除
*/