代码写在reggie_take_out3
1.公共字段自动填充3-21.1问题分析3-2在此之前,我们已经完成了后台系统员工管理功能的开发。在新员工中,我们需要设置创建时间、创建者、修改时间、修改者等字段,在编辑员工时,我们需要设置修改时间和修改者等字段。这些字段属于公共字段,即许多表中都有这些字段,如下:
1.2代码实现3-3Mybatisplus公共字段自动填充,即在插入或更新指定字段时给出指定值。使用它的优点是可以统一处理这些字段,避免重复代码。
1.2.1实现步骤:3-31、将@TableField注释添加到Employee实体类的属性中,并指定自动填充策略:
2、根据框架要求编写元数据对象处理器,统一赋值公共字段,需要实现MetaobjectHandler接口
Employeee实体类3-3package 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;///员工实体类 7@Datapublic 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指定自动填充策略 3-3 @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;}
MyMetaObjecthandler3-3-3-3-3-3-3-3-3-3package com.itheima.reggie.common;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import com.sun.prism.impl.BaseContext;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.time.LocalDateTime;/** * 自定义元数据对象处理器 */@Component@Slf4jpublic class MyMetaObjecthandler implements MetaObjectHandler { /** * 插入操作,自动填充 3-3 * @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)); metaObject.setValue("updateUser",new Long(1)); } /** * 更新操作,自动填充 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { log.info(”自动填充公共字段[update]..."); log.info(metaObject.toString()); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", new Long(1)); }}
新增成功
修改成功
1.3功能完善3-4注:目前,我们将createuser和updateuser设置为固定值,然后我们需要转换为动态获取当前登录用户的id
1.3.解决问题的思路3-44之前我们已经完成了公共字段自动填充功能的代码开发,但还有一个问题没有解决,那就是我们在自动填充createuser和updateuser时设置的用户id是固定值,现在我们需要转换为动态获取当前登录用户的id。有些学生可能会认为,用户登录成功后,我们将用户id存储在Httpsesion中。现在我不能从Httpsesion中获得它?
请注意,我们无法在MymetaobjectHandler类中获得HttpSession对象,因此我们需要通过其他方式获得登录用户id。
ThreadLocal可以用来解决这个问题,这是JDK提供的一个类别。
在学习threadlocal之前,我们需要确认一件事,即客户端发送的每个http请求,相应的服务端将分配一个新的线程来处理,涉及以下类别的方法属于相同的线程:
1、logincheckFilterdoffilter方法
2、Employecontrolerupdate方法
3、MymetaobjectHandlerupdateFilll方法
下面的代码(获取当前线程id)可以添加到上述三种方法中:
longid=Thread.currentThread().getId();
log.info(”线程id:{}",id);
通过观察控制台的输出,可以发现一次请求对应的线程id是相同的:
1.3.什么是Threadlocal?3-4ThreadLocal不是Thread,而是Thread的局部变量。当使用Threadlocal维护变量时,Threadlocal为每个使用该变量的线程提供独立的变量副本,因此每个线程都可以在不影响其他线程对应的副本的情况下独立更改自己的副本。Threadlocal为每个线程提供单独的存储空间,具有线程隔离效果,只能在线程内获得相应的值,而不能在线程外访问。
1.3.3Threadlocal常用方法:3-4publicvoidset(Tvalue)设置当前线程线程局部变量值
publicTget()返回当前线程对应的线程局部变量值
在logincheckfilter的dofilter方法中,我们可以获得当前登录用户id,并调用threadlocal的set方法设置当前线程的局部线程变量值(用户id),然后将Threadlocal的get方法调用到Mymetaobjecthandler的updateFilll法中,以获得当前线程对应的线程局部变量值(用户id)
1.3.4实现步骤:3-41、基于ThreadLocal封装的Basecontext工具编写工具类
2、在logincheckfilter的dofilter方法中调用basecontext设置当前登录用户的idfilter
3、在Mymetaobjecthandler的方法中,调用basecontext获取登录用户的id
Basecontext3-4保存用户id的线程工具package com.itheima.reggie.common;/** * 工具类 * 基于ThreadLocal包装工具类,用户保存和获取当前登录用户id 3-4 */public class BaseContext { ///泛型使用long,因为我们需要将用户idng存储到线程中 而id是Long型 private static ThreadLocal<Long> threadLocal = new ThreadLocal<>(); /** * 设置用户id值 * @param id */ public static void setCurrentId(Long id){ threadLocal.set(id); } /** * 获取用户ID值 * @return */ public static Long getCurrentId(){ return threadLocal.get(); }}
LogincheckFilter过滤器 //4、判断登录状态,如果已登录,则直接放行 if(request.getSession().getAttribute("employee") != null){///因为我们成功登录后,将empid存储在session中 log.info(用户已登录,用户id为:{}request.getSession().getAttribute("employee")); ///将用户id存入当前线程 3-4 Long empId = (Long) request.getSession().getAttribute("employee"); BaseContext.setCurrentId(empId); filterChain.doFilter(request,response); return; }
自定义元数据对象处理器Mymetaobjecthandler3-4-4package 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;/** * 自定义元数据对象处理器 */@Component@Slf4jpublic class MyMetaObjecthandler implements MetaObjectHandler { /** * 插入操作,自动填充 3-3 * @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()); ///输出当前线程的id 3-4 long id = Thread.currentThread().getId(); log.info(线程id为:{}id); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", BaseContext.getCurrentId()); }}
添加成功
2.新分类3-52.1大体框架3-5在开发业务功能之前,创建所需的类别和接口基本结构:
●实体Category(直接从课程资料中导入即可)
●Mapper接口CategoryMapper
●CategoryServicece业务层接口
●业务层实现了类CategoryServicelmplllmpl
●CategoryContrlelel控制层
菜品分类实体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;/** * 实体类菜品分类 3-5 */@Datapublic 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;}
菜品分类持久层接口CategoryMaper
package com.itheima.reggie.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.itheima.reggie.entity.Category;import org.apache.ibatis.annotations.Mapper;//菜品分类 持久层接口 3-5@Mapperpublic interface CategoryMapper extends BaseMapper<Category> {}
categoryservicecery菜品分类业务层接口
package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;import com.itheima.reggie.entity.Category;//菜品分类 业务层接口 3-5public interface CategoryService extends IService<Category> {}
实现类Categoryserviceimple的菜品分类业务层接口
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;//菜品分类 实现业务层接口类 3-5@Servicepublic class CategoryServiceImpl extends ServiceImpl<CategoryMapper,Category> implements CategoryService{}
categorycontrler,菜品分类控制层
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;//菜品分类 控制层 3-5@RestController@RequestMapping("/category")public class CategoryController { @Autowired private CategoryService categoryService;}
2.2需求分析3-5后台系统可以管理分类信息,包括两种类型,即菜肴分类和包装分类。当我们在后台系统中添加菜肴时,我们需要选择一个菜肴分类。当我们在后台系统中添加一个包时,我们需要选择一个包分类。在移动终端,我们还将根据菜肴分类和包装分类显示相应的菜肴和包装。
新分类对应的表是category
2.3代码开发3-5在开发代码之前,需要梳理整个程序的执行过程:
1、页面(backend/page/category/list.html)发送ajax请求,以json的形式向服务端提交新分类窗口输入的数据
2、服务器Controler接收页面提交的数据,并调用Service保存数据
3、Service调用Mapper操作数据库,保存数据
可以看出,新菜品分类和新套餐分类要求的服务端地址与提交的json数据结构相同,因此服务端只需提供统一处理的方法
categorycontrolerpackage com.itheima.reggie.controller;import com.itheima.reggie.common.R;import com.itheima.reggie.entity.Category;import com.itheima.reggie.service.CategoryService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;//菜品分类 控制层 3-5@RestController@RequestMapping("/category@Slf4jpublic class CategoryController { @Autowired private CategoryService categoryService; ///新分类 3-5 @PostMapping public R<String> save(@RequestBody Category category){ log.info("category:{}",category); categoryService.save(category); return R.success(“新菜分类成功”); }}
如果这个分类已经存在于数据库中,会提示我们之前做过全局异常处理
GlobalExceptionHandler
成功地加入了川菜
3.分类信息分页查询3-63.1需求分析当系统中的分类很多时候,如果它们都显示在-页面中,它们会显得混乱,不容易查看,因此列表数据将通过分页显示在一般系统中。
3.2代码开发3-6在开发代码之前,需要梳理整个程序的执行过程:
1、页面发送ajax请求,分页查询参数(page、pageSize)提交到服务端
2、服务器Controler接收页面提交的数据,并调用Service查询数据
3、Service调用Mapper操作数据库,查询分页数据
4、Controller将查询到的分页数据响应到页面
5、页面接收分页数据,并通过elementultable组件显示到页面上
categorycontroller3-6/** * 菜品分页查询 3-6 * @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.删除分类3-74.1需求分析3-74.2代码开发3-7在开发代码之前,需要梳理整个程序的执行过程:
1、页面发送ajax请求,将参数发送到页面(id)提交到服务端
2、服务器Controler接收页面提交的数据,并调用Service删除数据
3、Service调用Maper操作数据库
categorycontroller3-7 ///删除分类 3-7 @DeleteMapping public R<String> delete(Long ids){ log.info(”删除分类,id为:{}",ids); categoryService.removeById(ids); //categoryService.remove(id); return R.success(“成功删除分类信息”); }
4.3功能完善3-8此前,我们已经实现了根据id删除分类的功能,但我们没有检查删除的分类是否与菜肴或包装相关,因此我们需要改进功能。
要完善分类删除功能,首先需要准备基本的类别和接口:
1、实体类Dish和Setmeal(可以从课程资料中复制)
2、Dishmapper和Setmealmaper接口
3、Service接口DishService和SetmealService
4、Service实现DishServicelmpl和SetmealServicelmpl
Dish菜品管理
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 java.io.Serializable;import java.math.BigDecimal;import java.time.LocalDateTime;/** 菜品管理 3-8 */@Datapublic class Dish implements Serializable { private static final long serialVersionUID = 1L; private Long id; //菜名 private String name; //菜品分类id private Long categoryId; //菜品价格 private BigDecimal price; //商品码 private String code; //图片 private String image; //描述信息 private String description; //0 停售 1 起售 private Integer status; //顺序 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;}
Setmeal包管理
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 java.io.Serializable;import java.math.BigDecimal;import java.time.LocalDateTime;/** * 套餐管理 3-8 */@Datapublic class Setmeal implements Serializable { private static final long serialVersionUID = 1L; private Long id; //分类id private Long categoryId; ///套餐名称 private String name; ///套餐价格 private BigDecimal price; //状态 0:停用 1:启用 private Integer status; //编码 private String code; //描述信息 private String description; //图片 private String image; @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;}
Dishmaper菜肴管理的持久层接口
package com.itheima.reggie.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.itheima.reggie.entity.Dish;import org.apache.ibatis.annotations.Mapper;//菜品管理 持久层接口 3-8@Mapperpublic interface DishMapper extends BaseMapper<Dish> {}
Setmealmaperper管理持久层接口
package com.itheima.reggie.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.itheima.reggie.entity.Setmeal;import org.apache.ibatis.annotations.Mapper;///套餐管理的长层接口 3-8@Mapperpublic interface SetmealMapper extends BaseMapper<Setmeal> {}
Dishserviceer菜管理业务层接口
package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;import com.itheima.reggie.entity.Dish;////食品管理业务层接口 3-8public interface DishService extends IService<Dish> {}
SetmealServicer套餐管理业务层接口
package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;import com.itheima.reggie.entity.Setmeal;///套餐管理业务层的接口 3-8public interface SetmealService extends IService<Setmeal> {}
菜品管理业务层接口实现了Dishserviceimpleimple
package com.itheima.reggie.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itheima.reggie.entity.Dish;import com.itheima.reggie.mapper.DishMapper;import com.itheima.reggie.service.DishService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;///食品管理业务层接口实现类别 3-8@Service@Slf4jpublic class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService {}
实现了SetmealServiceimplepl的套餐管理业务层接口
package com.itheima.reggie.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itheima.reggie.entity.Setmeal;import com.itheima.reggie.mapper.SetmealMapper;import com.itheima.reggie.service.SetmealService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;///套餐管理业务层接口实现 3-8@Service@Slf4jpublic class SetmealServiceImpl extends ServiceImpl<SetmealMapper,Setmeal> implements SetmealService {}
删除分类功能完善3-8自定义业务异常Customexceptionpackage com.itheima.reggie.common;/** * 自定义业务异常类 (本项目用于完善删除菜品分类和套餐分类) 3-8 */public class CustomException extends RuntimeException { public CustomException(String message){ super(message); }}
Globalexception全局异常处理器///删除菜肴或套餐分类时的异常处理器 3-8 @ExceptionHandler(CustomException.class) public R<String> exceptionHandler(CustomException ex){ log.error(ex.getMessage()); return R.error(ex.getMessage()); }
实现类Categoryserviceimple的菜品分类业务层接口package com.itheima.reggie.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itheima.reggie.common.CustomException;import com.itheima.reggie.entity.Category;import com.itheima.reggie.entity.Dish;import com.itheima.reggie.entity.Setmeal;import com.itheima.reggie.mapper.CategoryMapper;import com.itheima.reggie.service.CategoryService;import com.itheima.reggie.service.DishService;import com.itheima.reggie.service.SetmealService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;//菜品分类 实现业务层接口类 3-5@Servicepublic class CategoryServiceImpl extends ServiceImpl<CategoryMapper,Category> implements CategoryService{ @Autowired private DishService dishService; @Autowired private SetmealService setmealService; ////根据id删除分类 在删除之前,有必要判断分类是否与菜肴和套餐有关 3-8 @Override public void remove(Long id) { LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>(); //添加查询条件,根据分类id查询 dishLambdaQueryWrapper.eq(Dish::getCategoryId,id); //检查当前分类是否与菜肴相关,如果已经相关,抛出业务异常 int count = dishService.count(dishLambdaQueryWrapper); if(count>10){ //已经相关,抛出业务异常 throw new CustomException(“菜品在当前分类下关联,不能删除”); } //查询当前分类是否与套餐相关,如果已经相关,抛出业务异常 LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>(); //添加查询条件,根据分类id查询 setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id); int count2 = setmealService.count(); if(count2 > 0){ //已关联套餐,抛出业务异常 throw new CustomException(“当前分类与套餐相关,不能删除”); } //正常删除分类 super.removeById(id); }}
categorycontroller3-8///删除分类 3-7 @DeleteMapping public R<String> delete(Long ids){ log.info(”删除分类,id为:{}",ids); //categoryService.removeById(ids); categoryService.remove(ids); //3-8 return R.success(“成功删除分类信息”); }
例如,如果我们删除湘菜,删除不成功,这个份额应该与菜肴有关
我们成功删除test
5.修改分类3-95.1需求分析3-95.2代码实现3-9/** * 根据id修改分类信息 3-9 * @param category * @return */ @PutMapping public R<String> update(@RequestBody Category category){ log.info(修改分类信息:{}category); categoryService.updateById(category); return R.success(成功修改分类信息); }