黑马程序员Java项目实战《瑞吉外卖》 笔记 Day3:视频P40-P48

黑马程序员Java项目实战《瑞吉外卖》 笔记

本课程以当前热门的外卖点餐为业务基础,业务真实、实用、广泛。基于流行的Spring Boot、mybatis plus等技术框架进行开发,带领学员体验真实项目开发流程、需求分析过程和代码实现过程。学完本课程能够收获:锻炼需求分析能力、编码能力、bug调试能力,增长开发经验。

  • 链接:https://www.bilibili.com/video/BV13a411q753

20230320 Day3:视频P40-P48

  • 公共字段自动填充
  • 新增分类
  • 分类信息分页查询
  • 删除分类
  • 修改分类

公共字段自动填充:

问题分析:

前面我们已经完成了后台系统的员工管理功能开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字 段,在编辑员工时需要设置修改时间和修改人等字段。这些字段属于公共字段,也就是很多表中都有这些字段。能不能对于这些公共字段在某个地方统一处理,来简化开发呢?
答案就是使用Mybatis Plus提供的公共字段自动填充功能。

代码实现:

Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以
统一对这些字段进行处理,避免了重复代码。
实现步骤:
1、在实体类的属性上加入@TableField注解,指定自动填充的策略
2、按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

在common包下新建MyMetaObjectHandler类,继承MetaObjectHandler接口,并实现insertFill和updateFill的两个方法

package com.raggie.common;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;

public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        
    }

    @Override
    public void updateFill(MetaObject metaObject) {

    }
}

向EmployeeController中,涉及到公共信息的赋值添加注释

@PostMapping
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
    //设置初始密码为123456,进行md5加密处理
    employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));

//        employee.setCreateTime(LocalDateTime.now());
//        employee.setUpdateTime(LocalDateTime.now());
//        Long empID = (Long) request.getSession().getAttribute("employee");
//        employee.setCreateUser(empID);
//        employee.setUpdateUser(empID);

    employeeService.save(employee);

    log.info("新增员工,员工信息:{}",employee.toString());
    return R.success("新增员工成功!");
}
@PutMapping
public  R<String> update(HttpServletRequest request,@RequestBody Employee employee){
    log.info(employee.toString ());
//        Long empID = (Long) request.getSession().getAttribute("employee");
//        employee.setUpdateUser(empID);
//        employee.setUpdateTime(LocalDateTime.now());
    employeeService.updateById(employee);
    log.info("员工信息修改为:{}",employee.toString());
    return R.success("员工信息修改成功!");
}

在MyMetaObjectHandler设置自动填充属性赋值

package com.raggie.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]...");
        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));
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]...");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",new Long(1));
    }
}

注意:当前我们设置createUser和updateUser:为固定值 后面我们需要进行改造,改为动态获得当前登录用户的id

功能完善:

前面我们已经完成了公共字段自动填充功能的代码开发,但是还有一个问题没有解决,就是我们在自动填充createUser和 updateUser时设置的用户id是固定值,
现在我们需要改造成动态获取当前登录用户的id。有的同学可能想到,用户登录成功后我们将用户id存入了HttpSession中,现在我从HttpSession中获取不就行了?
注意,我们在MyMetaObjectHandler类中是不能获得HttpSession对象的,所以我们需要通过其他方式来获取登录用户id。 可以使用ThreadLocal来解决此问题,它是DK中提供的一个类。

实现步骤:

1、编写BaseContext.工具类,基于ThreadLocal封装的工具类
2、在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id
3、在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

在common包中新建工具类BaseContext,用于调用ThreadLocal保存用户id

package com.raggie.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();
    }
}

回到MyMetaObjectHandler中,修改updateUser和createUser,调用BaseContext获取id值

package com.raggie.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]...");
        log.info(metaObject.toString());

        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]...");
        log.info(metaObject.toString());

        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }
}

新增分类:

需求分析:

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

在开发业务功能前,先将需要用到的类和接口基本结构创建好:

  • 实体类Category(直接从课程资料中导入即可)
package com.raggie.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;

}
  • Mapper:接口CategoryMapper
package com.raggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raggie.entity.Category;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CategoryMapper extends BaseMapper<Category> {

}

  • 业务层接口CategoryService
package com.raggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.raggie.entity.Category;

public interface CategoryService extends IService<Category> {

}

  • 业务层实现类CategoryServicelmpl
