Springboot单体架构搭建|第三章 springboot集成mybatis-plus

第三章 集成mybatis-plus

  • 前言
    • mybatis
    • mybatis-plus
    • 实战
    • 代码生成
  • 总结和注意点

前言

该架构是参考公司原架构做了优化,计划慢慢从0开始完全独立自己搭建一个基于springboot的restful服务后台架构,并且完全后端分离。系列文章所涉及的项目源码都放在了个人github上,关于前端我采用vue,后期会写在其他文章中。
本人的github地址:https://github.com/jokerliuli
接上一章,本文开始springboot集成mybatis-plus

mybatis

mybatis,作为dao层大哥大框架,可以说是目前使用率最高的。面试的时候也几乎是必问的一项(底层源码如果能看懂差不多就能敲门HAT了),关于它的详细信息这篇文章不再过多介绍,我这篇文章是在各位同学已经学会mybatis的基础上展开。

mybatis-plus

我们直接进入mybatis-plus。
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是MP官网的介绍,很简单很准确,按照我个人理解,MP其实是MyBatis模仿JPA的,然后对tkmap(通用mapper)的一种升级。
什么是JPA呢,这边简单介绍一下就是一个dao层的ORM框架规则,spring-data-jpa就是jpa的一个实现,jpa旨在减少sql,直接通过接口的命名就自动映射拼接sql。有兴趣的同学可以去自学一下,这边也不多介绍了。
MP很好的继承了tk的通用接口(单表的增删改查),然后在此基础上有自己的条件构造器(类似jpa的地方),最后还有自己的代码生成器。这些在官网的guide里写的很清晰,建议结合guide一起看这篇文章。
先在pom中引入:

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.0.6
        

如果需要用到MP的代码生成还需要引入freemarker 模板引擎:

        
        
            org.freemarker
            freemarker
            2.3.28
        

再来看MP的教程
Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第1张图片

看到这个用过tk的同学应该已经懂了,这个dao层有现成的写好的很多CRUD接口,我们只需要extends BaseMapper,直接点就能用。

实战

假如我们有一个Information表,我们怎么对其完成增删改查的restful接口呢?
目录如下:

Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第2张图片
InformationController

package com.jokerliu.manage.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import com.jokerliu.enums.Result;
import com.jokerliu.enums.ResultStatusCode;
import javax.annotation.Resource;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jokerliu.manage.service.IInformationService;
import com.jokerliu.manage.entity.Information;
import org.springframework.web.bind.annotation.*;

