南充北京网站建设,wordpress安装主题后找不到后台入口,网页制作新建站点步骤,有创意的域名黑马程序员Java项目实战《瑞吉外卖》#xff0c;轻松掌握springboot mybatis plus开发核心技术的真java实战项目——第二部分 1.员工管理模块1.1 完善登陆功能1.2 新增员工1.2.1 全局异常捕获 1.3 员工信息分页查询1.4 启用/禁用员工账号1.4.1 使用自定义消息转换器 1.5 编辑… 黑马程序员Java项目实战《瑞吉外卖》轻松掌握springboot mybatis plus开发核心技术的真java实战项目——第二部分 1.员工管理模块1.1 完善登陆功能1.2 新增员工1.2.1 全局异常捕获 1.3 员工信息分页查询1.4 启用/禁用员工账号1.4.1 使用自定义消息转换器 1.5 编辑员工信息 2. 菜品分类管理2.1 公共字段填充(这里有重点)2.2 新增分类2.3 菜品类的分页2.4 删除分类这里有注意点2.5 修改分类 1.员工管理模块 1.1 完善登陆功能
问题分析前面的登陆存在一个问题如果用户不进行登陆直接访问系统的首页照样可以正常访问这种设计是不合理的我们希望看到的效果是只有完成了登陆后才可以访问系统中的页面如果没有登陆则跳转到登陆页面 那么如何实现
答案就是使用过滤器或者是拦截器在拦截器或者是过滤器中判断用户是否已经完成了登陆如果没有登陆则跳转到登陆页面
代码实现这里使用的是过滤器 ①创建自定义过滤器LongCheckFilter
package com.itheima.reggie.filter;import lombok.extern.slf4j.Slf4j;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 {Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest (HttpServletRequest) servletRequest;HttpServletResponse httpServletResponse (HttpServletResponse) servletResponse;log.info(拦截到请求{},httpServletRequest.getRequestURI());filterChain.doFilter(httpServletRequest,httpServletResponse);}
}
②在启动类加上注解ServletComponentScan
然后先测试一下过滤器能不能生效具体的逻辑等下再书写发送请求看后台能不能打印拦截的信息
package com.itheima.reggie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;Slf4j
SpringBootApplication
ServletComponentScan
public class ReggieApplication {public static void main(String[] args) {SpringApplication.run(ReggieApplication.class,args);log.info(项目启动成功...);}
}③完善过滤器的处理逻辑
过滤器具体的处理逻辑如下:
获取本次请求的URI判断本次请求是否需要处理如果不需要处理则直接放行判断登录状态如果已登录则直接放行如果未登录则返回未登录结果
)
具体逻辑的代码实现
package com.itheima.reggie.filter;import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.R;
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();Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//对请求和响应进行强转,我们需要的是带http的HttpServletRequest request (HttpServletRequest) servletRequest;HttpServletResponse response (HttpServletResponse) servletResponse;//1、获取本次请求的URIString requestURL request.getRequestURI();//定义不需要处理的请求路径 比如静态资源(静态页面我们不需要拦截,因为此时的静态页面是没有数据的)String[] urls new String[]{/employee/login,/employee/logout,/backend/**,/front/**};//做调试用的//log.info(拦截到请求{},requestURL);//2、判断本次请求是否需要处理boolean check check(urls, requestURL);//3、如果不需要处理则直接放行if(check){//log.info(本次请求{}不需要处理,requestURL);filterChain.doFilter(request,response);return;}//4、判断登录状态如果已登录则直接放行if(request.getSession().getAttribute(employee) ! null){//log.info(用户已登录用户id为{},request.getSession().getAttribute(employee));filterChain.doFilter(request,response);return;}//log.info(用户未登录);//5、如果未登录则返回未登录结果通过输出流方式向客户端页面响应数据,具体响应什么数据看前端的需求然后前端会根据登陆状态做页面跳转response.getWriter().write(JSON.toJSONString(R.error(NOTLOGIN)));return;}/*** 路径匹配检查本次请求是否需要放行* param urls* param requestURI* return*/public boolean check(String[] urls,String requestURI){for (String url : urls) {//把浏览器发过来的请求和我们定义的不拦截的url做比较匹配则放行boolean match PATH_MATCHER.match(url, requestURI);if(match){return true;}}return false;}
}
功能测试: 发起几个请求看看后台的输出和能不能访问到资源里面的数据和能不能跳转注意上面的后台日志代码已经被注释需要在后台看到日志的话需要把注释去掉 1.2 新增员工 数据模型
新增员工其实就是将我们的新增页面录入的员工数据插入到employee表注意employee表中对username字段加入了唯一的约束因为username是员工的登陆账号必须是唯一的 employee表中的status字段默认设置为1表示员工状态可以正常登陆 代码开发
在开发代码之前需要梳理一下整个程序的执行过程:
页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端服务端Controller接收页面提交的数据并调用Service将数据进行保存Service调用Mapper操作数据库保存数据 /*** 新增员工* param employee* return*/PostMapping()//因为请求就是 /employee 在类上已经写了所以咱俩不用再写了public RString save(HttpServletRequest request,RequestBody Employee employee){//对新增的员工设置初始化密码123456,需要进行md5加密处理后续员工可以直接修改密码employee.setPassword(DigestUtils.md5DigestAsHex(123456.getBytes()));employee.setCreateTime(LocalDateTime.now());employee.setUpdateTime(LocalDateTime.now());//获得当前登录用户的idLong empId (Long) request.getSession().getAttribute(employee);employee.setCreateUser(empId); //创建人的id,就是当前用户的id在进行添加操作的idemployee.setUpdateUser(empId);//最后的更新人是谁//mybatis提供的新增方法employeeService.save(employee);return R.success(新增员工成功);}功能测试登陆之后点击添加然后确认然后去数据库看一下新增数据成功没新增成功那就表示代码可以执行 注意但是因为我们把username设置为唯一索引所以下次再新增用户的时候就会出现异常这个异常是MySQL数据库抛出来的
解决bug: 上面的程序不好不好在 我写一个save 可以这么保存但是未来越来越多的代码 都会面临这样的问题那就会写很多的try和catch。所以我现在利用全局异常捕获这个异常。 1.2.1 全局异常捕获
这个全局异常捕获写在common包下
package com.itheima.reggie.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;/*** 全局异常处理*/
ControllerAdvice(annotations {RestController.class, Controller.class}) //表示拦截哪些类型的controller注解
ResponseBody
Slf4j
public class GlobalExceptionHandler {/*** 处理SQLIntegrityConstraintViolationException异常的方法* return*/ExceptionHandler(SQLIntegrityConstraintViolationException.class)public RString exceptionHandle(SQLIntegrityConstraintViolationException exception){log.error(exception.getMessage()); //报错记得打日志if (exception.getMessage().contains(Duplicate entry)){//获取已经存在的用户名这里是从报错的异常信息中获取的String[] split exception.getMessage().split( );String msg split[2] 这个用户名已经存在;return R.error(msg);}return R.error(未知错误);}
}功能测试登陆后添加一个一个已经存在账号名看前端页面提示的是什么信息以及看后台是否输出了报错日志 根据产品原型明确业务需求重点分析数据的流转过程和数据格式通过debug断点调试跟踪程序执行过程 1.3 员工信息分页查询
需求分析系统中的员工比较多的时候如果在一个页面中全部展示出来会显得比较乱不便于查看所以一般都系统中都会以分页的方式来展示列表数据。 流程分析
在开发代码之前需要梳理一下整个程序的执行过程:
页面发送ajax请求,将分页查询参数(page、 pageSize、 name)提交到服务端服务端Controller接收页面提交的数据并调用Service查询数据Service调用Mapper操作数据库查询分页数据Controller将查询到的分页数据响应给页面页面接收到分页数据并通过ElementUl的Table组件展示到页面上 Java代码
//配置mybatis-plus的分页插件
package com.itheima.reggie.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*** 配置mybatis-plus提供的分页插件拦截器*/
Configuration
public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mybatisPlusInterceptor;}
}/*** 员工信息分页* param page 当前页数* param pageSize 当前页最多存放数据条数,就是这一页查几条数据* param name 根据name查询员工的信息* return*/GetMapping(/page)public RPage page(int page,int pageSize,String name){//这里之所以是返回page对象(mybatis-plus的page对象)是因为前端需要这些分页的数据(比如当前页总页数)//在编写前先测试一下前端传过来的分页数据有没有被我们接受到//log.info(page {},pageSize {},name {} ,page,pageSize,name);//构造分页构造器 就是page对象Page pageInfo new Page(page,pageSize);//构造条件构造器 就是动态的封装前端传过来的过滤条件 记得加泛型LambdaQueryWrapperEmployee queryWrapper new LambdaQueryWrapper();//根据条件查询 注意这里的条件是不为空queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);//添加一个排序条件queryWrapper.orderByDesc(Employee::getUpdateTime);//执行查询 这里不用封装了mybatis-plus帮我们做好了employeeService.page(pageInfo,queryWrapper);return R.success(pageInfo);}功能测试 分页的三个时机 ①用户登录成功时分页查询一次 ②用户使用条件查询的时候分页一次 ③跳转页面的时候分页查询一次 1.4 启用/禁用员工账号
需求分析
在员工管理列表页面中可以对某个员工账号进行启用或者是禁用操作。账号禁用的员工不能登陆系统启用后的员工可以正常登陆
需要注意的是只有管理员(admin用户)才可以对其他普通用户进行启用操作禁用操作所以普通用户登录系统后启用禁用按钮不显示
并且如果某个员工账号的状态为正常则按钮显示为’‘禁用’如果员工账号状态为已禁用则按钮显示为“启用”。
普通员工登录系统后启用禁用按钮不显示
代码开发 注意:这里修改状态码要反着来因为正常的用户你只能把它设置为禁用已经禁用的账号你只能把它设置为正常
流程分析
在开发代码之前需要梳理一下整个程序的执行过程:
页面发送ajax请求将参数(id、status)提交到服务端服务端Controller接收页面提交的数据并调用Service更新数据Service调 用Mapper操作数据库 注意启用禁用的员工账号本质上就是一个更新操作也就是对status状态字段进行修改操作
在controller中创建update方法此方法是一个通用的修改员工信息的方法,因为status也是employee中的一个属性而已这里使用了动态SQL的功能根据具体的数据修改对应的字段信息 /*** 根据id修改员工信息* param employee* return*/PutMappingpublic RString 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后我们会发现后台的SQL语句使用的id和数据库中的id是不一样的
原因是mybatis-plus对id使用了雪花算法所以存入数据库中的id是19为长度但是前端的js只能保证数据的前16位的数据的精度对我们id后面三位数据进行了四舍五入所以就出现了精度丢失就会出现前度传过来的id和数据里面的id不匹配就没办法正确的修改到我们想要的数据
当然另一种解决bug的方法是:关闭mybatis-plus的雪花算法来处理ID我们使用自增ID的策略来往数据库添加id就行 1.4.1 使用自定义消息转换器
代码bug修复
思路既然js对long型的数据会进行精度丢失那么我们就对数据进行转型我们可以在服务端Java端给页面响应json格式的数据时进行处理将long型的数据统一转换为string字符串
代码实现步骤
提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象到json数据的转换(资料中已经提供直接复制到项目中使用)在WebMvcConfig配置类中扩展Spring mvc的消息转换器在此消息转换器中使用提供的对象转换器进行Java对象到json数据的转换 步骤一自定义消息转换类
package com.itheima.reggie.common;import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;/*** 对象映射器:基于jackson将Java对象转为json或者将json转为Java对象* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]*/
public class JacksonObjectMapper extends ObjectMapper {public static final String DEFAULT_DATE_FORMAT yyyy-MM-dd;public static final String DEFAULT_DATE_TIME_FORMAT yyyy-MM-dd HH:mm:ss;public static final String DEFAULT_TIME_FORMAT HH:mm:ss;public JacksonObjectMapper() {super();//收到未知属性时不报异常this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);//反序列化时属性不存在的兼容处理this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);SimpleModule simpleModule new SimpleModule().addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))).addSerializer(BigInteger.class, ToStringSerializer.instance).addSerializer(Long.class, ToStringSerializer.instance).addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))).addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))).addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));//注册功能模块 例如可以添加自定义序列化器和反序列化器this.registerModule(simpleModule);}
}步骤二在前面的webMvcConfig 配置类中扩展spring mvc 的消息转换器在此消息转换器中使用spring提供的对象转换器进行Java对象到json数据的转换 /*** 扩展mvc框架的消息转换器* param converters*/Overrideprotected void extendMessageConverters(ListHttpMessageConverter? converters) {//log.info(扩展消息转换器...);//创建消息转换器对象MappingJackson2HttpMessageConverter messageConverter new MappingJackson2HttpMessageConverter();//设置对象转换器底层使用Jackson将Java对象转为jsonmessageConverter.setObjectMapper(new JacksonObjectMapper());//将上面的消息转换器对象追加到mvc框架的转换器集合中//转换器是有优先级顺序的这里我们把自己定义的消息转换器设置为第一优先级所以会优先使用我们的转换器来进行相关数据进行转换如果我们的转换器没有匹配到相应的数据来转换那么就会去寻找第二个优先级的转换器以此类推converters.add(0,messageConverter);}然后启动程序使用f12查看服务器响应到浏览器的用户id是不是变成了字符串和数据库中是否相对应
发现对应即消息转换器配置成功
然后再去测试 启用与禁用 员工账号这个功能发现操作更新成功并且数据库修改成功 1.5 编辑员工信息
需求分析
在员工管理列表页面点击编辑按钮跳转到编辑页面在编辑页面回显员工信息并进行修改最后点击保存按钮完成编辑操作 在开发代码之前需要梳理一下操作过程和对应的程序的执行流程:
点击编辑按钮时页面跳转到add.html, 并在url中携带参数[员Iid]在add.html页面获取url中的参数[员工id]发送ajax请求请求服务端同时提交员工id参数服务端接收请求,根据员工id查询员工信息将员工信息以json形式响应给页面页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显点击保存按钮,发送ajax请求将页面中的员工信息以json方式提交给服务端服务端接收员工信息并进行处理完成后给页面响应页面接收到服务端响应信息后进行相应处理
注意: add.html页 面为公共页面,新增员工和编辑员工都是在此页面操作
数据回显后端代码其实主要逻辑在前端。。。。。 /*** 根据前端传过来的员工id查询数据库进行数据会显给前端* param id* return*/GetMapping(/{id})public REmployee getById(PathVariable Long id){Employee employee employeeService.getById(id);if (employee ! null){return R.success(employee) ;}return R.error(没有查询到该员工信息);}修改回显数据后点击保存会发送一个update的请求给后端前面我们已经写了这个update的controller所以只需要在前端跳转发请求就行这样就实现了方法的复用减少了代码量
功能测试自己测试编辑看能不能数据回显可不可以修改成功修改后数据库的数据有没有跟着变化 2. 菜品分类管理
2.1 公共字段填充(这里有重点)
问题分析 把相关的注解加在需要mybatis-plus自动帮我们填充的字段上面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;然后设置一个处理类在此类中为公共字段赋值需要实现 接口
package com.itheima.reggie.common;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;import java.time.LocalDateTime;/*** author LJM* create 2022/4/16* 自定义元数据对象处理器*/
Slf4j
Component //注意:这个要记得交给spring容器管理不然这个功能就没发用。。。。
//那么怎么确定你要添加的功能是不是要交给容器管理呢就是你直接写了一个工具类或者是功能类需要对数据库的数据或者是数据库数据的结果产生影响的时候你明明写了这样一个类但是功能却没有生效那么这个时候就要首先考虑是不是容器没有托管这个类
public class MyMetaObjecthandler implements MetaObjectHandler {/*** 插入操作自动填充* param metaObject*/Overridepublic void insertFill(MetaObject metaObject) {metaObject.setValue(createTime, LocalDateTime.now());metaObject.setValue(updateTime,LocalDateTime.now());metaObject.setValue(createUser, new Long(1)); //这里的id是不能直接获取的所以这里先写死后面教你怎么动态获取员工idmetaObject.setValue(updateUser,new Long(1));}/*** 更新操作自动填充* param metaObject*/Overridepublic void updateFill(MetaObject metaObject) {metaObject.setValue(updateTime,LocalDateTime.now());metaObject.setValue(updateUser,new Long(1));}
}功能完善 然后为了动态的获取员工的id,这里我们使用了threadLocal这个局部变量来获取和存储员工id;
创建一个工具类来设置和获取threadLocal中的员工id, 注意要先把数据设置进threadLocal中才能获取到
package com.itheima.reggie.common;/*** author LJM* create 2022/4/16* 基于ThreadLocal封装工具类用户保存和获取当前登录用户id*/
public class BaseContext {//用来存储用户idprivate static ThreadLocalLong threadLocal new ThreadLocal();/*** 设置值* param id*/public static void setCurrentId(Long id){threadLocal.set(id);}/*** 获取值* return*/public static Long getCurrentId(){return threadLocal.get();}
}在前面我们写的LongCheckFilter这个过滤器中把这个地方的代码加上添加和保存id的代码
//4、判断登录状态如果已登录则直接放行if(request.getSession().getAttribute(employee) ! null){//log.info(用户已登录用户id为{},request.getSession().getAttribute(employee));//把用户id存储到本地的threadLocalLong emId (Long) request.getSession().getAttribute(employee);BaseContext.setCurrentId(emId);filterChain.doFilter(request,response);return;}把处理器中的静态id改为动态获取
metaObject.setValue(createUser, BaseContext.getCurrentId());
metaObject.setValue(updateUser,BaseContext.getCurrentId());这里的ID之所以全为1是因为操作添加员工这个功能的管理员为admin它的id就是1
2.2 新增分类
需求分析 数据模型
从资料去复制实体Category类到entity包
数据库中的表结构 创建mapper:
package com.itheima.reggie.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Category;
import org.apache.ibatis.annotations.Mapper;Mapper
public interface CategoryMapper extends BaseMapperCategory {
}创建service
package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Category;/*** author LJM* create 2022/4/16*/
public interface CategoryService extends IServiceCategory {
}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;/*** author LJM* create 2022/4/16*/
Service
public class CategoryServiceImpl extends ServiceImplCategoryMapper, Category implements CategoryService {}编写controller 我们发现新增菜品分类的请求地址是:http://localhost:8080/category
提交的数据格式为
{name: 湘菜, type: 1, sort: 1}/*** 新增套餐分类* param category* return*/PostMappingpublic RString save(RequestBody Category category){log.info({category} ,category);categoryService.save(category);return R.success(新增分类成功);}功能测试登录后点击添加新增菜品分类看是否成功数据库的数据是否变化
2.3 菜品类的分页 代码开发 /*** 分页查询* param page* param pageSize* return*/GetMapping(/page)public RPage page(int page,int pageSize){//创建一个分页构造器PageCategory categoryPage new Page(page,pageSize);//创建一个条件构造器 用来排序用的 注意这个条件构造器一定要使用泛型否则使用条件查询这个方法的时候会报错LambdaQueryWrapperCategory queryWrapper new LambdaQueryWrapper();//添加排序条件 根据sort字段进行排序queryWrapper.orderByAsc(Category::getSort);categoryService.page(categoryPage,queryWrapper);return R.success(categoryPage);}功能测试 2.4 删除分类这里有注意点
需求分析 代码实现 注意这里的删除功能是不完整的因为可能需要删除的数据是与其他表关联的所以删除之前要先判断该条数据是否与其他表中的数据关联 /*** 根据id来删除分类的数据* param id* return*/DeleteMapping()public RString delete(RequestParam(ids) Long ids){ //注意这里前端传过来的数据是idscategoryService.removeById(ids);return R.success(分类信息删除成功);}功能完善 创建对应的mapper:
package com.itheima.reggie.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Dish;
import org.apache.ibatis.annotations.Mapper;/*** author LJM* create 2022/4/16*/
Mapper
public interface DishMapper extends BaseMapperDish {}package com.itheima.reggie.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.reggie.entity.Setmeal;
import org.apache.ibatis.annotations.Mapper;
/*** author LJM* create 2022/4/16*/
Mapper
public interface SetmealMapper extends BaseMapperSetmeal {
}创建service
package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Dish;/*** author LJM* create 2022/4/16*/
public interface DishService extends IServiceDish {
}package com.itheima.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.reggie.entity.Setmeal;public interface SetmealService extends IServiceSetmeal {
}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;/*** author LJM* create 2022/4/16*/
Service
Slf4j
public class DishServiceImpl extends ServiceImplDishMapper, Dish implements DishService {
}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;/*** author LJM* create 2022/4/16*/
Service
Slf4j
public class SetmealServiceImpl extends ServiceImplSetmealMapper, Setmeal implements SetmealService {
}添加自定义的service方法(就是我们需要的业务mybatis没有提供所以就需要自己另外在service创建新的方法并且在相关的业务中实现)
//在CategoryService中定义自己需要的方法直接写就行
void remove(Long id);在CategoryService实现类中重写该方法自定义异常类因为这里需要抛异常了package com.itheima.reggie.common;/*** 自定义业务异常类*/
public class CustomException extends RuntimeException {public CustomException(String message){super(message);}
}//然后在外面前面写的GlobalExceptionHandler全局异常捕获器中添加该异常这样就可以把相关的异常信息显示给前端操作的人员看见/*** 处理自定义的异常为了让前端展示我们的异常信息这里需要把异常进行全局捕获然后返回给前端* param exception* return*/ExceptionHandler(CustomException.class)public RString exceptionHandle(CustomException exception){log.error(exception.getMessage()); //报错记得打日志//这里拿到的message是业务类抛出的异常信息我们把它显示到前端return R.error(exception.getMessage());}/*** 根据id删除 分类删除之前需要进行判断是否有关联数据* param id*/Overridepublic void remove(Long id) {LambdaQueryWrapperDish dishLambdaQueryWrapper new LambdaQueryWrapper();//添加查询条件dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);//注意:这里使用count方法的时候一定要传入条件查询的对象否则计数会出现问题计算出来的是全部的数据的条数int count dishService.count(dishLambdaQueryWrapper);//查询当前分类是否关联了菜品如果已经管理直接抛出一个业务异常if (count 0){//已经关联了菜品抛出一个业务异常throw new CustomException(当前分类项关联了菜品,不能删除);}//查询当前分类是否关联了套餐如果已经管理直接抛出一个业务异常LambdaQueryWrapperSetmeal setmealLambdaQueryWrapper new LambdaQueryWrapper();setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);//注意:这里使用count方法的时候一定要传入条件查询的对象否则计数会出现问题计算出来的是全部的数据的条数int setmealCount setmealService.count(setmealLambdaQueryWrapper);if (setmealCount 0){//已经关联了套餐抛出一个业务异常throw new CustomException(当前分类项关联了套餐,不能删除);}//正常删除super.removeById(id);}然后在controller调用刚刚实现的方法就行把之前的remove方法给删除就行重新调用我们自己实现的方法 /*** 根据id来删除分类的数据* param id* return*/DeleteMappingpublic RString delete(RequestParam(ids) Long id){ //注意这里前端传过来的数据是idscategoryService.remove(id);return R.success(分类信息删除成功);}测试自己添加测试数据测试就行记得一定要测试一下删除有相关联的数据看会不会删除和在前端提示异常信息
2.5 修改分类 这里的编辑的数据回显前端已经帮我们做好了所以我们就不需要去数据库查询了这样可以减少对数据库的操作 /*** 根据id修改分类* param category* return*/PutMappingpublic RString update(RequestBody Category category){categoryService.updateById(category);return R.success(修改分类信息成功);}记得在对应的实体类加上公共字段的值设置前面我们配置了这个所以这里只需要加注解就行 //创建时间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;