1. Spring Validation(续)
默认情况下,Spring Validation框架会在检查所有的请求参数后再提示可能的失败,即“检查到某个错误时并不会直接中止,而是继续检查”,如果需要实现“快速失败”(即:检查到某个错误时直接视为失败,不会继续后续的检查),需要在配置类中使用@Bean
方法创建并配置Validator
对象!
则在项目的根包下创建config.ValidationConfiguration
类,在此配置类中创建并配置Validator
:
package cn.tedu.csmall.product.config;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.validation.Validation;
/**
* Validation的配置类
*
* @author java@tedu.cn
* @version 0.0.1
*/
@Slf4j
@Configuration
public class ValidationConfiguration {
public ValidationConfiguration() {
log.info("加载配置类:ValidationConfiguration");
}
@Bean
public javax.validation.Validator validator() {
return Validation
.byProvider(HibernateValidator.class)
.configure() // 开始配置Validator
.failFast(true) // 快速失败,即检查到错误就直接视为失败
.buildValidatorFactory()
.getValidator();
}
}
Spring Validation是通过不同的注解,实现不同的检查功能,常用的检查注解有:
@NotNull
:不允许为null
@NotEmpty
:不允许为空(长度为0的字符串)- 仅适用于字符串类型的请求参数
- 可以与
@NotNull
同时使用,且当通过@NotNull
后再执行检查
@NotBlank
:不允许为空白- 仅适用于字符串类型的请求参数
- 可以与
@NotNull
同时使用,且当通过@NotNull
后再执行检查
@Pattern
:使用正则表达式检查,需要通过此注解的regexp
属性来配置正则表达式- 仅适用于字符串类型的请求参数
- 可以与
@NotNull
同时使用,且当通过@NotNull
后再执行检查
@Min
:值不得小于多少- 仅适用于数值类型的请求参数
- 可以与
@NotNull
同时使用,且当通过@NotNull
后再执行检查
@Max
:值不得大于多少- 仅适用于数值类型的请求参数
- 可以与
@NotNull
同时使用,且当通过@NotNull
后再执行检查
@Range
:值必须在指定的区间范围内- 仅适用于数值类型的请求参数
- 可以与
@NotNull
同时使用,且当通过@NotNull
后再执行检查
如果处理请求的方法的参数不是封装的数据类型,需要进行检查时,需要先在当前类上添加@Validated
注解,然后,再在参数上添加相关的检查注解,例如:
@Slf4j
@RestController
@RequestMapping("/brands")
@Api(tags = "02. 品牌管理模块")
@Validated // 【新增】
public class BrandController {
@Autowired
private IBrandService brandService;
public BrandController() {
log.info("创建控制器:BrandController");
}
// http://localhost:9080/brands/test/delete
@Deprecated
@ApiOperation("测试:删除品牌")
@ApiOperationSupport(order = 901)
@ApiImplicitParam(name = "enable", dataType = "int", paramType = "query") // 【新增】
@GetMapping("/test/delete")
// 【新增】以下方法的参数上添加了检查注解
public String delete(@Range(max = 1) Integer enable) {
log.debug("接收到【删除品牌(测试)】的请求");
log.debug("enable = {}", enable);
throw new RuntimeException("此接口仅用于测试,并未实现任何功能!");
}
}
使用这种方式检查请求参数时,如果检查不通过,将抛出ConstraintViolationException
异常,所以,还需要在**全局异常处理器(GlobalExceptionHandler
)**中添加:
@ExceptionHandler
public JsonResult<Void> handleConstraintViolationException(ConstraintViolationException e) {
log.debug("处理ConstraintViolationException");
Integer serviceCode = ServiceCode.ERR_BAD_REQUEST.getValue();
StringBuilder messageBuilder = new StringBuilder();
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
messageBuilder.append(constraintViolation.getMessage());
}
String message = messageBuilder.toString();
return JsonResult.fail(serviceCode, message);
}
1. 创建Passport项目
此项目主要用于处理“管理员”相关的数据,将实现:添加管理员、管理员登录、显示管理员列表、删除管理员、启用或禁用管理员,并且,在执行相关操作前检查当前登录的账号是否具有相关权限。
项目参数:
- GITEE仓库地址:
https://gitee.com/chengheng2022/jsd2205-csmall-passport-teacher.git
- 项目名称:
jsd2205-csmall-passport-teacher
- Group:
cn.tedu
Artiface
:csmall-passport
Package
:cn.tedu.csmall.passport
- Java版本:
8
- 创建过程中勾选依赖项:无
当项目创建出来后,通过前序项目,调整新项目的pom.xml
,原则上,除了当前项目的信息以外,其它内容全部从前序项目中复制过来即可!
然后,创建出mall_ams
数据库,并配置IntelliJ IDEA窗口中右侧的Database面板,执行mall_ams.sql
中的代码,以创建出相关的数据表。
从前序项目中复制application.properties
的配置到新项目中,并且,需要修改:
- 服务端口,不得与其它服务冲突
- 连接数据库的URL中的数据库名称,需要改为
mall_ams
# 服务端口
server.port=9081
# 连接数据库的相关配置
spring.datasource.url=jdbc:mysql://localhost:3306/mall_ams?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
# 配置SQL的XML文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
# 开启Knife4j的增强模式
knife4j.enable=true
# 日志的显示级别
logging.level.cn.tedu.csmall=trace
然后,将前序项目的config
包复制到新项目中(包括包中的各配置类),需要调整:
Knife4jConfiguration
:- 修改
basePackage
属性的值为:"cn.tedu.csmall.passport.controller"
- 修改
MybatisConfiguration
:- 修改
@MapperScan
注解中的参数为:"cn.tedu.csmall.passport.mapper"
- 修改
2. 添加管理员–Mapper
关于Mapper层(数据访问层 / 持久层),应该实现:
-
插入管理员数据
-
insert into ams_admin (除了id和2个时间以外的字段列表) values (匹配的值列表)
-
-
根据用户名统计管理员账号的数量
-
保证用户名唯一
-
select count(*) from ams_admin where username=?
-
-
根据手机号码统计管理员账号的数量
-
保证手机号码唯一
-
select count(*) from ams_admin where phone=?
-
-
根据电子邮箱统计管理员账号的数量
-
保证电子邮箱唯一
-
select count(*) from ams_admin where email=?
-
在项目的根包下创建pojo.entity.Admin
实体类,并在类中声明与数据表保持一致的属性(各属性名需遵循驼峰命名法):
@Data
public class Admin implements Serializable {
}
在项目的根包下创建mapper.AdminMapper
接口:
public interface AdminMapper {
int insert(Admin admin);
int countByUsername(String username);
int countByPhone(String phone);
int countByEmail(String email);
}
在src/main/resources
下创建mapper
文件夹,并在此文件夹中粘贴得到AdminMapper.xml
文件:
<!-- 省略顶部固定代码 -->
<mapper namespace="cn.tedu.csmall.passport.mapper.AdminMapper">
<!-- int insert(Admin admin); -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
...
</insert>
<!-- int countByUsername(String username); -->
<select id="countByUsername" resultType="int">
SELECT count(*) FROM ams_admin WHERE username=#{username}
</select>
<!-- int countByPhone(String phone); -->
<select id="countByPhone" resultType="int">
SELECT count(*) FROM ams_admin WHERE phone=#{phone}
</select>
<!-- int countByEmail(String email); -->
<select id="countByEmail" resultType="int">
SELECT count(*) FROM ams_admin WHERE email=#{email}
</select>
</mapper>
在src/test/java
的根包下创建mapper.AdminMapperTests
测试类,编写并执行以上各方法的测试:
3. 添加管理员–Service
在项目的根包下创建pojo.dto.AdminAddNewDTO
类:
@Data
public class AdminAddNewDTO implements Serializable {
// 用户名,密码,昵称,头像,手机号码,电子邮箱,简介,是否启用
}
在项目的根包下创建service.IAdminService
接口,并在接口中添加抽象方法:
@Transactional
public interface IAdminService {
void addNew(AdminAddNewDTO adminAddNewDTO);
}
从前序项目中复制ServiceCode
、ServiceException
到此项目。
在项目的根包下创建service.impl.AdminServiceImpl
类,实现以上接口,添加@Service
注解:
@Service
public class AdminServiceImpl implements IAdminService {
// 自动装配AdminMapper
@Override
public void addNew(AdminAddNewDTO adminAddNewDTO) {
// 从参数中取出用户名
// 调用Mapper对象的countByUsername()根据用户名统计管理员账号的数量
// 判断统计结果是否大于0
// 是:抛出ServiceException(ERR_CONFLICT)
// 从参数中取出手机号码
// 调用Mapper对象的countByPhone()根据手机号码统计管理员账号的数量
// 判断统计结果是否大于0
// 是:抛出ServiceException(ERR_CONFLICT)
// 从参数中取出电子邮箱
// 调用Mapper对象的countByEmail()根据电子邮箱统计管理员账号的数量
// 判断统计结果是否大于0
// 是:抛出ServiceException(ERR_CONFLICT)
// 创建Admin对象,将作为插入时的参数
// 将参数的各属性复制到Admin对象中:BeanUtils.copyProperties()
// 补全Admin对象的属性:loginCount,值为0
// TODO 取出Admin对象中的密码,将其加密,再存入到Admin对象中
// 调用Mapper对象的insert()插入管理员数据,并获取返回值
// 判断返回值是否不等于1
// 是:抛出ServiceException(ERR_INSERT)
}
}
提示:在实现以上业务时,请在关键点输出日志!
在src/test/java
下的根包下创建service.AdminServiceTests
测试类,编写并执行测试:
4. 添加管理员–Controller
从前序项目中复制JsonResult
、GlobalExceptionHandler
类到当前项目。
在项目的根包下创建controller.AdminController
类,并添加注解:
-
@Slf4j
-
@RestController
-
@RequestMapping("/admins")
-
@Api(tags = "01. 管理员管理模块")
然后,在控制器类中添加处理请求的方法:
@Autowired
private IAdminService adminService;
// http://localhost:9081/admins/add-new
@ApiOperation("添加管理员")
@ApiOperationSupport(order = 100)
@PostMapping("/add-new")
public JsonResult<Void> addNew(AdminAddNewDTO adminAddNewDTO) {
log.debug("开始处理【添加管理员】的请求,参数:{}", adminAddNewDTO);
adminService.addNew(adminAddNewDTO);
return JsonResult.ok();
}
完成后,启动项目,通过 http://localhost:9081/doc.html 打开在线API文档,并通过此文档进行调试。
5. 添加管理员–页面
作业
【作业】保证项目完成以下功能,要求Mapper层、Service层、Controller层的代码开发,其中,Mapper层、Service层需要有对应的测试类,Controller层配置好在线API文档,关于Spring Validation的使用是可选的
- 添加品牌(addNew),业务规则:品牌名必须唯一
- 根据id删除品牌(delete),业务规则:数据必须存在,不存在关联的类别(见pms_brand_category表),不存在关联的SPU(见pms_spu表)
- 根据id启用品牌(setEnable),业务规则:数据必须存在,必须处于禁用状态
- 根据id禁用品牌(setDisable),业务规则:数据必须存在,必须处于启用状态
- 根据id修改品牌基本资料(update),基本资料对应的属性:自行设计,业务规则:数据必须存在
- 添加类别(addNew),业务规则:父级类别id不为0时必须保证父级类别存在,类别名必须唯一,如果添加的是子级类别,需保证父级类别的isParent为1
- 根据id删除类别(delete),业务规则:数据必须存在,不存在关联的品牌(见pms_brand_category表),不存在关联的属性模板(见pms_category_attribute_template表),不存在关联的SPU(见pms_spu表)
- 根据id启用类别(setEnable),业务规则:数据必须存在,必须处于禁用状态
- 根据id禁用类别(setDisable),业务规则:数据必须存在,必须处于启用状态
- 根据id显示类别(setDisplay),业务规则:数据必须存在,必须处于隐藏状态
- 根据id隐藏类别(setHidden),业务规则:数据必须存在,必须处于显示状态
- 根据id修改类别基本资料(update),基本资料对应的属性:自行设计,业务规则:数据必须存在
- 绑定(插入)品牌与类别的关联(bind)(见pms_brand_category表),业务规则:品牌必须存在,类别必须存在,绑定关联必须不存在
- 解绑(删除)品牌与类别的关联(unbind)(见pms_brand_category表),业务规则:数据必须存在
- 添加相册(addNew),业务规则:相册名必须唯一
- 根据id删除相册(delete),业务规则:数据必须存在,不存在关联的图片(见pms_picture表),不存在关联的SPU(见pms_spu表)
- 根据id修改相册基本资料(update),基本资料对应的属性:自行设计,业务规则:数据必须存在
- 添加图片(addNew)(见pms_picture表),业务规则:相册必须存在
- 根据id删除图片(delete)(见pms_picture表),业务规则:数据必须存在