Spring Boot & MyBatis

环境整合配置

创建项目

创建一个普通的 maven 项目

项目文件结构

添加核心依赖

  • spring-boot-starter-parent 放在 parent 标签下
  • spring-boot-starter-web
  • mybatis-spring-boot-starter
  • pagehelper-spring-boot-starter
  • mysql-connector-java
  • spring-boot-starter-jdbc
  • commons-lang3 工具包
  • spring-boot-starter-validation
  • lombok 开发依赖
pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath />
</parent>

<groupId>org.example</groupId>
<artifactId>shsxt-spring-boot-mybatis-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>shsxt-spring-boot-mybatis-quickstart</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<finalName>spring-boot-mybatis-quickstart</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.6</version>
</plugin>
</plugins>

</build>
</project>

配置插件

pom.xml
1
2
3
4
5
6
7
8
9
10
<build>
<finalName>spring-boot-mybatis-quickstart</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.6</version>
</plugin>
</plugins>
</build>

application.yml 整合配置

application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 项目配置
server:
# 设置项目启动端口号
port: 8080
# 设置项目路径
servlet:
context-path: /admin

# 数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&&useSSL=false&serverTimezone=GMT%2B8
username: root
password: root

# mybatis 配置
mybatis:
# 配置映射文件路径
mapper-locations: classpath:/mappers/*.xml
# 设置别名的包路径
type-aliases-package: org.exmaple.po
configuration:
# 下划线转驼峰
map-underscore-to-camel-case: true


# 分页设置
pagehelper:
helper-dialect: mysql

# 设置显示dao执行的sql语句
logging:
level:
org:
example:
dao: debug

使用c3p0数据源

增加依赖

pom.xml
1
2
3
4
5
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>

配置application.yml

注意数据源的配置和jdbc的不同,这里写什么取决于type对应的类的属性,c3p0的com.mchange.v2.c3p0.ComboPooledDataSource类的属性是什么,这里就要写什么。

application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 项目配置
server:
# 设置项目启动端口号
port: 8080
# 设置项目路径
servlet:
context-path: /admin

# 数据源配置
spring:
datasource:
type: com.mchange.v2.c3p0.ComboPooledDataSource
driverClass: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&&useSSL=false&serverTimezone=GMT%2B8
user: root
password: root
maxPoolSize: 200
minPoolSize: 10
initialPoolSize: 10
acquireIncrement: 5
idleConnectionTestPeriod: 28000
maxIdleTime: 28000

# mybatis 配置
mybatis:
# 配置映射文件路径
mapper-locations: classpath:/mappers/*.xml
# 设置别名的包路径
type-aliases-package: org.exmaple.po
configuration:
# 下划线转驼峰
map-underscore-to-camel-case: true


# 分页设置
pagehelper:
helper-dialect: mysql

# 设置显示dao执行的sql语句
logging:
level:
org:
example:
dao: debug

创建数据源配置类

一定要配置这个类,否则连不上数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.example.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class DataSourceConfiguration extends WebMvcConfigurerAdapter {
@Bean(name = "dataSource")
@Qualifier(value = "dataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
DataSource dataSource= DataSourceBuilder.create()
.type(com.mchange.v2.c3p0.ComboPooledDataSource.class).build();
return dataSource;
}
}

代码实现

C3p0 数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.example.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class DataSourceConfiguration extends WebMvcConfigurerAdapter {
@Bean(name = "dataSource")
@Qualifier(value = "dataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
DataSource dataSource= DataSourceBuilder.create()
.type(com.mchange.v2.c3p0.ComboPooledDataSource.class).build();
return dataSource;
}
}

断言工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.example.util;

import org.example.exceptions.ParamsException;

/**
* 断言工具类
*/
public class AssertUtil {
/**
* 判断是否为true
* 如果为true,则抛出异常
* @param flag
* @param msg
*/
public static void isTrue(boolean flag, String msg){
if(flag){
throw new ParamsException(msg);
}
}
}

自定义参数异常类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package org.example.exceptions;

/**
* 参数异常类
*/
public class ParamsException extends RuntimeException{
private Integer code = 300;
private String msg = "参数异常";

public ParamsException() {
super("参数异常");
}

public ParamsException(String msg) {
super(msg);
this.msg = msg;
}

public ParamsException(Integer code) {
super("参数异常");
this.code = code;
}

public ParamsException(Integer code, String msg) {
super("参数异常");
this.code = code;
this.msg = msg;
}

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;
}
}

分页查询条件类

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.example.query;

import lombok.Data;

/**
* 用户查询条件
*/
@Data
public class UserQuery {
private Integer pageNum = 1; // 当前页
private Integer pageSize = 10; // 每页数量
private String userName; // 用户名
}

