当前位置: 首页 > news >正文

SpringBoot(RESTful,统一响应结构,输出日志,增删改查功能,分页功能,批量删除,常见bug)【详解】

目录

一、准备工作

1. 前后端分离开发流程

2. 开发规范

1.RESTful请求风格

2.统一响应结果

3.代码中输出日志

二、部门管理(增删改查功能)

1. 查询部门列表

2. 删除部门

3. 新增部门

4. 修改部门

三、员工管理(分页功能和批量删除)

1. PageHelper插件

2. 分页查询员工-带条件

3. 删除员工-批量删

四、常见bug

1. Invalid bound statement (not found)

2. Unable to find a @SpringBootConfiguration,you need to use @ContextConfiguration

3. Mapped Statements already contains value: com.itheima.mapper.DeptMapper.selectList

4. Bad Sql Grammer... You have a error in your sql Syntax

5. 其它错误


一、准备工作

1. 前后端分离开发流程

2. 开发规范

所有的项目规范,==并非==全世界统一的强制性规范。实际开发中,可能每个项目组都有自己的一套规范

1.RESTful请求风格

  • 接收请求路径里的变量参数:@PathVariable("占位符名称")

  • 接收不同请求方式的请求:

    • @GetMpping

    • @PostMapping

    • @PutMapping

    • @DeleteMapping

2.统一响应结果

我们这次案例练习里,所有Controller的方法返回值,统一都是Result类型

3.代码中输出日志

企业开发中,通常是要输出日志,为将来程序出bug之后,找线索做准备

传统的方式:在类里添加以下代码,然后才可以使用log.info(), log.warn(), log.error()等方法

private static final Logger log = LoggerFactory.getLogger(当前类名.class);

例如:

@RestController
public class DeptController {private static final Logger log = LoggerFactory.getLogger(DeptController.class);@GetMapping("xxx/{id}")public Result xxx(@PathVariable("id") String id){//打印日志:根据id查询部门,id=id值log.info("根据id查询部门,id={}", id);//完成功能代码……return null;}
}

可以使用Lombok简化一下:

  1. 在类上添加注解 @Slf4j

  2. 在方法里就可以直接使用log对象输出日志了:可以使用log.info(), log.warn(), log.error()等方法

@Slf4j
@RestController
public class DeptController {@GetMapping("xxx/{id}")public Result xxx(@PathVariable("id") String id){//打印日志:根据id查询部门,id=id值log.info("根据id查询部门,id={}", id);//完成功能代码……return null;}
}

在SpringBoot配置文件里加上:

#logging.level,包名=日志级别 表示指定包下的程序类,只输出指定级别以上的日志logging.level.com.itheima=error

二、部门管理(增删改查功能)

1. 查询部门列表

DeptController:

package com.itheima.controller;import com.itheima.pojo.Dept;
import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** 通常会在Controller类上加@RequestMapping("所有方法的路径前缀")* 然后每个方法上再添加@RequestMapping、@PostMapping、@PutMapping、@DeleteMapping,只需要加后边剩下的那一截路径即可* 比如:*      类上配置是@RequestMapping("/depts")*      方法上的配置是@GetMapping("/{id}")*      这时候,此方法的访问路径是:类的路径 + 方法的路径,即: /depts/{id}** 部门管理Controller*/
@Slf4j
@RestController
@RequestMapping("/depts")
public class DeptController {@Autowiredprivate DeptService deptService;@GetMappingpublic Result queryAllDeppts(){log.info("查询所有部门");List<Dept> empList = deptService.queryAllDepts();return Result.success(empList);}
}

DeptService:

package com.itheima.service;import com.itheima.pojo.Dept;
import com.itheima.pojo.Emp;import java.util.List;/*** 部门管理*/
public interface DeptService {List<Dept> queryAllDepts();
}

DeptServiceImpl:

package com.itheima.service.impl;import com.itheima.mapper.DeptMapper;
import com.itheima.pojo.Dept;
import com.itheima.pojo.Emp;
import com.itheima.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;
import java.util.List;@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptMapper deptMapper;@Overridepublic List<Dept> queryAllDepts() {return deptMapper.selectAll();}
}

DeptMapper:

package com.itheima.mapper;import com.itheima.pojo.Dept;
import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;/*** 部门管理*/
@Mapper
public interface DeptMapper {@Select("select * from dept")List<Dept> selectAll();
}

2. 删除部门

DeptController:

@DeleteMapping("/{deptId}")
public Result deleteDeptById(@PathVariable("deptId") Integer id){log.info("根据id删除部门,id={}", id);deptService.deleteDeptById(id);return Result.success();
}

DeptService:

void deleteDeptById(Integer id);

DeptServiceImpl:

@Override
public void deleteDeptById(Integer id) {deptMapper.deleteById(id);
}

DeptMapper:

@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);

3. 新增部门

DeptController:

@PostMapping
public Result addDept(@RequestBody Dept dept){log.info("新增部门:{}", dept);deptService.addDept(dept);return Result.success();
}

DeptService:

void addDept(Dept dept);

DeptServiceImpl:

