SpringBoot系列(四):SpringBoot整合Mybatis实现不一样的CRUD


作者: 修罗debug
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

摘要:本文我们将回归介绍、分享Spring Boot在企业级应用开发的过程中所体现出来的作用, 特别是在应用系统业务模块的开发过程中,它跟Mybatis/MybatisPlus(某种持久层框架)整合所体现出来的“双剑合璧”的巨大功效!在本篇文章中,我们将首先分享如何基于Spring Boot整合Mybatis实现基本的CRUD!

内容:作为一款用于与数据库打交道的持久层框架,Mybatis/MybatisPlus着实给我们的企业级应用系统的开发带来了不可磨灭的作用,其高度封装后的数据库操作接口,即DAO API,可以为企业级应用开发过程中的底层数据库操作带来极大的便捷性,省去了在纯生JDBC时代所需要编写的大量样板式的代码的麻烦!

可以毫不夸张地讲,大部分企业级应用的开发工作量,其核心业务逻辑就是在实现或者处理业务模块的CRUD上,下面我们就基于前文搭建的标准企业级Spring Boot项目为奠基,整合Mybatis,并实现最基本的CRUD。

在开始开发之前,我们需要在数据库创建一个数据库表artile,即文章表,其数据库建表语句DDL如下所示:

CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '文章标题',
`user_id` int(11) DEFAULT NULL COMMENT '作者',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文章';


完了之后,采用Mybatis逆向工程生成该数据库表对应的实体类Entity、Mapper操作接口、Mapper操作接口对应的动态Sql配置文件mapper.xml。其对应的代码,各位小伙伴可以自行check下来进行查看。

在此之前,需要新建一个ArticleController、IArticleService接口以及ArticleService实现类!

一、新增与修改

(1)对于新增跟修改而言,其实主要有三点需要注意,第一点是前端提交过来的请求参数的校验;第二点是查询是否有新增了相同字段信息的记录;最后一点则是直接执行“新增逻辑”。其Controller对应的代码如下所示:

@RestController
@RequestMapping("article")
public class ArticleController extends AbstractController{
@Autowired
public IArticleService articleService;
//新增
@RequestMapping(value = "save",method = RequestMethod.POST)
public BaseResponse save(@RequestBody @Validated Article article, BindingResult result){
String error=ValidatorUtil.checkResult(result);
if (StringUtils.isNotBlank(error)){
return new BaseResponse(StatusCode.Fail.getCode(),error);
}
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
Integer id=articleService.saveEntity(article);
response.setData(id);
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
//更新
@RequestMapping(value = "update",method = RequestMethod.POST)
public BaseResponse update(@RequestBody @Validated Article article, BindingResult result){
String error=ValidatorUtil.checkResult(result);
if (StringUtils.isNotBlank(error)){
return new BaseResponse(StatusCode.Fail.getCode(),error);
}
if (article.getId()==null || article.getId()<=0){
return new BaseResponse(StatusCode.InvalidParams);
}
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
articleService.updateEntity(article);
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
}


  (2)在这里,我们开发了一个统一的用于“校验前端请求参数”的校验工具类ValidatorUtil,该工具是跟Hibernate-Validator组件一起结合使用的,主要用于校验一些加了指定注解的参数,如,上面的Article类,其实是加了一些校验注解参数的,如下所示:  

import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class Article {
private Integer id;

@NotBlank(message = "文章标题不能为空")
private String title;
@NotNull
private Integer userId; }


其中的@NotBlank等注解即可以用于校验指定的请求参数,当前端提交过来的对应的请求参数的取值为NULL或者空字符串时,则会报相应的错误,这些错误信息会被BindingResult所捕获,而下面的ValidatorUtil即展示了其对于这些错误的统一处理方式:  

/**请求参数统一校验工具
* @Author:debug (SteadyJack)
* @Date: 2019/8/27 11:52
**/
public class ValidatorUtil {
//统一校验处理的结果
public static String checkResult(BindingResult result){
StringBuilder sb=new StringBuilder("");
if (result!=null && result.hasErrors()){
//java8 stream写法
result.getAllErrors().stream().forEach(error -> sb.append(error.getDefaultMessage()).append("\n"));
}
return sb.toString();
}
}

(3)从该代码中,可以得知,校验处理的方式主要是通过遍历获取其中的errors列表,然后获取每个error对象实例的message,这个message即为注解@NotBlank上的message属性的取值,这一点大家在自测的时候即可看到!

完了之后,就是开发相应的Service的处理逻辑,其完整源代码如下所示:

    @Override
public Integer saveEntity(Article article) throws Exception {
article.setId(null);
articleMapper.insertSelective(article);
//可以返回成功插入到数据时那条记录对应的主键id
return article.getId();
}
@Override
public void updateEntity(Article article) throws Exception {
if (article.getId()!=null && article.getId()>=0){
articleMapper.updateByPrimaryKeySelective(article);
}
}


在这里,我们设置了成功插入记录时,自动返回对应的数据库记录的主键id。接下来就可以进入自测了,如下两张图即可说明一切:  




二、分页列表查询

对于分页列表查询,我相信对于接触过企业级应用开发的小伙伴而言都是不陌生的,分页列表查询,其实其重点主要在于两点,第一点是需要保证前端传递的参数具有分页的参数pageNo、pageSize;第二点是后端在执行完相应的分页查询后不仅需要将得到的列表数据返回到前端,还需要返回其他的分页参数,如netxPage,total,size等等,目的是为了方便前端做分页插件的开发与显示。

(1)首先是Controller的代码:

    //查询
@RequestMapping(value = "query",method = RequestMethod.GET)
public BaseResponse query(@Validated ArticleQueryDto dto, BindingResult result){
String error=ValidatorUtil.checkResult(result);
if (StringUtils.isNotBlank(error)){
return new BaseResponse(StatusCode.Fail.getCode(),error);
}
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
response.setData(articleService.pageList(dto));
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}

其中的ArticleQueryDto主要用于接收前端请求过来的参数,如下所示:  

import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**@Author:debug (SteadyJack)
* @Date: 2019/8/27 9:41
**/
@Data
public class ArticleQueryDto implements Serializable{
@NotNull
private Integer pageNo=1;

@NotNull
private Integer pageSize=10;
//待搜索的内容
private String search;
}

  (2)接下来是Service的代码(主要就是借助于PageHelper分页插件来实现):  

    @Override
public PageInfo<Article> pageList(ArticleQueryDto dto) throws Exception {
PageHelper.startPage(dto.getPageNo(),dto.getPageSize());
return new PageInfo<>(articleMapper.pageSelect(dto.getSearch()));
}

  最后是对应的Mapper操作接口及其对应的动态Sql的配置mapper.xml,代码如下所示:  

List<Article> pageSelect(@Param("search") String search);

对应的实际的Sql写法如下所示:

<select id="pageSelect" resultType="com.debug.springboot.model.entity.Article" >
SELECT <include refid="Base_Column_List"/>
FROM article
<where>
<if test="search!=null and search!=''">
title LIKE CONCAT('%',#{search},'%')
</if>
</where>
</select>

  (3)最后,当然是进入自测啦,下图即可说明一切:  



三、删除

对于删除,也没啥好说的,值得注意的是,一般在开发删除的时候,我们都是采用批量删除的方式进行开发(批量当然也适合于删除单个的逻辑!)

(1)首先是Controller对应的代码:

    //删除
@RequestMapping(value = "delete",method = RequestMethod.POST)
public BaseResponse delete(@RequestBody DeleteDto dto){
if (dto.getIds()==null || dto.getIds().isEmpty()){
return new BaseResponse(StatusCode.InvalidParams);
}
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
articleService.deleteEntity(dto.getIds());
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}

  其中的DeleteDto,主要包括了待批量删除的id列表:  

import lombok.Data;
import java.io.Serializable;
import java.util.Set;
/**
* @Author:debug (SteadyJack)
* @Date: 2019/8/27 10:09
**/
@Data
public class DeleteDto implements Serializable{
private Set<Integer> ids;
}

  (2)在Service中实现批量删除的代码逻辑如下所示:  

    @Override
public void deleteEntity(Set<Integer> ids) throws Exception {
//第一种方式
/*if (ids!=null && !ids.isEmpty()){
ids.stream().forEach(id -> articleMapper.deleteByPrimaryKey(id));
}*/

//第二种方式
if (ids!=null && !ids.isEmpty()){
articleMapper.deleteBatch(Joiner.on(",").join(ids));
}
}

在这里,Debug采用了两种不同的方式进行实现,一种是循环遍历逐个删除;一种是采用“Delete From 表名 Where id IN(xxx)”的sql进行删除,为了能让mybatis执行这样的批量删除sql,我们需要将Set<Integer> ids转化为“id + 逗号”拼接起来的字符串格式,即Joiner.on(“,”).join(xx)即可实现,其对应的动态sql如下所示:

void deleteBatch(@Param("ids") String ids);

  对应的动态Sql:  

<delete id="deleteBatch">
DELETE FROM article
WHERE id IN (${ids})
</delete>

  (3)最后当然是进入自测了,一图就足以说明一切了:  


至此,基于Spring Boot整合Mybatis的分享介绍就到这里了,各位小伙伴可以将源代码check下来,然后照着文中的介绍敲一遍、自测一遍,完了之后就基本上可以说了掌握了Spring Boot项目下Mybatis的基本使用了!  

补充:

1、本文涉及到的相关的源代码可以到此地址,check出来进行查看学习:

https://gitee.com/steadyjack/SpringBootTechnology

2、关注一下Debug的技术微信公众号呗,最新的技术文章、技术课程以及技术专栏将会第一时间在公众号发布哦!