目录
异常处理
Spring为了整合全局异常的处理(不用在每个方法下捕获或者抛出异常从而达到处理异常的机制),开发了如下注解:
- @RestControllerAdvice 定义全局异常的处理类 返回值JSON串
- @ExceptionHandler 标识拦截的异常的类型,如果类型匹配,则执行方法
@RestControllerAdvice
public class MyExceptionAdvice {
//写法:1.运行时异常(通用) 2.自定义异常信息 3.拦截所有异常Exception
@ExceptionHandler(RuntimeException.class)
public Object exception(Exception e){
//需求: 如果遇到异常,应该提示用户201/失败信息.
e.printStackTrace();
return SysResult.fail();
}
}
业务返回
满足不同业务需求的返回不同结果,方便前端识别业务执行成功与否,以便执行不同的操作.
@Data
@Accessors(chain = true)
@NoArgsConstructor//无参构造
@AllArgsConstructor//全参构造
public class SysResult implements Serializable {//实现接口是一种规范性写法
private Integer status;//状态码信息200 201
private String msg;//服务器返回的提示信息
private Object data;//返回的业务数据,不清楚是什么类型,就用object
//为了用户使用VO对象更加的方便,重载一些方法,简化程序的调用
public static SysResult fail(){
return new SysResult(201,"业务执行失败",null);
}
//1.不带参数的正确返回
public static SysResult success(){
return new SysResult(200,"业务执行成功",null);
}
//2.带返回值的正确返回 用户传递什么/返回值就是什么
public static SysResult success(Object data){
return new SysResult(200,"服务器处理成功",data);
}
//3.带返回值,带提示信息
public static SysResult success(String msg, Object data){
return new SysResult(200,msg,data);
}
}
用户登录
登录安全问题(token)
为了防止直接跳转网页,用户登录成功后会返回一个token标识,后续跳转页面时,会监测是否带有token标识,有则可以跳转,没有则不能执行相关业务.若后续关闭系统则需要重新登录并获取token标识.
业务实现
业务需求文档
- 需求:检测用户登录操作
- URL: /login
- 请求类型: Post请求
- 接收参数: User对象
- 返回值: Sysresult对象
Controller层:
//检测用户登录操作
@PostMapping("/login")
public SysResult login(@RequestBody User user){
System.out.println(user);
//token用于用户名和密码的校验,防止直接跳转网页
//token 有值则业务正确 null 业务操作失败
String token = userService.login(user);
if (token == null){
return SysResult.fail();
}
return SysResult.success(token);
}
Service层:
public String login(User user) {
String password = user.getPassword();//明文密码,需要加密,利用DigestUtils
//byte[] passByte = password.getBytes();
String md5Pass = DigestUtils.md5DigestAsHex(password.getBytes());
//根据用户名和密文查询数据库
//根据对象中不为null的属性充当where条件
user.setPassword(md5Pass);
QueryWrapper<User> qw = new QueryWrapper<>(user);
User userDB = userMapper.selectOne(qw);
//查询结果是否有效
if (userDB == null){//用户名密码错误 返回null
return null;
}
//用户名密码均正确 返回一个独一无二的token 利用UUID
return UUID.randomUUID().toString()
.replace("-", "");
}
用户管理
用户列表
列表分页
- 需求:分页查询用户列表信息
- URL:/list?query,pagenum,pagesize
- 请求类型:get
- 接收参数:PageResult对象
- 返回值:SysResult
Controller层:
/**
* 获取用户列表信息
* 参数:PageResult对象进行接收 传递3个参数
* 返回值:SysResult对象 参数(PageResult对象) 返回5个参数
* 我们想要的数据封装在sysResult对象的data属性里,data属性也即PageResult对象,
* 前端再根据需要,从data属性里面抽取需要的数据
*/
@GetMapping("/list")
public SysResult getUserList(PageResult pageResult){
return SysResult.success(userService.getUserList(pageResult));
}
Service层:
public PageResult getUserList(PageResult pageResult){
//参数1: page分页对象
Page page = new Page(pageResult.getPageNum(),pageResult.getPageSize());
//参数2:分页的查询条件 username模糊查询 注意bug:如果用户没有传递query,模糊查询一样拼接,与理想不符
QueryWrapper<User> qw = new QueryWrapper<>();
boolean flag = StringUtils.hasLength(pageResult.getQuery());
qw.like(flag,"username",pageResult.getQuery());
//page 原本两个参数 根据分页查询返回 total/分页后的记录records 4个参数
page = userMapper.selectPage(page, qw);
List<User> userList = page.getRecords();
long total = page.getTotal();
System.out.println(total);
return pageResult.setTotal(total).setRows(userList);
}
商品管理
商品列表
列表分页
- 需求:分页查询商品
- URL:/item/getItemList?query,pagenum,pagesize
- 请求类型:get
- 接收参数:PageResult对象
- 返回值:Sysresult
Controller层:
@GetMapping("/getItemList")
public SysResult getItemList(PageResult pageResult){
pageResult = itemService.getItemList(pageResult);
return SysResult.success(pageResult);
}
Service层:
public PageResult getItemList(PageResult pageResult) {
Page<Item> page = new Page<>(pageResult.getPageNum(),pageResult.getPageSize());
QueryWrapper<Item> qw = new QueryWrapper<>();
//query有值才拼接
boolean flag = StringUtils.hasLength(pageResult.getQuery());
qw.like(flag, "title", pageResult.getQuery());
page = itemMapper.selectPage(page, qw);
return pageResult.setTotal(page.getTotal()).setRows(page.getRecords());
}
商品分类
查询商品等级分类信息
- 需求:按照商品的level等级,查询出对应等级的商品分类信息
- URL:/itemCat/findItemCatList/{level}
- 请求类型:get
- 接收参数:level
- 返回值:SysResult对象
Controller层:
@GetMapping("/findItemCatList/{level}")
public SysResult findItemCatList(@PathVariable Integer level){
List<ItemCat> itemCatList = itemCatService.findItemCatList(level);
return SysResult.success(itemCatList);
}
Service层:
方式1:利用map集合封装所有的父子级关系
public List<ItemCat> findItemCatList(Integer level) {
long start = System.currentTimeMillis();
//map集合封装的是所有父子级关系
Map<Integer,List<ItemCat>> map = initMap();
//只获取一级,即parend_id=0
if (level == 1) return map.get(0);
//获取二级 1/2
if (level == 2) return getLevel2List(map);
//获取1/2/3级
else return getLevel3List(map);
}
/**
* 1.封装Map集合 Map<父级id,list<ItemCat>,value里的数据是key对应数据的子级,只有父子级关系(并不是一二级)
* 只查询一次数据库,作为辅助方法
* @return
*/
public Map<Integer, List<ItemCat>> initMap() {
Map<Integer,List<ItemCat>> map = new HashMap<>();
//1.查询item_cat表中的所有记录(1/2/3级菜单)
List<ItemCat> itemCatList = itemCatMapper.selectList(null);
//2.实现数据封装
/**
* 1.key不存在,准备一个新的list集合,直接追加到list集合
* 2.key存在,获取原有list集合,再追加
*/
for (ItemCat itemCat: itemCatList){
//key存在,获取原有list集合,再追加
if (map.containsKey(itemCat.getParentId()))
map.get(itemCat.getParentId()).add(itemCat);
//key不存在,准备一个新的list集合,直接追加到list集合
else {
List<ItemCat> list = new ArrayList<>();
list.add(itemCat);
map.put(itemCat.getParentId(), list);
}
}
return map;//将封装的数据进行返回
}
//查询一级和二级信息
public List<ItemCat> getLevel2List(Map<Integer, List<ItemCat>> map) {
//先遍历一级,再封装二级
//1.获取一级
List<ItemCat> oneList = map.get(0);
for (ItemCat oneItemCat: oneList){
//查询二级,通过一级的id
List<ItemCat> twoList = map.get(oneItemCat.getId());
oneItemCat.setChildren(twoList);
}
return oneList;
}
//查询一级/二级/三级
public List<ItemCat> getLevel3List(Map<Integer, List<ItemCat>> map) {
List<ItemCat> oneList = getLevel2List(map);
//遍历一级,获取二级
for (ItemCat oneItemCat: oneList){
List<ItemCat> twoList = oneItemCat.getChildren();
//如果当前一级没有二级,直接跳过当前循环
if (twoList == null || twoList.size() == 0) continue;
//若一级下面有二级,则查询三级
for (ItemCat twoItemCat: twoList){
List<ItemCat> threeList = map.get(twoItemCat.getId());//获取以二级id为父级的子级
twoItemCat.setChildren(threeList);//封装为二级的子级
}
}
return oneList;
}
方式2:利用多重for循环
public List<ItemCat> findItemCatList(Integer level) {
//1.查询一级商品分类信息
QueryWrapper<ItemCat> qw = new QueryWrapper<>();
qw.eq("parent_id", 0);
List<ItemCat> oneLists = itemCatMapper.selectList(qw);
for (ItemCat oneItemCat: oneLists){
qw.clear();
qw.eq("parent_id", oneItemCat.getId());
List<ItemCat> twoLists = itemCatMapper.selectList(qw);
//不能再次将二级记录封装给一级,如果在这里封装,则二级的children为null
//oneItemCat.setChildren(twoLists);
for (ItemCat twoItemCat: twoLists){
qw.clear();
qw.eq("parent_id", twoItemCat.getId());
List<ItemCat> threeLists = itemCatMapper.selectList(qw);
//将三级记录封装给二级
twoItemCat.setChildren(threeLists);
}
//将二级记录封装给一级
oneItemCat.setChildren(twoLists);
}
return oneLists;
}
常识:用户第一次查询数据库,需要建立链接,相对耗时长,但是后续查询是直接从链接池中动态获取链接,速度明显会加快.
for循环操作性能低:查询数据库的次数与for循环的层数和每层次数相关.
封装map集合:只查询一次数据库,作为辅助集合,后续业务都是对map集合进行相关操作,就不需要建立链接了,性能大幅度提高.
删除商品等级分类信息
- 需求:按照level商品等级信息,删除对应id分类信息
- URL:/itemCat/deleteItemCat + id/level
- 请求类型:delete
- 接收参数:ItemCat对象
- 返回值:SysResult
Controller层:
@DeleteMapping("/deleteItemCat")
public SysResult deleteItemCat(ItemCat itemCat){
itemCatService.deleteItemCat(itemCat);
return SysResult.success();
}
Service层:
@Transactional//添加事务
public void deleteItemCat(ItemCat itemCat) {
if (itemCat.getLevel() == 3) itemCatMapper.deleteById(itemCat.getId());
if (itemCat.getLevel() == 2) {
/*方式1
QueryWrapper<ItemCat> qw = new QueryWrapper<>();
qw.eq("parent_id", itemCat.getId());//只是删除当前二级下的所有三级数据,不用遍历所有的二级
List<ItemCat> threeList = itemCatMapper.selectList(qw);
//遍历删除三级*/
//方式2
QueryWrapper<ItemCat> qw = new QueryWrapper<>();
qw.eq("parent_id",itemCat.getId());
itemCatMapper.delete(qw);//删除三级
itemCatMapper.deleteById(itemCat.getId());//直接删除二级
}
else {//先删3,再删2,再删1
List<Integer> ids = new ArrayList<>();//用于存放要删除的二级id
//1.查询当前一级下的所有二级
QueryWrapper<ItemCat> qw = new QueryWrapper<>();
qw.eq("parent_id", itemCat.getId());
List<ItemCat> twoList = itemCatMapper.selectList(qw);
//2.根据2级删3级
for (ItemCat twoItemCat: twoList){
qw.clear();
qw.eq("parent_id", twoItemCat.getId());
itemCatMapper.delete(qw);
/*//3.删除当前2级,每次循环都要连接数据库,可以优化
itemCatMapper.deleteById(twoItemCat.getId());*/
ids.add(twoItemCat.getId());//将要删除的二级id存入集合
}
//3.一次性删除二级,不用在循环里面一个一个删除二级了
itemCatMapper.deleteBatchIds(ids);
//4.删除1级
itemCatMapper.deleteById(itemCat.getId());
}
}