@Override
public void addDept(Dept dept) {//补全数据LocalDateTime now = LocalDateTime.now();dept.setCreateTime(now);dept.setUpdateTime(now);deptMapper.insert(dept);
}

DeptMapper:

@Insert("INSERT INTO dept (name, create_time, update_time) " +"VALUES (#{name}, #{createTime}, #{updateTime})")
void insert(Dept dept);

4. 修改部门

DeptController:

@GetMapping("/depts/{id}")public Result findById(@PathVariable("id") Integer id) {Dept dept = deptService.findById(id);return Result.success(dept);}@PutMapping("/depts")public Result edit(@RequestBody Dept dept){boolean success = deptService.updateById(dept);if (success) {return Result.success();}else{return Result.error("修改部门失败");}}

DeptService:

Dept findById(Integer id);boolean updateById(Dept dept);

DeptServiceImpl:

@Overridepublic Dept findById(Integer id) {return deptMapper.findById(id);
}@Overridepublic boolean updateById(Dept dept) {dept.setUpdateTime(LocalDateTime.now());return deptMapper.updateById(dept);
}

DeptMapper:

@Select("select * from dept where id = #{id}")
Dept findById(Integer id);@Update("update dept set name=#{name}, update_time=#{updateTime} where id = #{id}")
boolean updateById(Dept dept);

三、员工管理(分页功能和批量删除)

1. PageHelper插件

分页功能介绍

分页查询功能,客户端通常需要我们提供两项数据:

  • 数据列表:用于展示给用户看的数据;用户每次点击某一页的按钮,就要去服务端加载这一页的数据列表

  • 总数量:客户端得到总数量后,可以根据每页几条计算分了多少页,从而显示页码按钮

原始分页功能的实现方式

服务端要准备以上两项数据,需要:

  • 执行SQL语句查询总数量:select count(*) from emp where gender = 1

  • 执行SQL语句查询数据列表:select * from emp where gender = 1 limit 0, 5

存在的问题:

  1. 有两条SQL语句,Mapper接口里要写两个方法;

  2. 两条SQL语句的where条件相同

PageHelper介绍

分页插件PageHelper官网:MyBatis 分页插件 PageHelper

PageHelper是一款开源的Mybatis插件,可以帮我们简化分页功能的实现

PageHelper的使用

1.添加依赖坐标

<!--PageHelper分页插件-->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.2</version>
</dependency>

2.使用方式:在调用Mapper时,按照如下步骤

//1. 开启分页
PageHelper.startPage(页码,每页几条);
//2. 查询列表:SQL语句里不要加limit,只要查询列表就行.
//   例如 select * from emp where...    不要再加limit
List<Emp> empList = empMapper.selectList();
//3. 获取分页结果
Page<Emp> page = (Page<Emp>)empList;
long total = page.getTotal(); //获取总数量

PageHelper底层原理:

2. 分页查询员工-带条件

根据页面提交的搜索条件,分页查询员工列表

EmpController:

package com.itheima.controller;import com.itheima.pojo.Emp;
import com.itheima.pojo.PageBean;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDate;/*** 员工管理Controller*/
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {@Autowiredprivate EmpService empService;@GetMappingpublic Result queryEmpsByPage(@RequestParam(value = "page", defaultValue = "1") Integer page,@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,String name, Integer gender,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){PageBean<Emp> pageBean = empService.queryEmpsByPage(name, gender, begin, end, page, pageSize);return Result.success(pageBean);}
}

EmpService:

public interface EmpService {PageBean<Emp> queryEmpsByPage(String name, Integer gender, LocalDate begin, LocalDate end, Integer page, Integer pageSize);
}

EmpServiceImpl:

package com.itheima.service.impl;import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import com.itheima.pojo.PageBean;
import com.itheima.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDate;
import java.util.List;@Service
public class EmpServiceImpl implements EmpService {@Autowiredprivate EmpMapper empMapper;@Overridepublic PageBean<Emp> queryEmpsByPage(String name, Integer gender, LocalDate begin, LocalDate end, Integer page, Integer pageSize) {//开启分页PageHelper.startPage(page, pageSize);//查询列表List<Emp> empList = empMapper.selectList(name, gender, begin, end);//得到分页结果Page<Emp> p = (Page<Emp>) empList;//封装PageBean对象PageBean<Emp> pageBean = new PageBean<>();pageBean.setTotal((int) p.getTotal());pageBean.setRows(empList);return pageBean;}
}

EmpMapper:

package com.itheima.mapper;import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;import java.time.LocalDate;
import java.util.List;/*** 员工管理*/
@Mapper
public interface EmpMapper {List<Emp> selectList(String name, Integer gender, LocalDate begin, LocalDate end);
}

EmpMapper.xml:

xml映射文件:要和Mapper接口同名同包

xml内容:

  • mapper标签的namespace必须是Mapper接口的全限定类名;

  • select标签的id必须是对应的方法名