package com.raggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.raggie.entity.Category;
import com.raggie.mapper.CategoryMapper;
import com.raggie.service.CategoryService;
import org.springframework.stereotype.Service;

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

}

  • 控制层CategoryController
package com.raggie.controller;

import com.raggie.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

}

在CategoryController创建save方法,用于保存菜品信息,添加@PostMapping注解。
调用categoryService的save方法保存信息,并调用R.success将操作成功信息返回给前端。

@PostMapping()
public R<String> save(@RequestBody Category category) {
    categoryService.save(category);
    log.info("菜品保存成功,信息为:{}",category.toString());
    return R.success("菜品保存成功!");
}

分类信息分页查询:

仿照员工分页查询,在CategoryController中新建Page方法

@GetMapping("/page")
public R<Page> page(int page, int pageSize){
    log.info("接收到查询请求,page={},pagesize={},name={}",page,pageSize);
    //构造分页构造器
    Page pageinfo=new Page(page,pageSize);
    //构造条件构造器
    LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper();
    //添加排序条件
    queryWrapper.orderByAsc(Category::getSort);
    //执行查询
    categoryService.page(pageinfo,queryWrapper);
    return R.success(pageinfo);
    }

删除分类:

代码开发

在开发代码之前,需要梳理一下整个程序的执行过程:
1、页面发送ajax请求,将参数(id)提交到服务端
2、服务端Controller接收页面提交的数据并调用Service删除数据
3、Service调用Mapper操作数据库

在CategoryController中新建Delete方法,调用categoryService的removeById方法

@DeleteMapping()
public R<String> delete(Long id){
    log.info("分类{}删除成功",id);
    categoryService.removeById(id);
    return R.success("分类删除成功!");
}

功能完善

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

要完善分类删除功能,需要先准备基础的类和接口:
1、实体类Dish和Setmeal(从课程资料中复制即可)

package com.raggie.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;

/**
 * 套餐
 */
@Data
public 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;
}

package com.raggie.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;

/**
 菜品
 */
@Data
public 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;

}

2、Mapperi接口DishMapper和SetmealMapper

package com.raggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raggie.entity.Dish;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface DishMapper extends BaseMapper<Dish> {

}

package com.raggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.raggie.entity.Setmeal;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal> {
    
}

3、Service:接▣DishService和SetmealService

package com.raggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.raggie.entity.Dish;

public interface DishService extends IService<Dish> {

}


package com.raggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.raggie.entity.Setmeal;

public interface SetmealService extends IService<Setmeal> {

}

4、Service:实现类DishServicelmpli和SetmealServicelmpl

package com.raggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.raggie.entity.Dish;
import com.raggie.mapper.DishMapper;
import com.raggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    
}

package com.raggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.raggie.entity.Setmeal;
import com.raggie.mapper.SetmealMapper;
import com.raggie.service.SetmealService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal>implements SetmealService {
    
}

在CategoryService创建remove方法

package com.raggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.raggie.entity.Category;

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

}

在CategoryServiceImpl调用DishService和SetmealService

private DishService dishService;
private SetmealService setmealService;

查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常

LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);
int count = dishService.count(dishLambdaQueryWrapper);
if(count>0){
    //已经关联了菜品

}

查询当前分类是否关联了套餐,如果已经关朕,抛出一个业务异常

LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);
count = setmealService.count(setmealLambdaQueryWrapper);
if(count>0){
    //已经关联了套餐
    
}

若都没有关联,则调用removeById删除分类

super.removeById(id);

在common中创建自定义业务异常类CustomException,继承RuntimeException

package com.raggie.common;

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

在remove方法中抛出异常

public void remove(Long id) {
    //查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常
    LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
    dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);
    int count = dishService.count(dishLambdaQueryWrapper);
    if(count>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);
    }

在CustomException中创建异常处理方法

@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
    log.error(ex.getMessage());
    return R.error(ex.getMessage());
}

回到CategoryController中,在delete中调用刚刚修改的remove方法删除分类

public R<String> delete(Long id){
    log.info("分类{}删除成功",id);
    categoryService.remove(id);

    return R.success("分类删除成功!");
}

修改分类:

需求分析

在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作

在中CategoryController创建update方法,添加@PutMapping注解,调用categoryService的updateById方法修改分类信息,并返回值

@PutMapping
public R<String> update(@RequestBody Category category){
    log.info("修改分类信息:{}",category.toString());
    categoryService.updateById(category);
    R.success("修改分类信息成功!");
}

你可能感兴趣的:(java,mybatis,spring,boot)