【从零开始分析项目实战】09-分类管理业务开发(Mybatisplus公共字段自动填充功能的实现)

注:本文章基于黑马程序员相关视频及资料进行编写,代码简单,较容易理解,若有问题或者源码资料获取可以在评论区留言或者联系作者!



一、公共字段自动填充

(1)问题分析:
前面我们已经完成了后台系统的员工管理功能的开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间和修改人等字段,这些字段属于公共字段,也就是很多表中都有这些字段,这时候我们可以使用mybatis plus提供的公共字段自动填充功能

Mybatis plus的公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值。使用它的好处就是可以统一对这些字段进行处理,避免了重复代码;

(2)实现步骤:

  1. 在实体类的属性上加入TableFiled注解。指定自动填充的策略;
  2. 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口;

(3)编码实现
加入@Tablefiled注解:

@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;

因为在MyMetaObjectHandler 类中是无法使用HttpServletRequest类,所以是无法从session域汇总获取到用户id的,这里我们先将用户id写死验证功能是否正常运行;

编写元数据处理类继承MetaObjectHandler类并实现指定方法:

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", new Long(1));
        metaObject.setValue("updateUser", new Long(1));
        log.info(metaObject.toString());
    }

运行程序,新增用户,可以正常插入公共字段;
在这里插入图片描述

(4)公共字段填充功能完善:
客户端发送的每一次http请求,对应的都会在服务端分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都是属于一个线程的:
1.LoginCheckFilter的doFilter方法
2.EmployeeController的update方法
3.MyMetaObjectHandler的updateFill方法;

ThreadLocal并不是一个Thread。而是Thread的局部变量。当使用ThreaLocal维护变量的时候,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都独立的改变自己副本,而不会影响到其它线程所对应的副本。ThreadLocal为每个线程都提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到相应的值,线程外则不能访问到

实现步骤:

  1. 编写BaseContext工具类,基于ThreadLocal封装的工具类
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. 在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id
//获取用户登录的id,传给ThreadLocal变量
            Long empid = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empid);
  1. 在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id
 metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
  1. 运行项目,进行新增操作,能够自动进行公共字段的填入;
    在这里插入图片描述

二、新增分类

(1)需求分析:后台系统中可以管理分类信息,分裂包括两种类型,分别是菜品分类和套餐分类。当我们在后台系统中添加菜品时需要一个菜品分类,当我们在后台系统中添加一个套餐的时候需要选择一个套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐;

(2)代码开发(大体框架)

  • 实体类Category(这里省略)
  • Mapper接口CategoryMapper
@mapper
public interface CategoryMapper extends BaseMapper<Category> {
}

  • 业务层接口CategoryService
public interface CategroyService extends IService<Category> {
}
  • 业务层实现类CategoryServiceImpl
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}
  • 控制层CategoryController
@RestController
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    CategoryService categoryService;
}

(3)CategoryController具体业务实现:接受前端传来的json数据,然后封装成为一个category对象,使用categoryService进行插入,由于数据库加了name唯一的索引,所以当插入一个已经存在的name值的时候,就会抛出异常,之前定义的全局异常处理器会返回错误信息给前端,后面成功返回代码不再执行;