/**
 * 

* 资讯表 前端控制器 *

* * @author JokerLiu * @since 2019-03-18 */ @Slf4j @RestController @RequestMapping("/manage/information") public class InformationController { @Resource private IInformationService iInformationService; /** * 单个新增 * @param information 传递的实体 * @return Result */ @PostMapping("save") public Result save(@RequestBody Information information){ //log.info(information.toString()); return new Result(ResultStatusCode.OK,iInformationService.save(information)); } /** * 单个删除 * @param information 传递的实体 * @return Result */ @PostMapping("remove") public Result remove(@RequestBody Information information) { return new Result(ResultStatusCode.OK,iInformationService.remove(new QueryWrapper(information))); } /** * 单个更新 * @param information 传递的实体 * @return Result */ @PostMapping("update") public Result update(@RequestBody Information information) { //information.setUpdateDate(null); return new Result(ResultStatusCode.OK,iInformationService.updateById(information)); } /** * 根据id查询获取一个返回 * @param id 对象id * @return Result */ @GetMapping("getOne") public Result getOne(@RequestParam(name = "id") Long id){ return new Result(ResultStatusCode.OK,iInformationService.getById(id)); } /** * 条件查询list * @param information 传递的实体 * @return Result */ @PostMapping("list") public Result list( @RequestBody Information information) { return new Result(ResultStatusCode.OK,iInformationService.list(new QueryWrapper(information))); } /** * 条件分页查询 * @param page 第几页(1开始) * @param limit 每页size * @return Result */ @PostMapping("page") public Result page(@RequestParam(name = "page") int page, @RequestParam(name = "limit") int limit, @RequestBody Information information) { return new Result(ResultStatusCode.OK,iInformationService.page(new Page<>(page,limit),new QueryWrapper(information))); } }

Information

package com.jokerliu.manage.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.jokerliu.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * 

* 资讯表 *

* * @author JokerLiu * @since 2019-03-18 */ @Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) @TableName("manage_information") public class Information extends BaseEntity { private static final long serialVersionUID = 1L; /** * 文章标题 */ private String title; /** * 简略标题 */ private String shortTitle; /** * 分类栏目:新闻动态(1),产品方案(2),成功案例(3) */ private Integer informationType; /** * 关键词 */ private String keyword; /** * 文章摘要 */ private String summary; /** * 文章作者 */ private String author; /** * 删除(0)草稿(1)发布(2) */ private Integer publishStatus; /** * 缩略图 */ private String thumbnail; /** * 文章内容 */ private String content; }

InformationMapper

package com.jokerliu.manage.mapper;

import com.jokerliu.manage.entity.Information;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * 

* 资讯表 Mapper 接口 *

* * @author JokerLiu * @since 2019-03-05 */ public interface InformationMapper extends BaseMapper { }

IInformationService

package com.jokerliu.manage.service;

import com.jokerliu.manage.entity.Information;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * 

* 资讯表 服务类 *

* * @author JokerLiu * @since 2019-03-18 */ public interface IInformationService extends IService { }

InformationServiceImpl

package com.jokerliu.manage.service.impl;

import com.jokerliu.manage.entity.Information;
import com.jokerliu.manage.mapper.InformationMapper;
import com.jokerliu.manage.service.IInformationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * 

* 资讯表 服务实现类 *

* * @author JokerLiu * @since 2019-03-18 */ @Service public class InformationServiceImpl extends ServiceImpl implements IInformationService { }

InformationMapper.xml







其中我们注意IInformationServiceInformationMapper,他们需要继承IServiceBaseMapperInformationMapper.xml什么都不需要写,然后就能实现MP文档中的那些CRUD接口了。
接下来就是在InformationController等地方书写自己需要的逻辑就行了。这边我已经写好了controller层的几个常用接口了。

代码生成

上面的实战例子里的controller,其实就是我通过代码生成加载自定义controller模板生成的。
这边我直接贴出我的代码生成器配置和模板,各位同学需要按照自己需求结合MP关于代码生成的文档来修改,不要直接黏贴使用,这个模板的自定义性太强了,我的模板你很可能不适合。

Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第3张图片
CodeGenerator

package com.jokerliu;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * Created by Joker
 * Date: 2018/12/10
 * Time: 13:57
 *@author alex
 * 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
 */
public class CodeGenerator {

    /**
     * 

* 读取控制台内容 *

*/ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); /** * 拼接模块名 */ String projectPath = System.getProperty("user.dir")+"/"+scanner("完整模块名"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("JokerLiu"); gc.setOpen(false); mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://47.100.27.31:3306/jladmin?characterEncoding=utf-8&allowMultiQueries=true"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("alex"); dsc.setPassword("536"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(scanner("父目录名")); pc.setParent("com.jokerliu"); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; List focList = new ArrayList<>(); focList.add(new FileOutConfig("/templates/mapper.xml.ftl") { @Override public String outputFile(TableInfo tableInfo) { // 自定义输入文件名称 return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig tc = new TemplateConfig(); tc.setController("templates/controller.java"); // 配置自定义输出模板 //指定自定义模板路径(resource目录下),注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); tc.setXml(null); mpg.setTemplate(tc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setSuperEntityClass("com.jokerliu.entity.BaseEntity"); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); // strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController"); strategy.setInclude(scanner("表名")); // 忽略那些数据库字段,因为BaseEntity里已经包含 strategy.setSuperEntityColumns("id","remarks","sort","version","status","uuid","create_by","create_date","update_by","update_date"); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); } }

模板controller.java.ftl

package ${package.Controller};

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.extern.slf4j.Slf4j;
import com.jokerliu.enums.Result;
import com.jokerliu.enums.ResultStatusCode;
import javax.annotation.Resource;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
<#if restControllerStyle>
import org.springframework.web.bind.annotation.*;
<#else>
import org.springframework.stereotype.Controller;

<#if superControllerClassPackage??>
import ${superControllerClassPackage};


/**
 * 

* ${table.comment!} 前端控制器 *

* * @author ${author} * @since ${date} */ @Slf4j <#if restControllerStyle> @RestController <#else> @Controller @RequestMapping("<#if package.ModuleName??>/${package.ModuleName}/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}") <#if kotlin> class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}() <#else> <#if superControllerClass??> public class ${table.controllerName} extends ${superControllerClass} { <#else> public class ${table.controllerName} { @Resource private ${table.serviceName} i${entity}Service; /** * 单个新增 * @param ${table.entityPath} 传递的实体 * @return Result */ @PostMapping("save") public Result save(@RequestBody ${entity} ${table.entityPath}){ //log.info(${table.entityPath}.toString()); return new Result(ResultStatusCode.OK,i${entity}Service.save(${table.entityPath})); } /** * 单个删除 * @param ${table.entityPath} 传递的实体 * @return Result */ @PostMapping("remove") public Result remove(@RequestBody ${entity} ${table.entityPath}) { return new Result(ResultStatusCode.OK,i${entity}Service.remove(new QueryWrapper(${table.entityPath}))); } /** * 单个更新 * @param ${table.entityPath} 传递的实体 * @return Result */ @PostMapping("update") public Result update(@RequestBody ${entity} ${table.entityPath}) { //${table.entityPath}.setUpdateDate(null); return new Result(ResultStatusCode.OK,i${entity}Service.updateById(${table.entityPath})); } /** * 根据id查询获取一个返回 * @param id 对象id * @return Result */ @GetMapping("getOne") public Result getOne(@RequestParam(name = "id") Long id){ return new Result(ResultStatusCode.OK,i${entity}Service.getById(id)); } /** * 条件查询list * @param ${table.entityPath} 传递的实体 * @return Result */ @PostMapping("list") public Result list( @RequestBody ${entity} ${table.entityPath}) { return new Result(ResultStatusCode.OK,i${entity}Service.list(new QueryWrapper(${table.entityPath}))); } /** * 条件分页查询 * @param page 第几页(1开始) * @param limit 每页size * @return Result */ @PostMapping("page") public Result page(@RequestParam(name = "page") int page, @RequestParam(name = "limit") int limit, @RequestBody ${entity} ${table.entityPath}) { return new Result(ResultStatusCode.OK,i${entity}Service.page(new Page<>(page,limit),new QueryWrapper(${table.entityPath}))); } }

直接执行CodeGenerator的main函数,输入完整模块名、父目录名、表名,就可以生成所需的Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第4张图片

总结和注意点

MP的官方文档很详细,只要懂mybatis都学起来很快。这边说几个重点
1.条件构造器:
Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第5张图片
通常new QueryWapper()就可以满足日常开发。
2.自定义分页
分页有时候需要自定义sql(比如关联查询)
Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第6张图片
Springboot单体架构搭建|第三章 springboot集成mybatis-plus_第7张图片
3.关联查询,使用到数据库的某些函数的时候,等还是xml里自己写sql吧,建议不要过度依赖MP,基本遇到什么问题就先查MP文档,优先MP解决,不可以在考虑自己写sql。推荐大家学习一下jpa,以后应该是主流。

你可能感兴趣的:(SpringBoot架构)