全局异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package org.example;

import org.example.exceptions.ParamsException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

/**
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalExceptionHandlerResolver {

/**
* 全局异常
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Map<String, Object> exceptionHandler (Exception ex){
Map<String, Object> map = new HashMap<>();
// 默认异常代码和消息
map.put("code",500);
map.put("msg","系统异常");

return map;
}

/**
* 特定异常 参数异常
* @param ex
* @return
*/
@ExceptionHandler(value = ParamsException.class)
@ResponseBody
public Map<String, Object> paramsExceptionHandler (ParamsException ex){
Map<String, Object> map = new HashMap<>();
// 默认异常代码和消息
map.put("code",ex.getCode());
map.put("msg",ex.getMsg());

return map;
}

/**
* 特定异常 参数校验
* @param ex
* @return
*/
@ExceptionHandler(value = BindException.class)
@ResponseBody
public Map<String, Object> paramsExceptionHandler (BindException ex){
Map<String, Object> map = new HashMap<>();
// 默认异常代码和消息
map.put("code",500);
map.put("msg",ex.getBindingResult().getFieldError().getDefaultMessage());

return map;
}
}

定义JavaBean对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.example.po;


import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotBlank;

/**
* 用户
*/
@Data
public class User {
private Integer userId;

@NotBlank(message = "用户名不能为空")
private String userName;

@NotBlank(message = "密码不能为空")
@Length(min =6, max = 10, message="密码长度至少6位但不超过10位")
private String userPwd;
}

Dao层接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package org.example.dao;

import org.example.po.User;
import org.example.query.UserQuery;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
* 用户dao接口
*/
public interface UserMapper {

/**
* 通过用户名获取用户信息
* @param userName 用户名
* @return 用户信息
*/
User findUserByName(String userName);

/**
* 通过用户Id获取用户信息
* @param userId 用户Id
* @return
*/
User findUserById(Integer userId);

/**
* 查询用户列表
* @param userQuery
* @return
*/
List<User> findUserByParam(UserQuery userQuery);

/**
* 添加用户
* @param user
* @return 受影响的行数
*/
int addUser(User user);

/**
* 修改用户
* @param user
* @return 受影响的行数
*/
int updateUser(User user);

/**
* 删除用户
* @param userId 用户Id
* @return 受影响的行数
*/
int deleteUserById(Integer userId);
}

SQL映射文件定义

UserMapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?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="org.example.dao.UserMapper">
<!-- 通过用户名获取用户信息 -->
<select id="findUserByName" parameterType="string" resultType="org.example.po.User">
select * from tb_user where user_name=#{userName}
</select>

<!-- 通过用户Id获取用户信息 -->
<select id="findUserById" parameterType="int" resultType="org.example.po.User">
select * from tb_user where user_id=#{userId}
</select>

<select id="findUserByParam" parameterType="org.example.query.UserQuery" resultType="org.example.po.User">
select *
from tb_user
<where>
<if test ="null != userName and '' != userName">
and user_name like concat('%',#{userName}, '%')
</if>
</where>
</select>

<!-- 添加用户 -->
<insert id="addUser">
insert into tb_user(user_name, user_pwd)
values (#{userName}, #{userPwd})
</insert>

<!-- 修改用户 -->
<update id="updateUser">
update tb_user set user_name = #{userName}, user_pwd=#{userPwd}
where user_id=#{userId}
</update>

<delete id="deleteUserById">
delete from tb_user where user_id = #{userId}
</delete>
</mapper>

Service业务逻辑层代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package org.example.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.example.dao.UserMapper;
import org.example.po.User;
import org.example.query.UserQuery;
import org.example.util.AssertUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
* 用户业务类
*/
@Service
public class UserService {
@Resource
private UserMapper userMapper;

/**
* 通过用户名获取用户信息
* @param userName 用户名
* @return 用户信息
*/
public User findUserByName(String userName){

return this.userMapper.findUserByName(userName);
}

/**
* 通过用户Id获取用户信息
* @param userId 用户Id
* @return
*/
public User findUserById(Integer userId){
return this.userMapper.findUserById(userId);
}

/**
* 分页查询用户列表
* @param userQuery
* @return
*/
public PageInfo<User> findUserByParam(UserQuery userQuery){
// 开启分页
PageHelper.startPage(userQuery.getPageNum(), userQuery.getPageSize());
// 查询数据
List<User> userList = this.userMapper.findUserByParam(userQuery);
// 分页查询
return new PageInfo<User>(userList);
}

/**
* 添加用户
* @param user
* @return
*/
public void addUser(User user) {
// 参数校验
AssertUtil.isTrue(user == null, "用户信息不能为空");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()),"用户姓名不能为空");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"密码不能为空");
AssertUtil.isTrue(this.findUserByName(user.getUserName()) != null, "用户名不能重复");

// 执行添加操作
AssertUtil.isTrue(this.userMapper.addUser(user) < 1, "添加用户失败");
}