@PostMapping
    public R<String> save(@RequestBody Category category){
        log.info("categroy"+category);
        categoryService.save(category);
        return R.success("新增分类成功");

(4)运行项目,检查插入功能,当插入重复值时,会提示错误信息,当输入不存在的信息时,能够成功插入;
在这里插入图片描述


三、分类信息分页查询

(1)需求分析:系统中的分类很多的时候,如果在一个页面中全部展示出来会显的比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据;

(2)程序执行流程;

  1. 页面发送ajax请求,将分页查询参数(page,pagesize)提交到服务器
  2. 服务端controller接受页面提交的数据并调用Service查询数据
  3. Service调用Mapper操作数据库,查询分页数据
  4. Controller将查询到的分页数据响应给页面
  5. 页面接受到查询到分页数据展示到页面上

(3)编写分页查询业务逻辑:

@GetMapping("/page")
    public R<Page>  page(int page, int pageSize){
        //分页构造器
        Page<Category> pageInfo=new Page<>(page,pageSize);
        //条件构造器
        LambdaQueryWrapper<Category> queryWrapper=new LambdaQueryWrapper<>();
        //添加排序条件
        queryWrapper.orderByAsc(Category::getSort);
        //进行分页的查询
        categoryService.page(pageInfo,queryWrapper);

        return R.success(pageInfo);

(4)运行项目,访问分页查询,能够正常显示数据;
在这里插入图片描述


四、删除分类

(1)需求分析:
在分类管理页面,可以对某个分类进行删除操作,需要注意的是当分类关联了菜品或者套餐的时候,此分类不允许删除;

(2)执行流程:

  1. 页面发送Ajax请求,将参数id提交到服务端
  2. 服务端Controller接受页面提交的数据调用Service删除数据
  3. Service调用Mapper操作数据库
    编码实现:
    @DeleteMapping
    public R<String> delete(Long ids){
        categoryService.removeById(ids);
        return R.success("分类信息删除成功");

    }

四、删除分类完善

(1)功能完善:
前面我们已经实现了根据id删除分类的功能,但是并没有检查删除的分类是否关联了菜品或者套餐,所以我们需要进行功能完善;

(2)创建大体框架:
由于删除分类时需要判断当前分类下是否含有菜品和套餐,所以我们首先需要引入Dish和Setmeal的大体框架

  1. 实体类Dish和Setmeal
  2. Mapper接口DishMapper和SetmealMapper
  3. Service接口DishService和SetmealService
  4. Service实现类DishServiceImpl和SetmealServiceImpl

(2)在CategoryService中新建删除方法;

public interface CategoryService extends IService<Category> {
    public void remove(Long id);
}

(3)在CategoryServiceImpl中对方法进行业务逻辑的编写,首先对Setmeal表和Dish表进行查询,看是否含有与分类id相关联的字段,若有则抛出异常,若没有则进行删除操作;

 /*
    * 根据id删除分类,删除之前需要进行判断*/
    @Override
    public void remove(Long id) {
        LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件,根据分类id进行查询
        dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);
        int count1 = dishService.count(dishLambdaQueryWrapper);
        //判断当前分类是否关联了菜品,如果已经关联,抛出一个业务异常
        if (count1>0){
            //已经关联菜品,抛出一个业务异常
            throw new CustomException("当前分类下关联了菜品,无法删除");
        }

        //判断当前套餐是否关联了菜品,如果已经关联,抛出一个业务异常
        LambdaQueryWrapper<Setmeal> SetmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        SetmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);
        int count2 = setmealService.count(SetmealLambdaQueryWrapper);
        if (count2>0){
            //已经关联套餐,抛出一个业务异常
            throw new CustomException("当前分类下关联了套餐,不能删除");


        }

        //正常删除分类
        super.removeById(id);

    }

(4)编写一个异常处理类CustomException,用于处理删除分类操作抛出的异常;

public class CustomException extends RuntimeException {
    public CustomException(String message){
        super(message);
    }
}

(5)在GlobalExceptionHandler全局异常处理器上加入CustomException,并编写异常接受后的操作;

 @ExceptionHandler(CustomException.class)//处理此种异常方法
    public R<String> exceptionHandler(CustomException ex){
        log.info(ex.getMessage());


        return R.error(ex.getMessage());
    }

(6)运行项目,当删除含有菜品或者套餐的分类时,页面会反馈错误信息;
在这里插入图片描述

五、修改分类

修改分类功能较为简单,前端将修改后的分类的数据传给后端,后端通过数据库更新,修改数据;
在这里插入图片描述

   @PutMapping
    public R<String> update( @RequestBody Category category){

        log.info("修改分类信息:{}",category);

        categoryService.updateById(category);
        return R.success("修改分类信息成功");

    }

在这里插入图片描述
在这里插入图片描述


如果感觉内容写的还不错的话,一键三连不迷路!!!!
后面将会更新更多学习内容,一起学习吧!!!!!!
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PoJo123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值