公共字段自动填充
需求分析
对于之前的开发中,有创建时间、创建人、修改时间、修改人等字段,在其他功能中也会有出现,属于公共字段
,对于这些公共字段最好是在某个地方统一处理以简化开发,使用Mybatis Plus
提供的公共字段自动填充
功能
代码实现
- 在实体类的属性上加入
@TableField
注解,指定自动填充的策略 - 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现
MetaObjectHandler
接口
修改步骤
- 先在实体类中的
公共字段
加上自动填充的注释
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;
- 修改控制类中的一些代码
将这些属性的值统一的在insertFill
方法中实现。
package com.springboot.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
@Slf4j
/*** 自定义元数据处理器*//*** 插入操作,自动填充* @param metaObject*/public class MyMetaObjecthandler implements MetaObjectHandler {@Overridepublic 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*/@Overridepublic void updateFill(MetaObject metaObject) {log.info("公共字段自动填充[update]...");log.info(metaObject.toString());metaObject.setValue("updateTime",LocalDateTime.now());metaObject.setValue("updateUser",new Long(1));}
}
问题:自动填充时设置的用户id是固定值,目的是要动态获取当前登录用户的id
解决办法:使用ThreadLocal
(用户登录成功,我们将用户id
存在了HttpSession
中,但是在MyMetaObjectHandler
类中是不能获取HttpSession
对象的)
关于ThreadLocal
客户端每次发送的http请求,对于的服务端都会分配一个新的线程来处理
LoginCheckFilter
中的doFilter
方法、EmployeeController
中的update
方法、MyMetaObjectHandler
的updateFill
方法都属于相同的一个线程
即一次请求,对应的线程id都是相同的,所以可以得到相同的值
package com.springboot.reggie.common;
/*** 基于ThreadLocal封装工具类 用于保存和获取当前登录用户的id*/public class BaseContext {private static ThreadLocal<Long>/*用户id是Long型*/ threadLocal = new ThreadLocal<>();/***设置值* @param id*/public static void setCurrentId(Long id)//设置成静态的 因为这是工具方法{threadLocal.set(id);}/*** 获取值* @return id*/public static Long getCurrentId(){return threadLocal.get();}
}
package com.springboot.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.springboot.reggie.common.R;
import com.springboot.reggie.entity.Employee;
import com.springboot.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {@Autowiredprivate EmployeeService employeeService;/*** 员工登录* @param request* @param employee* @return*/@PostMapping("/login")public R<Employee> login(HttpServletRequest request,@RequestBody Employee employee){//1、将页面提交的密码password进行md5加密处理String password = employee.getPassword();password = DigestUtils.md5DigestAsHex(password.getBytes());//2、根据页面提交的用户名username查询数据库LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(Employee::getUsername,employee.getUsername());Employee emp = employeeService.getOne(queryWrapper);//3、如果没有查询到则返回登录失败结果if(emp == null){return R.error("登录失败");}//4、密码比对,如果不一致则返回登录失败结果if(!emp.getPassword().equals(password)){return R.error("登录失败");}//5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果if(emp.getStatus() == 0){return R.error("账号已禁用");}//6、登录成功,将员工id存入Session并返回登录成功结果request.getSession().setAttribute("employee",emp.getId());return R.success(emp);}/*** 员工退出* @param request* @return*/@PostMapping("/logout")public R<String> logout(HttpServletRequest request){//清理Session中保存的当前登录员工的idrequest.getSession().removeAttribute("employee");return R.success("退出成功");}/*** 新增员工* @param employee* @return*/@PostMappingpublic R<String> save(HttpServletRequest request,@RequestBody Employee employee){log.info("新增员工,员工信息:{}",employee.toString());//设置初始密码123456,需要进行md5加密处理employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));// employee.setCreateTime(LocalDateTime.now());//employee.setUpdateTime(LocalDateTime.now());//获得当前登录用户的id//Long empId = (Long) request.getSession().getAttribute("employee");//employee.setCreateUser(empId);//employee.setUpdateUser(empId);employeeService.save(employee);return R.success("新增员工成功");}/*** 员工信息分页查询* @param page* @param pageSize* @param name* @return*/@GetMapping("/page")public R<Page> page(int page,int pageSize,String name){log.info("page = {},pageSize = {},name = {}" ,page,pageSize,name);//构造分页构造器Page pageInfo = new Page(page,pageSize);//构造条件构造器LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper();//添加过滤条件queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);//添加排序条件queryWrapper.orderByDesc(Employee::getUpdateTime);//执行查询employeeService.page(pageInfo,queryWrapper);return R.success(pageInfo);}/*** 根据id修改员工信息* @param employee* @return*/@PutMappingpublic R<String> update(HttpServletRequest request,@RequestBody Employee employee){log.info(employee.toString());//Long empId = (Long)request.getSession().getAttribute("employee");// employee.setUpdateTime(LocalDateTime.now());//employee.setUpdateUser(empId);employeeService.updateById(employee);return R.success("员工信息修改成功");}/*** 根据id查询员工信息* @param id* @return*/@GetMapping("/{id}")public R<Employee> getById(@PathVariable Long id){log.info("根据id查询员工信息...");Employee employee = employeeService.getById(id);if(employee != null){return R.success(employee);}return R.error("没有查询到对应员工信息");}
}
解决一下之前“玄学报错”的问题:原因是启动类中少了@ServletComponentScan
下面是引用的某ai的回答,没系统学过springboot,这些东西也不懂,之后还得学。。还有比较长的路要走。
@ServletComponentScan是一个Spring Boot注解,用于扫描和注册Servlet、Filter和Servlet监听器。
在传统的Java Web应用程序中,我们需要在web.xml文件中配置Servlet、Filter和Servlet监听器。但是,使用Spring Boot时,可以使用注解来代替web.xml的配置。
@ServletComponentScan注解可以在Spring Boot的主配置类上使用,它会自动扫描指定包及其子包下的所有Servlet、Filter和Servlet监听器,并将它们注册到Servlet容器中。
使用@ServletComponentScan注解的步骤如下:
在Spring Boot的主配置类上添加@ServletComponentScan注解。
在指定的包及其子包下创建Servlet、Filter和Servlet监听器。
在创建的Servlet、Filter和Servlet监听器类上使用@WebServlet、@WebFilter和@WebListener等注解进行配置。
通过使用@ServletComponentScan注解,Servlet、Filter和Servlet监听器就可以通过注解的方式进行配置和注册,而不需要再手动编写web.xml文件。
需要注意的是,@ServletComponentScan注解只在使用嵌入式Servlet容器(如Tomcat)时有效,对于外部部署的WAR包,它不会生效。
总结:@ServletComponentScan是一个Spring Boot注解,用于自动扫描和注册Servlet、Filter和Servlet监听器,简化了配置和注册的过程。
新增分类
需求分析
菜品分类和套餐分类,添加菜品时选择菜品分类,添加套餐时选择套餐分类。
通过前端和设计的数据库看:
代码设计
实体类
package com.springboot.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;}
- 页面发送ajax请求,将新增分类窗口输入的数据以json形式提交到服务端
- 服务端Controller接收页面提交的数据并调用Service将数据进行保存
- Service调用Mapper操作数据,保存数据
通过前端发送给后台的请求发现新增菜品和新增套餐分类请求的服务端地址和提交的json数据结构相同,所以服务端只需要提供一个方法统一处理。
添加淮扬菜成功
代码不放了
分类信息分页查询
package com.springboot.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.springboot.reggie.common.R;
import com.springboot.reggie.entity.Category;
import com.springboot.reggie.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/*** 分类管理*/
@RestController
@RequestMapping("/category")
@Slf4j
public class CategoryController {@Autowiredprivate CategoryService categoryService;/*** 新增分类* @param category* @return*/@PostMappingpublic R<String> save(@RequestBody Category category){log.info("category{}",category);categoryService.save(category);return R.success("新增分类成功");}/*** 分页查询* @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);// return null;}
}
删除分类
- 页面发送ajax请求,将参数
id
提交到服务端 - 服务端
Controller
接受页面提交的数据并调用Service
删除数据 Service
调用Mapper
操作数据库
简单删除
/***删除分类 根据id删除分类* @param id* @return*/@DeleteMappingpublic R<String> delete(Long id){log.info("删除分类,id为:{}",id);categoryService.removeById(id);return R.success("删除分类成功...");//return null;}
完善删除功能
上述的删除只是简单的直接删除菜品或者是套餐,但是没有检查删除的分类是否关联了菜品或者套餐需要进行完善。
准备好实体类(菜品和菜单)
尝试删除“湘菜”,湘菜是关联了套餐的,删除失败。
添加一个无关联的菜品或者套餐,然后再删除,之前一直是报错,原因是没有添加@Autowired
注解。
@Autowired是Spring框架中的一个注解,用于自动装配(自动注入)对象的依赖关系。在Spring容器中,使用@Autowired注解可以实现自动装配,即根据指定的类型,自动在容器中查找匹配的bean,并将其注入到目标对象中。
@Autowired可以用在构造方法、成员变量、方法以及参数上。当用在构造方法上时,Spring会自动在容器中查找匹配的bean,并将其作为参数传入构造方法中。当用在成员变量上时,Spring会自动在容器中查找匹配的bean,并将其注入到成员变量中。当用在方法上时,Spring会自动在容器中查找匹配的bean,并将其作为方法的参数传入。当用在方法参数上时,Spring会自动在容器中查找匹配的bean,并将其作为方法参数传入。
使用@Autowired注解可以简化代码,减少手动配置的工作量。它可以减少了因为对象之间的依赖关系而需要手动创建对象和设置依赖关系的繁琐工作。同时,它也增加了代码的可读性和可维护性,使得代码更加清晰和简洁。
需要注意的是,使用@Autowired注解时,需要先确保Spring容器中存在匹配的bean。如果存在多个匹配的bean,可以使用@Qualifier注解或者通过指定名称的方式来指定具体要注入的bean。如果没有找到匹配的bean,会抛出异常。
package com.springboot.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.springboot.reggie.common.R;
import com.springboot.reggie.entity.Category;
import com.springboot.reggie.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** 分类管理*/
@RestController
@RequestMapping("/category")
@Slf4j
public class CategoryController {@Autowiredprivate CategoryService categoryService;public Long id;/*** 新增分类* @param category* @return*/@PostMappingpublic R<String> save(@RequestBody Category category){log.info("category{}",category);categoryService.save(category);return R.success("新增分类成功");}/*** 分页查询* @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);// return null;}/***删除分类 根据id删除分类* @param id* @return*/@DeleteMapping()// @RequestParampublic R<String> delete(Long id){//this.ids = ids;log.info("删除分类,id为:{}",id);categoryService.remove(id);return R.success("删除分类成功...");//return null;}}
package com.springboot.reggie.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.springboot.reggie.common.CustomException;
import com.springboot.reggie.entity.Category;import com.springboot.reggie.entity.Dish;
import com.springboot.reggie.entity.Setmeal;
import com.springboot.reggie.mapper.CategoryMapper;import com.springboot.reggie.service.CategoryService;import com.springboot.reggie.service.DishService;
import com.springboot.reggie.service.SetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper,Category> implements CategoryService{@Autowiredprivate DishService dishService;//录入菜品分类@Autowiredprivate SetmealService setmealService;//录入套餐分类/*** 根据id删除分类 删除之前需要进行判断* @param id*/@Overridepublic void remove (Long id){//调用框架中的查询方法LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加查询条件 根据分类id进行查询dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);//统计当前分类下关联了几个菜品int count = dishService.count(dishLambdaQueryWrapper);//如果当前分类下关联的菜品个数大于0 说明有菜品关联 不能删//查询当前分类是否关联了菜品 如果已关联 抛出一个业务异常if(count>0){throw new CustomException("当前分类下关联了菜品,不能删除!");}//查询当前分类是否关联了套餐 如果已关联 抛出一个业务异常LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();//添加查询条件 根据分类id进行查询setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);int cnt = setmealService.count(setmealLambdaQueryWrapper);//如果当前分类下关联的菜品个数大于0 说明有套餐关联 不能删if(cnt>0){throw new CustomException("当前分类下关联了套餐,不能删除!");}//正常删除分类super.removeById(id);}}
修改分类
需求分析
点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作
代码
/*** 根据id修改分类信息* @param category* @return*/@PutMappingpublic R<String> update(@RequestBody Category category){log.info("修改分类信息:{}",category);categoryService.updateById(category);return R.success("修改分类信息成功...");}