分类管理业务开发
1、公共字段自动填充
**需求分析:**将create_time、update_time、create_user、update_user这些字段的设置抽取到一个公共类中进行设置
**实现方法:**使用mybatis-plus的公共字段自动填充功能实现
实现步骤:
- 在实体类的属性上加入@TableField注解,指定自动填充的策略
- 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类的实现需要MetaObjectHandler接口
1.1、修改Employee类
package com.itheima.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @Author: xjhqre
* @DateTime: 2022/6/15 16:45
*/
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber; // 身份证号码
private Integer status;
@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;
}
1.2、创建MyMetaObjectHandler
**实现难点:**无法在MyMetaObjectHandler类中使用Session获取登陆用户的id来设置修改和创建者的id
**解决方法:**使用ThreadLocal解决
**实现原理:**客户端发送的每个http请求,服务端都会有一个对应的线程来处理,以下三个方法都是由同一个线程来处理的
- LoginCheckFilter的doFilter方法
- EmployeeController的update方法
- MyMetaObjectHandler的updateFill方法
所以只需要在LoginCheckFilter的doFilter方法中分配一个Threadlocal存储用户id,即可在MyMetaObjectHandler的updateFill方法使用
实现步骤:
- 编写BaseContext工具类,基于ThreadLocal封装的工具类
- 在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登陆用户的id
- 在MyMetaObjectHandler的方法中调用BaseContext获取用户的id
1.2.1、创建BaseContext
package com.itheima.reggie.common;
/**
* 基于ThreadLocal封装的工具类,保存和获取当前登陆用户的id
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId() {
return threadLocal.get();
}
}
1.2.2、修改LoginCheckFilter
在doFilter方法中,添加红框中的代码
1.2.3、实现MyMetaObjectHandler代码
package com.itheima.reggie.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert]...");
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update]...");
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
}
}
1.3、删除以前的设置代码
以上步骤做完后可以删除以前新增员工方法和修改员工方法里对公共字段设置的代码
save方法:
update方法:
1.4、测试是否成功
2、搭建分类业务架构
2.1、创建Category实体类
package com.itheima.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 分类
*/
@Data
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//类型 1 菜品分类 2 套餐分类
private Integer type;
//分类名称
private String name;
//顺序
private Integer sort;
//创建时间
@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;
//是否删除
private Integer isDeleted;
}
2.2、创建CategoryMapper
package com.itheima.reggie.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Category;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}
2.3、创建CategoryService
package com.itheima.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Category;
public interface CategoryService extends IService<Category> {
}
2.4、创建CategoryServiceImpl
package com.itheima.reggie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.mapper.CategoryMapper;
import com.itheima.reggie.service.CategoryService;
import org.springframework.stereotype.Service;
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}
2.5、创建CategoryController
package com.itheima.reggie.controller;
import com.itheima.reggie.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
}
3、新增分类
执行流程:
- 页面(backend/page/category/list.html)发送ajax请求,将新增分类窗口输入的数据以JSON的形式提交到服务端
- 服务端Controller接收页面提交的数据并调用Service将数据进行保存
- Service调用Mapper操作数据库,保存数据
3.1、新增分类方法
在CategoryController类中创建save方法
/**
* 新增分类
* @param category
* @return
*/
@PostMapping
public R<String> save(@RequestBody Category category) {
log.info("category: {}", category);
categoryService.save(category);
return R.success("新增分类成功");
}
3.2、测试新增分类
4、分页查询分类信息
执行流程:
- 页面发送ajax请求,将分页查询参数(page, pageSize)提交到服务端
- 服务端Controller接收页面提交的数据并调用Service查询数据
- Service调用Mapper操作数据库,查询分页数据
- Controller将查询到的分页数据响应给页面
- 页面接收到分页数据并通过ElementUl的Table组件展示到页面上
4.1、创建分页查询方法
在CategoryController里创建page方法
/**
* 分页查询
* @param page
* @param pageSize
* @return
*/
@PostMapping("/page")
public R<Page<Category>> 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.2、测试
访问分类管理页面查看效果
4.3、请求出错
程序报错:Unknown column ‘is_deleted’ in ‘field list’
原因是Category实体类里有一个属性isDeleted,而数据库里没有相应的字段。当前阶段把这个属性注释掉即可
5、删除分类
执行流程:
- 页面发送ajax请求,将参数(ids)提交到服务端
- 服务端Controller接收页面提交的数据并调用Service删除数据
- Service调用Mapper操作数据库
5.1、创建基本的删除方法
在CategoryController创建delete方法
/**
* 根据id删除分类
* @param ids
* @return
*/
@DeleteMapping
public R<String> delete(Long ids) {
log.info("删除分类,id为:{}", ids);
categoryService.removeById(ids);
return R.success("分类信息删除成功");
}
5.2、完善删除功能
需要准备的类和接口
- 实体类Dish和Setmeal
- Mapper接口DishMapper和SetmealMapper
- Service接口DishService和SetmealMapper
- Service实现类DishServiceImpl和SetmealServiceImpl
5.3、在Service中创建删除方法
package com.itheima.reggie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Category;
/**
* @Author: xjhqre
* @DateTime: 2022/6/17 21:21
*/
public interface CategoryService extends IService<Category> {
void remove(Long ids);
}
5.4、自定义业务异常
package com.itheima.reggie.common;
public class CustomException extends RuntimeException{
public CustomException(String message) {
super(message);
}
}
5.5、捕获自定义业务异常
在全局异常捕获类GlobalExceptionHandler中重载exceptionHandler方法
/**
* 自定义业务异常处理
* @param ex
* @return
*/
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex) {
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
5.6、修改CategoryController里的删除方法
调用我们自己编写的remove方法
/**
* 根据id删除分类
* @param ids
* @return
*/
@DeleteMapping
public R<String> delete(Long ids) {
log.info("删除分类,id为:{}", ids);
categoryService.remove(ids);
return R.success("分类信息删除成功");
}
5.7、删除测试
6、修改分类
6.1、创建修改方法
/**
* 根据id修改分类信息
* @param category
* @return
*/
@PutMapping
public R<String> update(@RequestBody Category category) {
log.info("修改分类信息:{}", category);
categoryService.updateById(category);
return R.success("修改分类信息成功");
}