文章目录
- 1. 公共字段自动填充
- 1.1 问题分析
- 1.2 代码实现
- 1.3 代码完善
- 2. 新增分类
- 2.1 需求分析
- 2.2 数据模型
- 2.3 代码开发
- 3. 分类信息分页查询
- 3.1 代码开发
- 4. 删除分类
- 4.1 需求分析
- 4.2 代码开发
- 4.3 功能完善
- 5. 修改分类
1. 公共字段自动填充
1.1 问题分析
-
在后台系统的员工管理功能开发中,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间和修改人等字段。这些字段属于公共字段,也就是很多表中都有这些字段。
-
对于这些公共字段在某个地方统一处理,来简化开发 ——> 使用Mybatis Plus提供的
公共字段自动填充
功能。
1.2 代码实现
1、在实体类的属性上加入@TableField注解,指定自动填充的策略
2、按照框架要求编写元数据对象处理器
,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入操作,自动填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充insert...");
log.info(metaObject.toString());
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
/**
* 更新操作,自动填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充update...");
log.info(metaObject.toString());
log.info("线程id为:{}",Thread.currentThread().getId());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}
1.3 代码完善
-
– 问题:在自动填充createUser和updateUser时设置的用户id是固定值,现在需要改造成动态获取当前登录用户的id。
– 在
MyMetaObjectHandler类
中是不能获得HttpSession对象
的,所以我们需要通过其他方式来获取登录用户id。– 可以使用
ThreadLocal
来解决此问题,它是JDK中提供的一个类。 -
在学习ThreadLocal之前,我们需要先确认一个事情,就是客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:
1、LogincheckFilter的doFilter方法
2、EmployeeController的update方法
3、MyMetaObjectHandler的updateFill方法-
可在上述三个方法中添加代码测试:
log.info("线程id为:{}",Thread.currentThread().getId());
-
-
什么是ThreadLocal
ThreadLocal
并不是一个Thread,而是Thread的局部变量
。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本
,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。1)在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id)
2)在MyMetaObjectHandler的updateFill方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。
-
实现步骤
1、编写BaseContext工具类,基于ThreadLocal封装的工具类
2、在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id
3、在MyMetaobjectHandler的方法中调用BaseContext获取登录用户的id
public class BaseContext { public static ThreadLocal<Long> threadLocal = new ThreadLocal<>(); public static void setCurrentId(Long id){ threadLocal.set(id); } public static Long getCurrentId(){ return threadLocal.get(); } }
2. 新增分类
2.1 需求分析
- 后台系统中可以管理分类信息,分类包括两种类型,分别是菜品分类和套餐分类。
- 当在后台系统中添加菜品时需要选择一个菜品分类,当在后台系统中添加一个套餐时需要选择一个套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐。
2.2 数据模型
- category 表
2.3 代码开发
-
用到的类和接口
- 实体类Category
- Mapper接口CategoryMapper
- 业务层接口CategoryService
- 业务层实现类CategoryServicelmpl
- 控制层CategoryController
-
controller层代码
/** * 新增分类 * @param category * @return */ @PostMapping public R<String> save(@RequestBody Category category){ log.info("新增category: {}",category); categoryService.save(category); return R.success("新增分类成功!"); }
3. 分类信息分页查询
3.1 代码开发
-
程序执行过程
1、页面发送ajax请求,将分页查询参数(page,pageSize)提交到服务端
2、服务端Controller接收页面提交的数据并调用Service查询数据
3、Service调用Mapper操作数据库,查询分页数据
4、Controller将查询到的分页数据响应给页面
5、页面接收到分页数据并通过ElementUI的Table组件展示到页面上
-
controller层代码
/** * 分页查询 * @param page * @param pageSize * @return */ @GetMapping("page") public R<Page> page(int page, int pageSize){ log.info("分页查询page {}, pageSize {}",page, 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. 删除分类
4.1 需求分析
- 在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是
当分类关联了菜品或者套餐
时,此分类不允许删除
。——> 此时,给用户提示不能删除
4.2 代码开发
-
1、页面发送ajax请求,将参数(id)提交到服务端
2、服务端Controller接收页面提交的数据并调用Service删除数据
3、Service调用Mapper操作数据库
4.3 功能完善
-
前面实现了根据id删除分类的功能,但是并没有检查删除的分类
**是否关联了菜品或者套餐**
,所以我们需要进行功能完善。 -
准备基础的类和接口
1、实体类Dish和Setmeal(从课程资料中复制即可)
2、Mapper接口DishMapper和SetmealMapper
3、Service接口DishService和SetmealService
4、Service实现类DishServicelmpl和SetmealServicelmpl
-
提供自定义异常类 CustomerException
public class CustomerException extends RuntimeException{ /** * 将异常错误信息传入 * @param message */ public CustomerException(String message){ super(message); } }
-
自定义CustomerException异常的处理方法
/** * 自定义CustomerException异常的处理方法 * @param ex * @return */ @ExceptionHandler(CustomerException.class) public R<String> exceptionHandler(CustomerException ex){ log.info(ex.getMessage()); return R.error(ex.getMessage()); }
-
DishServiceImpl中自定义根据id删除分类前的判断方法
@Service public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService { @Autowired private DishService dishService; @Autowired private SetmealService setmealService; /** * 根据id删除分类前,需判断是否与菜品或套餐关联 * @param id */ @Override public void remove(Long id) { LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>(); dishLambdaQueryWrapper.eq(Dish::getCategoryId, id); int count1 = dishService.count(dishLambdaQueryWrapper); // 查询当前分类是否与菜品关联,若关联,抛出一个业务异常 if(count1 > 0){ throw new CustomerException("当前分类关联了菜品,不能删除"); } LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>(); setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id); int count2 = setmealService.count(setmealLambdaQueryWrapper); // 查询当前分类是否与套餐关联,若关联,抛出一个业务异常 if(count2 > 0){ throw new CustomerException("当前分类关联了套餐,不能删除"); } // 正常删除分类 super.removeById(id); } }
5. 修改分类
-
在分类管理列表,点击修改按钮,在修改窗口回显分类信息并进行修改。
-
/** * 根据id修改分类信息 * @param category * @return */ @PutMapping public R<String> update(@RequestBody Category category){ log.info("修改分类信息:{}",category); categoryService.updateById(category); return R.success("分类信息修改成功!"); }