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)最后,當然是進入自測啦,下圖即可說明一切:  



三、刪除

對于刪除,也沒啥好說的,值得注意的是,一般在開發刪除的時候,我們都是采用批量刪除的方式進行開發(批量當然也適合于刪除單個的邏輯?。?/span>

(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的技術微信公眾號唄,最新的技術文章、技術課程以及技術專欄將會第一時間在公眾號發布哦!