/**
* 修改用户
* @param user
*/
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user){
// 参数校验
AssertUtil.isTrue(user == null, "用户信息不能为空");
AssertUtil.isTrue(user.getUserId() == null,"用户数据异常");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()),"用户姓名不能为空");
AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"密码不能为空");
User checkUser = this.userMapper.findUserByName(user.getUserName());
AssertUtil.isTrue(checkUser != null && !checkUser.getUserId().equals(user.getUserId()), "用户名已存在");


// 执行添加操作
AssertUtil.isTrue(this.userMapper.updateUser(user) < 1, "修改用户失败");
}

/**
* 删除用户
* @param userId 用户Id
*/
public void deleteUserById(Integer userId){
AssertUtil.isTrue( userId == null ||this.userMapper.findUserById(userId) == null, "用户不存在");
AssertUtil.isTrue(this.userMapper.deleteUserById(userId) < 1, "删除用户失败");
}
}

Controller控制器定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package org.example.controller;

import com.github.pagehelper.PageInfo;
import org.example.exceptions.ParamsException;
import org.example.po.User;
import org.example.query.UserQuery;
import org.example.service.UserService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

/**
* 用户控制器
*/
@RestController
@RequestMapping("/users")
public class UserController {
@Resource
private UserService userService;

/**
* 通过用户名获取用户信息
* @param userName 用户名
* @return 用户信息
*/
@GetMapping("{userName}/username")
public User findUserByName(@PathVariable String userName){

return this.userService.findUserByName(userName);
}

/**
* 通过用户Id获取用户信息
* @param userId 用户Id
* @return
*/
@GetMapping("{userId}/id")
public User findUserById(@PathVariable Integer userId){
return this.userService.findUserById(userId);
}

/**
* 分页条件查询
* @param userQuery
* @return
*/
@GetMapping("list")
public PageInfo<User> findUserByPage(UserQuery userQuery){
return this.userService.findUserByParam(userQuery);
}

/**
* 添加用户
* @param user
* @return
*/
@PostMapping
public Map<String, Object> addUser(@RequestBody @Valid User user){
Map<String, Object> map = new HashMap<>();
// 调用service层的方法
this.userService.addUser(user);
map.put("code", 200);
map.put("msg", "添加用户成功");

return map;
}

/**
* 修改用户
* @param user
* @return
*/
@PutMapping
public Map<String, Object> updateUser(@RequestBody @Valid User user){
Map<String, Object> map = new HashMap<>();
// 调用service层的方法
this.userService.updateUser(user);
map.put("code", 200);
map.put("msg", "修改用户成功");

return map;
}

/**
* 删除用户用户
* @param userId 用户Id
* @return
*/
@DeleteMapping("{userId}")
public Map<String, Object> deleteUser(@PathVariable Integer userId){
Map<String, Object> map = new HashMap<>();
// 调用service层的方法
this.userService.deleteUserById(userId);
map.put("code", 200);
map.put("msg", "删除用户成功");

return map;
}
}

添加应用启动入口

启动入口写在 controller 层测父包根目录下

Starter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.example;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* 应用启动入口,写在 controller 层测父包根目录下
*/
@SpringBootApplication
@MapperScan("org.example.dao") // 扫描指定包下的接口类
public class Starter {
public static void main(String[] args) {
SpringApplication.run(Starter.class);
}
}

全局异常与事务控制

事务控制

在使用Jdbc作为数据库访问技术时,SpringBoot框架定义了基于 jdbc 的 PlatformTransactionManager 接口的实现 DataSourceTransactionManager, 并在 Spring Boot 应用启动时自动进行配置。如果使用 jpa ,Spring Boot 同样提供了对应的实现。

数据访问技术实现
JDBCDataSourceTransactionManager
JPAJpaTransactionManager
hibernateHibernateTransactionManager
JDOJdoTransactionManager
分布式事务JtaTransactionManager

MyBatis 底层数据访问是基于 jdbc 实现的,所以在 Spring Boot 环境下对事务进行控制,事务实现由 Spring Boot 实现并自动配置,在使用时通过注解方式(@Transactional)标注相关方法加入事务控制即可。

声明式事务配置
1
2
3
4
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user){
// ...
}

全局异常处理

SpringMvc 中对于异常统一处理提供了响应的处理方式,推荐使用的是实现接口 HandlerExceptionResolver 的方式,对代码侵入性较小。Spring Boot 同样提供了对异常的全局性处理,相关注解如下:

  • @ControllerAdvice

