【瑞吉外卖】-复盘(三)
课程内容
- 公共字段自动填充
- 新增分类
- 分类信息分页查询
- 删除分类
- 修改分类
1、公共字段自动填充
在前面开发的员工管理功能中,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间、修改人等字段。这些字段属于公共字段。如果在每个插入或修改数据的地方都手动设置这些字段,编码过于冗余。
因此,我们可以使用Mybatis Plus提供的公共字段自动填充功能。
实现步骤
- 在实体类的属性上加入@TableField注解,指定自动填充的策略。
- 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口
代码实现
1)在实体类的属性中假如@TableField注解,指定自动填充的策略
@TableField(fill=FieldFill.INSERT) //插入时填充字段
private LocalDateTime createTime;
@TableField(fill=FieldFill.INSERT_UPDATE) //插入和更新时填充字段
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT) //插入时填充字段
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新时填充字段
private Long updateUser;
2)按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口。
使用ThreadLocal传递当前登录用户id
在修改或插入员工信息,业务的执行流程如下:
客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:
1)LoginCheckFilter的doFilter方法
2)EmployeeController的update方法
3)MyMetaObjectHandler的updateFill方法
ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问当前线程的值。
我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的局部变量,然后再MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get()方法来获得当前线程所对应的用户id。
2、新增分类
菜品分类与套餐分类共享一张表,也共享一套增删改查的接口。
3、分类信息分页查询
/**
* 分页查询
* @param page
* @param pageSize
* @return
*/
@GetMapping("/page")
public R<Page> page(int page,int pageSize){
//分页构造器
Page<Category> pageInfo = new Page<>(page,pageSize);
//条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
//添加排序条件,根据sort进行排序
queryWrapper.orderByAsc(Category::getSort);
//分页查询
categoryService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
4、删除分类
需要注意的是:删除的分类需要检查是否关联了菜品或者套餐,如果关联了那么则不能删除。
自定义异常
在业务逻辑操作过程中,如果遇到一些业务参数、操作异常的情况下,我们直接抛出此异常。
/**
* 自定义业务异常类
*/
public class CustomException extends RuntimeException {
public CustomException(String message){
super(message);
}
}
在CategoryService中扩展remove方法,并在CategoryServiceImpl中实现remove方法。如果发现当前类关联了菜品或套餐,抛出一个业务异常:
@Autowired
private DishService dishService;
@Autowired
private SetmealService setmealService;
/**
* 根据id删除分类,删除之前需要进行判断
* @param id
*/
@Override
public void remove(Long id) {
//添加查询条件,根据分类id进行查询菜品数据
LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);
int count1 = dishService.count(dishLambdaQueryWrapper);
//如果已经关联,抛出一个业务异常
if(count1 > 0){
throw new CustomException("当前分类下关联了菜品,不能删除");//已经关联菜品,抛出一个业务异常
}
//查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常
LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);
int count2 = setmealService.count(setmealLambdaQueryWrapper);
if(count2 > 0){
throw new CustomException("当前分类下关联了套餐,不能删除");//已经关联套餐,抛出一个业务异常
}
//正常删除分类
super.removeById(id);
}
在GlobalExceptionHandler中处理自定义异常
/**
* 异常处理方法
* @return
*/
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
5、修改分类
到这里,我们并没有开发根据ID查询数据,进行页面回显的功能,但是页面的分类数据却能够回显回来。这里是因为前端直接使用了分页查询时查到的分类数据,从里面找到当前row的数据进行回显。
那么接下来我们就只需要开发一个方法,修改操作的方法即可:
/**
* 根据id修改分类信息
* @param category
* @return
*/
@PutMapping
public R<String> update(@RequestBody Category category){
log.info("修改分类信息:{}",category);
categoryService.updateById(category);
return R.success("修改分类信息成功");
}
遇到的注解
- @Component:是spring中的一个注解,它的作用是实现bean的注入。在Java的Web开发中,提供3个@Component注解衍生注解(功能与@Component一样)分别是:
- @Controller控制器(注入服务)用于标注控制层
- @Service服务(注入dao)用于标注服务层,主要用来进行业务逻辑处理
- @Repository(实现dao访问)用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件
- @TableField:修饰实体类的属性,在插入或者更新时会根据元数据处理器对其进行自动填充。