前言
上篇我们学习用service调用mapper来操作数据库,本篇主要优化文章的新增,已经返回统一的结果包装、异常处理
优化新增文章
修改mapper 添加方法供ArticleService调用
@Mapper
@Repository
public interface ArticleMapper {
List<Article> getArticles();
+++
Boolean addArticle(Article article);
+++
}
同样的,我们也修改ArticleService/Impl
// ArticleService
public interface ArticleService {
List<Article> getArticles();
+++
Boolean addArticle(Article article);
+++
}
// ArticleServiceImpl
@Service
public class ArticleServiceImpl implements ArticleService {
@Resource
private ArticleMapper articleMapper;
@Override
public List<Article> getArticles() {
return articleMapper.getArticles();
}
+++
@Override
public Boolean addArticle(Article article) {
return articleMapper.addArticle(article);
}
+++
}
我们新建一个vo类,用来存储返回结果和请求body字段,我们需要前端请求的数据做一下校验,安装validation
// 修改根目录pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.5.3</version>
</dependency>
创建一个类用来接新增文章的字段,并在其中做校验
// src/main/java/com/semyin/blog/vo/request/AddArticle.java
package com.semyin.blog.vo.request;
import javax.validation.constraints.NotBlank;
public class AddArticle {
@NotBlank(message = "标题不能为空")
private String title;
@NotBlank(message = "内容不能为空")
private String detail;
private Integer status;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
然后修改,ArticleController中的方法addArticle
,我们先来看有几个字段,id,title,detail,status
id ,一般默认后端生成,或者数据库自增,我们在创建数据库的时候并没有设置自增,这里我们手动设置一下,让它自增,
alter table article change id id int AUTO_INCREMENT
title,前端传入
detail,前端传入
status,后端默认状态为1,这个我们后期再讲
@PostMapping("/articles")
public Boolean addArticle(
@RequestBody @Valid AddArticle addArticle
) {
Article article = new Article();
article.setTitle(addArticle.getTitle());
article.setDetail(addArticle.getDetail());
article.setStatus(1);
Date now = new Date();
article.setCreateTime(now);
article.setUpdateTime(now);
return articleService.addArticle(article);
}
现在,我们修改src/main/resources/mapper/ArticleMapper.xml
文件,添加新增文章的sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.semyin.blog.mapper.ArticleMapper">
+++
// resultMap 用户映射我们代码里的字段和数据库里的字段
id 唯一名称,我们下面的sql语句中resultMap带上这个id,这样就做好了映射,一般用于驼峰
<resultMap id="BaseResultMap" type="com.semyin.blog.entity.Article" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="title" property="title" jdbcType="VARCHAR" />
<result column="detail" property="detail" jdbcType="VARCHAR" />
<result column="status" property="status" jdbcType="INTEGER" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
</resultMap>
+++
<select id="getArticles" resultMap="BaseResultMap" resultType="Article">
select * from article
</select>
<insert id="addArticle" parameterType="com.semyin.blog.entity.Article">
insert into article (title, detail, createTime, updateTime, status)
values ( #{title,jdbcType=VARCHAR},
#{detail,jdbcType=VARCHAR},
#{createTime,jdbcType=TIMESTAMP},
#{updateTime,jdbcType=TIMESTAMP},
#{status,jdbcType=INTEGER})
</insert>
</mapper>
看一下请求结果
POST http://localhost:8020/articles
Content-Type: application/json
{
"title": "semyin",
"detail": "semyin desc"
}
POST http://localhost:8020/articles
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 29 Mar 2022 09:33:10 GMT
Keep-Alive: timeout=60
Connection: keep-alive
true
Response code: 200; Time: 226ms; Content length: 4 bytes
可以看到返回了true,代表我们新增成功了,我们看一下数据库也会发现article表里也多了一条数据
### 优化一下返回结果
我们新增成功一条数据,返回前端一个true,这样不太友好,所以封装一个ResultVO很有必要
```java
// src/main/java/com/semyin/blog/vo/ResultVO.java
package com.semyin.blog.vo;
import java.io.Serializable;
public class ResultVO<T> implements Serializable {
public static final ResultVO<?> POST_OK = new ResultVO<>(200, "请求成功");
public static final ResultVO<?> POST_ERROR = new ResultVO<>(400, "失败");
/**
* 状态码
*/
private Integer code;
/**
* 提示信息
*/
private String msg;
/**
* 数据体
*/
private Object data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public T getCount() {
return count;
}
public void setCount(T count) {
this.count = count;
}
/**
* 数量,一般用户列表查询
*/
private T count;
public ResultVO() {
}
private ResultVO(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public ResultVO(Integer code, String msg, T data, T count) {
this.code = code;
this.msg = msg;
this.data = data;
this.count = count;
}
private ResultVO(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}
现在我们处理一下ArticleController.java中的addArticle
和getArticles
方法,对返回数据使用ResultVO包装给前端
@PostMapping("/articles")
public ResultVO<?> addArticle(
@RequestBody AddArticle addArticle
) {
Article article = new Article();
article.setTitle(addArticle.getTitle());
article.setDetail(addArticle.getDetail());
article.setStatus(1);
boolean isSuccess = articleService.addArticle(article);
+++
if (isSuccess) {
return ResultVO.POST_OK;
} else {
return ResultVO.POST_ERROR;
}
+++
}
@GetMapping("/articles")
public ResultVO<Integer> getArticles() {
List<Article> articleList = articleService.getArticles();
ResultVO<Integer> resultVO = new ResultVO<>();
resultVO.setCode(200);
resultVO.setCount(0); // count 我们后续会讲怎么处理
resultVO.setData(articleList);
resultVO.setMsg("ok");
return resultVO;
}
返回结果
// 新增
POST http://localhost:8020/articles
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 29 Mar 2022 09:49:38 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"code": 200,
"msg": "请求成功",
"data": null,
"count": null
}
Response code: 200; Time: 677ms; Content length: 50 bytes
// 列表
GET http://localhost:8020/articles
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 29 Mar 2022 10:00:58 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"code": 200,
"msg": "ok",
"data": [
{
"id": 1,
"title": "测试标题",
"detail": "我是测试内容",
"status": 1,
"createTime": "2022-03-24T08:53:56.000+00:00",
"updateTime": "2022-03-24T08:53:56.000+00:00"
},
{
"id": 2,
"title": "semyin",
"detail": "semyin desc",
"status": 1,
"createTime": "2022-03-29T10:00:35.000+00:00",
"updateTime": "2022-03-29T10:00:35.000+00:00"
}
],
"count": 0
}
Response code: 200; Time: 140ms; Content length: 335 bytes
这样我们查询和新增返回给前端的优化处理就已经做好了
校验参数
我们再请求中参数不对或者确实就会,校验不正确会抛出异常,我们在异常中处理这个
// pom.xml 安装lang3
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
// 新建 src/main/java/com/semyin/blog/exception/SystemExceptionHandler.java
package com.semyin.blog.exception;
import com.semyin.blog.vo.ResultVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.ArrayList;
import java.util.List;
@RestControllerAdvice
public class SystemExceptionHandler {
@ExceptionHandler(HttpMessageNotReadableException.class)
public Object handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
return ResultVO.MISSING_REQUEST_BODY;
}
/**
* 处理 json 请求体调用接口对象参数校验失败抛出的异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object jsonParamsException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
List<String> errMsgList = new ArrayList<>();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
String msg = fieldError.getField() + ":" + fieldError.getDefaultMessage();
errMsgList.add(msg);
}
String joinErrorString = StringUtils.join(errMsgList.toArray(), ",");
ResultVO<Integer> resultVO = new ResultVO<>();
resultVO.setMsg(joinErrorString);
resultVO.setCode(400);
return resultVO;
}
/**
* 缺失参数校验异常处理
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public Object handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
ResultVO<Integer> resultVO = new ResultVO<>();
resultVO.setMsg(e.getMessage());
resultVO.setCode(400);
return resultVO;
}
}
// 我们新增文章不传title试一下
{
"code": 400,
"msg": "title:标题不能为空",
"data": null,
"count": null
}
// 可以看到我们VO中AddArticle类里面的@NotBlank中的messgae被返回出来
结语
本篇主要学习如何使用VO来处理输入输出的数据
1、RequestVO、validation校验
2、异常处理
2、ResultVO封装
注解:@RequestBody,@Valid