瑞吉外賣實戰項目全攻略——第三天

瑞吉外賣實戰項目全攻略——第三天

該系列將記錄一份完整的實戰項目的完成過程,該篇屬於第三天

案例來自B站黑馬程序員Java項目實戰《瑞吉外賣》,請結合課程資料閱讀以下內容

該篇我們將完成以下內容:

  • 公共字段自動填充

  • 新添分類

  • 分類信息分頁查詢

  • 刪除分類

  • 修改分類

公共字段自動填充

我們的功能開發一般分為三個階段

需求分析

前面我們已經完成了後台系統的員工系統的開發,在新增或修改員工時需要填寫創建時間創建人修改時間修改人等繁雜信息

而且這些屬性基本在後續的菜品,套餐中都有所體現,我們把這些字段稱為公共字段,所以我們希望採用一種統一的方法來設置:

  • MyBatisPlus為我們提供了公共字段自動填充的功能

我們先來簡單介紹一下流程:

  1. 首先在我們需要修改的字段屬性上添加註解:
// 屬性包括有INSERT,UPDATE,INSERT_UPDATE

@TableField(fill = FieldFill.屬性)
  1. 按照框架書寫元數據對象處理器,在此類中統一為公共字段設置值,需要實現MetaObjectHandler接口
package com.qiuluo.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
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 添加時自動設置
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {

    }

    /**
     * 修改時自動設置
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {

    }
}

代碼實現

首先我們為實體類的待修改屬性添加上註解:

package com.qiuluo.reggie.domain;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public 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(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;

}

在common文件夾下創建新的MyMetaObjectHandler處理器:

package com.qiuluo.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
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 添加時自動設置
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("修改元數據");
        // 我們可以在這裡統一設置公共字段的值
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        // 但是關於修改或創建人我們無法設置,因為我們無法得知目前是誰修改,這裡暫時用Long(1)代替
        metaObject.setValue("createUser", new Long(1));
        metaObject.setValue("updateUser", new Long(1));
    }

    /**
     * 修改時自動設置
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", new Long(1));
    }
}

之後,再將我們的服務層中相關設置屬性代碼去掉即可

功能完善

在上面我們已經完成基本的公共字段的設置,但是我們會注意到我們無法設置相關人的信息

因為我們在之前的服務層中採用Request來獲得當前Session下的保存的員工id,但目前我們無法獲得Request

但是我們可以採用線程的單一性來獲得當前線程下存儲的內容

每次客戶端發送的每次http請求,對應的服務器都會分配一個新的線程來處理,在處理過程中設計到下面類方法都屬於一個相同的線程:

  • LoginCheckFilter的doFilter方法
  • EmployeeController的update方法
  • MyMetaObjectHandler的updateFill方法

驗證方法可以採用獲得並比較當前線程:

// 通過該方法獲得當前線程的id
long id = Thread.currentThread().getId();
// 以日誌的形式輸出即可
log.info("當前線程id:" + id);

正常情況下我們會得到三個線程相同的id

那麼我們就可以利用線程相同的原理,在當前線程中直接存儲id,再在MyMetaObjectHandler中獲得id

我們主要採用ThreadLocal,我們簡單介紹一下ThreadLocal:

  • ThreadLocal並不是一-個Thread,而是Thread的局部變量。
  • 當使用ThreadLocal維護變量時,每個使用該變量的線程具有獨立的變量副本。
  • 每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
  • ThreadLocal為每個線程提供單獨一份存儲空間,具有線程隔離的效果,只有在線程內才能獲取到對應的值,線程外則不能訪問。

其中Thread Local主要只有兩個方法:

// 設置當前線程的線程局部變量的值
public void set(T value);
// 返回當前線程所對應的局部變量的值
public T get();

下面我們演示相關步驟:

  1. 設置一個工具類,用於使用當前線程的方法
package com.qiuluo.reggie.common;

/**
 * 基於ThreadLocal的工具類,用於保存用戶id
 */
public class BaseContext {
    // 設置一個ThreadLocal,存儲Long型(id)
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    // 存儲方法,調用set
    public static void setCurrentId(Long id){
        threadLocal.set(id);
    }