  • select标签必须有resultType,是JavaBean的全限定类名,表示要把查询结果封装成什么类型的对象

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper"><select id="queryByPage" resultType="com.itheima.pojo.Emp">select * from emp<where><if test="name!=null and name.length()>0">and name like concat('%', #{name}, '%')</if><if test="gender!=null">and gender = #{gender}</if><if test="begin!=null">and entrydate >= #{begin}</if><if test="end!=null"><!-- XML的特殊字符:< 要写成 &lt;  -->and entrydate &lt;= #{end}</if></where></select><delete id="deleteByIds">delete from emp where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></delete>
</mapper>

3. 删除员工-批量删

EmpController:

@DeleteMapping("/{ids}")
public Result batchDeleteEmps(@PathVariable("ids") List<Integer> ids){empService.batchDeleteEmps(ids);return Result.success();
}

EmpService:

void batchDeleteEmps(List<Integer> ids);

EmpServiceImpl:

@Override
public void batchDeleteEmps(List<Integer> ids) {empMapper.batchDeleteByIds(ids);
}

EmpMapper:

void batchDeleteByIds(List<Integer> ids);

EmpMapper.xml

<delete id="batchDeleteByIds">delete from emp<where><if test="ids!=null and ids.size()>0"><foreach collection="ids" open="and id in(" item="id" separator="," close=")">#{id}</foreach></if></where>
</delete>

四、常见bug

1. Invalid bound statement (not found)

原因:Mapper接口里的定义了方法,但是找不到给它配置的SQL语句

解决:如果SQL语句要和方法关联起来,有以下要求,挨个检查确认一下:

  • XML映射文件,必须和Mapper接口同名同位置

  • XML里的mapper标签的namespace必须是Mapper接口的全限定类名

  • XML里配置SQL语句的标签(select, insert, update, delete)的id必须是方法名

2. Unable to find a @SpringBootConfiguration,you need to use @ContextConfiguration

在执行单元测试方法时,出现这个错误

原因:单元测试类不在 引导类所在的包 里边。SpringBoot项目的目录结构是有要求的

SpringBoot工程目录结构要求:

  • 引导类所在的包,必须是最大的包。其它所有类(包括单元测试类)必须全部在这个包下边

  • 比如,引导类在com.itheima包下,那么所有类都必须在这个包下

    • Controller类、Service类、Mapper类、其它类

    • 单元测试测试类等等

3. Mapped Statements already contains value: com.itheima.mapper.DeptMapper.selectList

出现的原因,是因为:这个方法有多个SQL语句配置,配置重复了。或者Mapper里的这个方法有重载(同名方法)

Mapper接口里的方法:所有方法不能同名

4. Bad Sql Grammer... You have a error in your sql Syntax

SQL语句有语法错误,检查SQL语句

5. 其它错误

  1. web工程:服务端必须启动成功后,然后Postman或者前端界面才可以访问成功

    如何启动:运行引导类(启动类),不要运行单元测试类

  2. 常见的HTTP错误码

    404:找不到资源。需要检查:你的请求路径,在Controller里有没有对应的方法存在

    500:服务器内部错误。需要检查:服务端控制台,查看错误信息,根据错误信息定位问题、解决问题

    405:Method Not Allowed,请求方式不允许。通常是 客户端发请求的方式,和Controller方法允许的请求方式不匹配

    • 比如:客户端发请求是 DELETE /depts/1;服务端只有一个方法路径是 @GetMapping("/depts/{id}")

相关文章:

  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • Elasticsearch:调整近似 kNN 搜索
  • 微信小程序调用百度智能云API(菜品识别)
  • 【计算机网络】集线器
  • 鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Search)
  • 前端开发者如何打造自己的生态以及ip
  • 哥斯拉流量webshell分析-->ASP/PHP
  • Docker 笔记(五)--链接
  • Qt 信号槽
  • 【学习学习】学习金字塔
  • IIS上部署.netcore WebApi项目及swagger
  • 行为型-观察者模式
  • php常用设计模式应用场景及示例
  • 超实用!免费软件站大盘点,总有一款适合你
  • 【兆易创新GD32H759I-EVAL开发板】 TLI(TFT LCD Interface)用法详细介绍
  • JS 中的深拷贝与浅拷贝
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • CODING 缺陷管理功能正式开始公测
  • Druid 在有赞的实践
  • PAT A1050
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • 利用DataURL技术在网页上显示图片
  • 前端面试题总结
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • C# - 为值类型重定义相等性
  • Mac 上flink的安装与启动
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 函数计算新功能-----支持C#函数
  • ​iOS安全加固方法及实现
  • ​第20课 在Android Native开发中加入新的C++类
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #include到底该写在哪
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • %@ page import=%的用法
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (zt)最盛行的警世狂言(爆笑)
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (附源码)计算机毕业设计高校学生选课系统
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (转)Oracle存储过程编写经验和优化措施
  • (转)Sublime Text3配置Lua运行环境
  • (转)树状数组
  • (转载)虚函数剖析
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .apk文件,IIS不支持下载解决
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .net用HTML开发怎么调试,如何使用ASP.NET MVC在调试中查看控制器生成的html?
  • @Responsebody与@RequestBody
  • @RestControllerAdvice异常统一处理类失效原因
  • [ IOS ] iOS-控制器View的创建和生命周期
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504