目录
一、公共字段填充
1.1 问题分析
1.2 代码实现
把相关的注解加在需要mybatis-plus自动帮我们填充的字段上面 @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;
然后设置一个处理类:在此类中为公共字段赋值,需要实现接口;
package com.learn.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 //注意:这个要记得交给spring容器管理,不然这个功能就没发用。。。。 //那么怎么确定你要添加的功能是不是要交给容器管理呢?就是你直接写了一个工具类或者是功能类,需要对数据库的数据或者是数据库数据的结果产生影响的时候,你明明写了这样一个类,但是功能却没有生效,那么这个时候就要首先考虑是不是容器没有托管这个类 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", new Long(1)); //这里的id是不能直接获取的,所以这里先写死,后面教怎么动态获取员工id metaObject.setValue("updateUser", new Long(1)); } /** * 更新操作,自动填充 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { log.info("公共字段自动填充[update]..."); log.info(metaObject.toString()); long id = Thread.currentThread().getId(); log.info("线程id为:{}",id); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", new Long(1)); } }
什么是ThreadLocal
然后为了动态的获取员工的id,这里我们使用了threadLocal这个局部变量来获取和存储员工id;
创建一个工具类来设置和获取threadLocal中的员工id, 注意:要先把数据设置进threadLocal中,才能获取到
package com.learn.reggie.common; /** * 基于ThreadLocal封装工具类,用户保存和获取当前登录用户的id */ public class BaseContext { private static ThreadLocal<Long> threadLocal = new ThreadLocal<>(); /** * 设置值 * @param id */ public static void setCurrentId(Long id){ threadLocal.set(id); } /** * 获取值 * @return */ public static Long getCurrentId(){ return threadLocal.get(); } }
在前面我们写的LoginCheckFilter这个过滤器中,把这个地方的代码加上添加和保存id的代码
//4、判断登录状态,如果已登录,则直接放行 if (request.getSession().getAttribute("employee") != null) { log.info("用户已登录,用户id为:{}", request.getSession().getAttribute("employee")); //把用户id存储到本地的ThreadLocal Long empId = (Long) request.getSession().getAttribute("employee"); BaseContext.setCurrentId(empId); filterChain.doFilter(request, response); return; }
把处理器中的静态id改为动态获取: metaObject.setValue("createUser", BaseContext.getCurrentId()); metaObject.setValue("updateUser",BaseContext.getCurrentId());
功能测试
二、新增分类
2.1 需求分析
2.2 数据模型
从资料去复制实体Category类到entity包;
数据库中的表结构:
2.3 代码开发
创建mapper:
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.Category; import org.apache.ibatis.annotations.Mapper; /** * @author 咕咕猫 * @version 1.0 */ @Mapper public interface CategoryMapper extends BaseMapper<Category> { }
创建service:
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.learn.reggie.entity.Category; import com.learn.reggie.mapper.CategoryMapper; import com.learn.reggie.service.CategoryService; import org.springframework.stereotype.Service; /** * @author 咕咕猫 * @version 1.0 */ @Service public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService { }
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.learn.reggie.entity.Category; import com.learn.reggie.mapper.CategoryMapper; import com.learn.reggie.service.CategoryService; import org.springframework.stereotype.Service; /** * @author 咕咕猫 * @version 1.0 */ @Service public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService { }
编写controller:
我们发现新增菜品分类的请求地址是:http://localhost:8080/category
提交的数据格式为: {name: "湘菜", type: "1", sort: "1"}
/** * 新增套餐分类 * @param category * @return */ @PostMapping public R<String> save(@RequestBody Category category) { log.info("category:{}", category); categoryService.save(category); return R.success("新增分类成功"); }
功能测试:登录后,点击添加新增菜品分类,看是否成功,数据库的数据是否变化;
三、菜品类的分页
3.1 需求分析
3.2 代码开发
/** * 分页查询 * * @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.1 需求分析
4.2 代码开发
代码实现: 注意这里的删除功能是不完整的,因为可能需要删除的数据是与其他表关联的,所以删除之前要先判断该条数据是否与其他表中的数据关联;
/** * 根据id来删除分类的数据 * @param id * @return */ @DeleteMapping() public R<String> delete(@RequestParam("ids") Long ids){ //注意这里前端传过来的数据是ids categoryService.removeById(ids); return R.success("分类信息删除成功"); }
功能完善
创建对应的mapper:
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.Dish; import org.apache.ibatis.annotations.Mapper; /** * @author 咕咕猫 * @version 1.0 */ @Mapper public interface DishMapper extends BaseMapper<Dish> { }
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.Setmeal; import org.apache.ibatis.annotations.Mapper; /** * @author 咕咕猫 * @version 1.0 */ @Mapper public interface SetmealMapper extends BaseMapper<Setmeal> { }
创建service:
package com.learn.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.learn.reggie.dto.DishDto; import com.learn.reggie.entity.Dish; /** * @author 咕咕猫 * @version 1.0 */ public interface DishService extends IService<Dish> { }
package com.learn.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.learn.reggie.entity.Setmeal; /** * @author 咕咕猫 * @version 1.0 */ public interface SetmealService extends IService<Setmeal> { }
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.learn.reggie.dto.DishDto; import com.learn.reggie.entity.Dish; import com.learn.reggie.entity.DishFlavor; import com.learn.reggie.mapper.DishMapper; import com.learn.reggie.service.DishFlavorService; import com.learn.reggie.service.DishService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; /** * @author 咕咕猫 * @version 1.0 */ @Service @Slf4j public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService { }
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.learn.reggie.entity.Setmeal; import com.learn.reggie.mapper.SetmealMapper; import com.learn.reggie.service.SetmealService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * @author 咕咕猫 * @version 1.0 */ @Service @Slf4j public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService { }
添加自定义的service方法:(就是我们需要的业务mybatis没有提供,所以就需要自己另外在service创建新的方法,并且在相关的业务中实现)
//在CategoryService中定义自己需要的方法,直接写就行 void remove(Long id);
在CategoryService实现类中重写该方法:
自定义异常类,因为这里需要抛异常了:package com.learn.reggie.common; /** * 自定义业务异常 */ public class CustomException extends RuntimeException{ public CustomException(String message){ super(message); } }
//然后在外面前面写的GlobalExceptionHandler全局异常捕获器中添加该异常,这样就可以把相关的异常信息显示给前端操作的人员看见 /** * 处理自定义的异常,为了让前端展示我们的异常信息,这里需要把异常进行全局捕获,然后返回给前端 * @param exception * @return */ @ExceptionHandler(CustomException.class) public R<String> exceptionHandle(CustomException exception){ log.error(exception.getMessage()); //这里拿到的message是业务类抛出的异常信息,我们把它显示到前端 return R.error(exception.getMessage()); }
/** * 根据id删除分类,删除之前需要进行判断 * @param id */ @Override public void remove(Long id) { LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>(); //添加查询条件,根据分类id进行查询 dishLambdaQueryWrapper.eq(Dish::getCategoryId,id); //注意:这里使用count方法的时候一定要传入条件查询的对象,否则计数会出现问题,计算出来的是全部的数据的条数 int count1 = dishService.count(dishLambdaQueryWrapper); //查询当前分类是否关联了菜品,如果已经关联,直接抛出一个业务异常 if (count1 > 0){ //已经关联菜品,直接抛出一个业务异常 throw new CustomException("当前分类下关联了菜品,不能删除"); } //查询当前分类是否关联了套餐,如果已经关联,直接抛出一个业务异常 LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>(); //添加查询条件,根据分类id进行查询 setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id); //注意:这里使用count方法的时候一定要传入条件查询的对象,否则计数会出现问题,计算出来的是全部的数据的条数 int count2 = setmealService.count(setmealLambdaQueryWrapper); if (count2 > 0){ //已经关联套餐,直接抛出一个业务异常 throw new CustomException("当前分类下关联了套餐,不能删除"); } //正常删除分类 super.removeById(id); }
然后在controller调用刚刚实现的方法就行:把之前的remove方法给删除就行,重新调用我们自己实现的方法;
/** * 根据id来删除分类的数据 * @param id * @return */ @DeleteMapping public R<String> delete(@RequestParam("ids") Long id){ //注意这里前端传过来的数据是ids categoryService.remove(id); return R.success("分类信息删除成功"); }
测试:自己添加测试数据测试就行;记得一定要测试一下删除有相关联的数据,看会不会删除和在前端提示异常信息;
五、修改分类
5.1 需求分析
5.2 代码开发
/** * 根据id修改分类信息 * @param category * @return */ @PutMapping public R<String> update(@RequestBody Category category){ log.info("修改分类信息:{}",category); categoryService.updateById(category); return R.success("修改分类信息成功"); }
记得在对应的实体类加上公共字段的值设置:前面我们配置了这个,所以这里只需要加注解就行;
//创建时间 @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;