    // 獲得方法,調用get
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}
  1. 在LoginCheckFilter的doFilter中獲得用戶id,並set儲存
package com.qiuluo.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.qiuluo.reggie.common.BaseContext;
import com.qiuluo.reggie.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 檢查用戶是否已經完成登錄
 */
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter{

    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String requestURI = request.getRequestURI();

        log.info("攔截到請求:{}",requestURI);

        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };

        boolean check = check(urls, requestURI);

        if(check){
            log.info("本次請求{}不需要處理",requestURI);
            filterChain.doFilter(request,response);
            return;
        }

        if(request.getSession().getAttribute("employee") != null){
            log.info("用戶已登錄,用戶id為:{}",request.getSession().getAttribute("employee"));

            log.info("線程id" + Thread.currentThread().getId());

            // 注意這裡:我們直接使用工具類的方法來設置線程局部變量
            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);

            filterChain.doFilter(request,response);
            return;
        }

        log.info("用戶未登錄");
        response.getWriter().write(JSON.toJSONString(Result.error("NOTLOGIN")));
        return;

    }
    public boolean check(String[] urls,String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if(match){
                return true;
            }
        }
        return false;
    }
}
  1. 在MyMetaObjectHandler的updateFill方法中獲得用戶id,並加載進相關屬性
package com.qiuluo.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
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 添加時自動設置
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("修改元數據");
        log.info("線程id" + Thread.currentThread().getId());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        // 我們調用工具類方法來使用ThreadLocal獲得當前id並賦值
        metaObject.setValue("createUser", BaseContext.getCurrentId());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }

    /**
     * 修改時自動設置
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}

實際測試

測試方法很簡單,我們直接在主頁中點擊新建或修改方法,到數據庫內部查看修改時間或修改人即可

新增分類

我們的功能開發一般分為三個階段

需求分析

根據需求,我們需要設置分類管理信息

分類信息存放在一張表中,分為為菜品分類和套餐分類,我們用數據庫的type屬性來區分兩種分類

我們來到前端,分別點擊菜品分類和套餐分類的創建,F12查看傳輸數據就會發現:

點擊菜品分類,這裡不僅傳輸了我們頁面書寫的name和sort,還額外傳遞了type屬性表示是菜品分類

因而我們只需要將數據傳入即可,不用分心設置是菜品分類還是套餐分類,直接書寫代碼即可

代碼實現

因為是新的實體類,我們需要重新構造一系列domain,mapper等內容

  1. 實體類
package com.qiuluo.reggie.domain;

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;

}
  1. 數據層
package com.qiuluo.reggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiuluo.reggie.domain.Category;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}
  1. 業務層接口
package com.qiuluo.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;

public interface CategoryService extends IService<Category> {
}
  1. 業務層實現類
package com.qiuluo.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiuluo.reggie.common.CustomException;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.Setmeal;
import com.qiuluo.reggie.mapper.CategoryMapper;
import com.qiuluo.reggie.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}
  1. 服務層並書寫方法
package com.qiuluo.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {

    // 自動裝配
    @Autowired
    private CategoryServiceImpl categoryService;

    // 查看前端url請求,為post類型,後面沒有跟網頁設置
    @PostMapping
    public Result<String> save(@RequestBody Category category){
        // 創建時間/人,修改時間/人均已統一設置,我們只需要將數據保存進數據庫即可
        categoryService.save(category);
        return Result.success("新增成功");
    }

}

實際測試

來到主頁面,新添後去數據庫查看相關信息即可

分類信息分頁查詢

我們的功能開發一般分為三個階段

需求分析

我們需要將數據展現到網頁中,同時防止數據過多擁擠,我們採用分頁查詢的方法給出數據:

我們在之前的員工分類中已經完成了分頁插件的創建,所以這次我們只需要刷新頁面查看url以及數據即可:

直接實現相關代碼即可

代碼實現

我們直接在CategoryController中實現方法即可:

package com.qiuluo.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {

    @Autowired
    private CategoryServiceImpl categoryService;

    /**
    * 分頁方法
    */

    @GetMapping("/page")
    public Result<Page> page(int page,int pageSize){
        
        // 創建Page,並載入參數
        Page pageImpl = new Page(page,pageSize);

        categoryService.page(pageImpl);

        // 我們不需要做匹配,但是我們需要按照sort的數值比對來進行排列,所以依舊需要創建LambdaQueryWrapper匹配器
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(Category::getSort);

        // 調用分頁方法
        categoryService.page(pageImpl,queryWrapper);

        // 數據返回
        return Result.success(pageImpl);
    }

}