该注解组合了 @Component 注解功能,最常用的就是作为全局异常处理的切面类并交给IOC容器维护,同时通过该注解可以指定包扫描的范围。@ControllerAdvice 约定了几种可行的返回值,如果是直接返回 model 类的话,需要使用 @ResponseBody 进行 json 转换。如果返回一个视图,则直接返回一个字符串,这个字符串就是视图的名称。

  • @ExceptionHandler

该注解在 Spring3.* 版本引入,在处理异常时标注在方法级别,代表当前方法处理的异常类型有哪些。

全局异常处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 全局异常处理 返回 json
* value = Exception.class 表示这个全局异常类需要捕获 Exception 异常
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultInfo exceptionHandler(Exception e){
ResultInfo resultInfo = nwe ResultInfo(); // 自定义的异常消息类
resultInfo.setCode(300);
resultInfo.setMsg("操作失败");

// ParamsException 是一个自定义异常
if(e instanceof ParamsException){
ParamsException ex = (ParamsException) e;
resultInfo.setMsg(ex.getMsg());
resultInfo.setCode(ex.getCode());
}

// 返回一个对象,@ResponseBody注解会自动将这个对象转换为json
return resultInfo;
}
}

如果想捕捉特定的异常,只需要将 value 的值指定为你想捕捉的异常类就行,如下面代码只捕捉未登录异常(自定义异常)

特定异常处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ControllerAdvice
public class NoLoginExceptionHandler {
// 捕捉特定异常
@ExceptionHandler(value = NoLoginException.class)
@ResponseBody
public ResultInfo exceptionHandler(NoLoginException e){
ResultInfo resultInfo = nwe ResultInfo(); // 自定义的异常消息类
resultInfo.setCode(401);
resultInfo.setMsg("您还没有登录");

// 返回一个对象,@ResponseBody注解会自动将这个对象转换为json
return resultInfo;
}
}

数据校验 Validation

日常开发中,对于前端提交的表单,后台接口接收数据后,为了程序的严谨性,通常后端会加入业务参数的合法性校验操作来避免程序的非技术性bug。对于客户端提交的数据验证,SpringBoot通过 spring-boot-starter-validation 模块包含了数据校验的工作。

相关概念

  • JSR303: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如 @Null、@NotNull、@Pattern,位于 javax.validation.constraints 包下。JSR-349 是其升级版本,添加了一些新特性。
  • Hibernate Validation:Hibernate Validation 是对这个规范的实现,并增加了一些其它校验注解,如 @EMail @Length @Range 等。
  • Spring Validation:Spring Validation 对 Hibernate Validation 进行了二次封装,在 Spring MVC 模块中添加了自动校验,并将校验信息封装进了特定的类中。

环境配置

实现参数校验,程序必须引入 spring-boot-starter-validation 依赖,在引入 spring-boot-starter-web 依赖时,该模块会自动依赖 spring-boot-starter-validation ,所以程序中 spring-boot-starter-web 会一并依赖 spring-boot-starter-validation 到项目中(从SpringBoot2.3开始需要单独引入)。

校验相关注解

注解 功能
@AssertFalse 可以为null,如果不为null的话必须为 false
@AssertTrue 可以为null,如果不为null的话必须为 true
@DecimalMax 设置不能超过最大值
@DecimalMin 设置不能超过最小值
@Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future 日前必须在当前日前的未来
@Past 日前必须在当前日前的过去
@Max 最大不能超过此最大值
@Min 最大不能小于此最小值
@NotNull 不能为null,但可以为空
@Pattern 必须满足指定的正则表达式
@Size 集合、数组、map等的Size()值必须在指定范围内
@Email 必须是email格式
@Length 长度必须在指定范围内
@NotBlank 字符串不能为null,且trim()后也不能等于空
@NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于空
@Length 长度必须在指定范围内
@Range 值必须在指定范围内
@URL 必须是一个URL

校验注解使用

  1. 实体类参数校验
1
2
3
4
5
6
7
8
9
10
11
12
13
public class User{
private Integer id;

@NotBlank(message = "用户名不能为空")
private String userName;

@NotBlank(message = "密码不能为空")
@Length(min =6, max = 10, message="密码长度至少6位但不超过10位")
private String userPwd;

@Email
private String email;
}
  1. 接口方法形参 使用 @Valid 注解表示该参数启用参数校验。
1
2
3
public void saveUser(@Valid User user){
// ...
}
  1. 异常捕捉

参数校验抛出的是 BindException 异常,可以在全局异常捕获中获取其信息,如下面的代码

1
2
3
4
if(e instanceof BindException){
BindException be = (BindExcepiton) e;
resultInfo.setMsg(be.getBindingResult().getFieldError().getDefaultMessage());
}