實際測試

我們直接打開頁面,刷新頁面,有數據出現即可

刪除分類

我們的功能開發一般分為三個階段

需求分析

我們點擊頁面後,可以查看後面有一個刪除操作,點擊後我們會刪除該套餐:

但是請注意噹噹前套餐中有相關菜品時,我們如果刪除,那麼菜品將無法顯示,所以我們還需要設置條件當該套餐中出現菜品時無法刪除

我們的套餐中的菜品信息並非存儲在套餐數據庫中,而是存儲在Dish和Setmeal數據表中:

所以我們需要創建這兩者的基本信息,並在Category的業務層中修改默認代碼,創建一個符合我們要求的方法

代碼實現

首先我們將創建Dish和Setmeal的基本信息,下面不做配置,這裡僅作簡單羅列(和之前配置完全相同):

  • Dish-Setmeal的實現類
  • Dish-Setmeal的數據層
  • Dish-Setmeal的業務層接口
  • Dish-Setmeal的業務層
  • Dish-Setmeal的服務層

接下來我們來介紹代碼的正式實現:

  1. 首先去數據庫業務層接口定義我們需要實現的方法接口:
package com.qiuluo.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;

public interface CategoryService extends IService<Category> {

    // 書寫我們需要的方法
    public Result<String> remove(Long id);
}
  1. 再去數據庫業務層實現我們定義的方法:
package com.qiuluo.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiuluo.reggie.common.CustomException;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.Setmeal;
import com.qiuluo.reggie.mapper.CategoryMapper;
import com.qiuluo.reggie.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

    // 我們需要使用到DishServiceImpl,SetmealServiceImpl來查詢是否有菜品相連,自動裝配即可
    
    @Autowired
    private DishServiceImpl dishService;

    @Autowired
    private SetmealServiceImpl setmealService;

    // 實現方法
    public Result<String> remove(Long id){

        // 判斷是否有菜品相連
        LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
        dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);

        int count1 = dishService.count(dishLambdaQueryWrapper);

        if (count1 > 0){
            // 如果有菜品相連,我們先拋出業務異常,這裡暫時不實現,我們在後面實現
        }

        // 判斷是否有套餐相連
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);

        int count2 = setmealService.count(setmealLambdaQueryWrapper);

        if (count2 > 0){
            // 如果有套餐相連,我們先拋出業務異常,這裡暫時不實現,我們在後面實現
        }

        // 均無相連,採用父類的根據id刪除方法,並返回成功信息
        super.removeById(id);

        return Result.success("成功刪除");
    }

}
  1. 我們在前面拋出了業務異常,我們去定義一個自定義異常:
package com.qiuluo.reggie.common;

/**
 * 自定義業務異常類
 * 注意:自定義異常都需要繼承RuntimeException
 */
public class CustomException extends RuntimeException{

    public CustomException(String message){
        super(message);
    }
}
  1. 去我們的總的異常處理器中添加處理該異常的方法:
package com.qiuluo.reggie.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局異常處理
 * @ControllerAdvice 來書寫需要修改異常的註解類(該類中包含以下注解)
 * @ResponseBody 因為返回數據為JSON數據,需要進行格式轉換
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 處理異常
     * @ExceptionHandler 來書寫需要修改的異常
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){

        log.error(ex.getMessage());
        if (ex.getMessage().contains("Duplicate entry")){
            String[] split = ex.getMessage().split(" ");
            String msg = split[2] + "已存在";
            return Result.error(msg);
        }
        return Result.error("未知錯誤");
    }

    /**
     * 處理自定義異常
     * @ExceptionHandler 來書寫需要修改的異常
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public Result<String> CustomExceptionHandler(CustomException ex){

        // 我們直接獲得異常中攜帶的信息並返回即可
        log.error(ex.getMessage());

        return Result.error(ex.getMessage());
    }
}
  1. 回到業務層相對應位置拋出異常
package com.qiuluo.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiuluo.reggie.common.CustomException;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.domain.Dish;
import com.qiuluo.reggie.domain.Setmeal;
import com.qiuluo.reggie.mapper.CategoryMapper;
import com.qiuluo.reggie.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

    @Autowired
    private DishServiceImpl dishService;

    @Autowired
    private SetmealServiceImpl setmealService;

    // 實現方法
    public Result<String> remove(Long id){

        // 判斷是否有菜品相連
        LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
        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);

        return Result.success("成功刪除");
    }

}
  1. 最後我們到服務層實現該接口
package com.qiuluo.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {

    @Autowired
    private CategoryServiceImpl categoryService;
    
    // 這裡需要注意:前端傳來的id名稱為ids(資料中帶有的),參數搞錯了是無法匹配的
    @DeleteMapping
    public Result<String> delete(Long ids){

        categoryService.remove(ids);

        return Result.success("刪除成功");
    }
}

實際測試

回到主頁面,點擊一個自己創建的分類的刪除鍵,分類消失

回到主頁面,點擊一個系統創建的分類的刪除鍵,分類存在並彈出彈框顯示已有關聯無法刪除

修改分類

我們的功能開發一般分為三個階段

需求分析

我們打開修改界面,點擊修改後查看相關url以及參數即可

url如下:

參數如下:

我們會發現修改分類實際是根據id來修改分類,其中傳遞的參數實際上是一個Category實現類

那麼我們直接書寫代碼即可

代碼實現

我們直接在服務層書寫代碼:

package com.qiuluo.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qiuluo.reggie.common.Result;
import com.qiuluo.reggie.domain.Category;
import com.qiuluo.reggie.service.impl.CategoryServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {

    @Autowired
    private CategoryServiceImpl categoryService;

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

        categoryService.updateById(category);

        return Result.success("修改成功");
    }

}

實際測試

我們直接回到主頁面,點擊修改,來到後台查看數據實現即可

易錯點

在這裡我們會點出該項目目前容易出錯的位置

工具類的使用

在公共字段自動填充的部分,我們為了使用ThreadLocal從而創建了相對的工具類

我們的工具類就是為了便捷操作而使用的,我們為了使用相關的參數但同時多次不用創建實體而直接使用工具類

例如我們的ThreadLocal的工具類中:

package com.qiuluo.reggie.common;

// 我們直接創建了實體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();
    }
}

異常處理

我們平時遇到的異常都是產生錯誤由系統拋出的異常

只有真實的項目中我們才會需要創建自己定義的異常,難點在於異常的創建格式以及將異常加入異常處理器中

首先我們需要創建自定義異常:

package com.qiuluo.reggie.common;

/**
 * 自定義業務異常類
 * 注意:一定要繼承RuntimeException
 * 繼承RuntimeException之後我們才能直接拋出該異常
 */
public class CustomException extends RuntimeException{

    // 內部直接書寫一個構造方法,因為我們拋出異常時都是直接new一個新的異常(手動書寫)
    public CustomException(String message){
        // 存儲一個簡單的反饋信息
        super(message);
    }
}

再之後我們需要將該異常加入到異常處理器中:

package com.qiuluo.reggie.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局異常處理
 * @ControllerAdvice 來書寫需要修改異常的註解類(該類中包含以下注解)
 * @ResponseBody 因為返回數據為JSON數據,需要進行格式轉換
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 處理自定義異常
     * @ExceptionHandler 來書寫需要修改的異常
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public Result<String> CustomExceptionHandler(CustomException ex){

        // 我們不需要做過多處理,我們只是將大部分需要拋出異常的部分整合起來在這裡統一處理
        return Result.error(ex.getMessage());
    }
}

結束語

該篇內容到這裡就結束了,希望能為你帶來幫助~

附錄

該文章屬於學習內容,具體參考B站黑馬程序員的Java項目實戰《瑞吉外賣》

這裡附上視頻鏈接:業務開發Day3-01-本章內容介紹_嗶哩嗶哩_bilibili