Springboot单体架构搭建|第三章 springboot集成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中引入:

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.6</version>
        </dependency>

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

        <!-- freemarker 模板引擎 -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>

再来看MP的教程
在这里插入图片描述

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

实战

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

在这里插入图片描述
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.*;

/**
 * <p>
 * 资讯表 前端控制器
 * </p>
 *
 * @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;

/**
 * <p>
 * 资讯表
 * </p>
 *
 * @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;

/**
 * <p>
 * 资讯表 Mapper 接口
 * </p>
 *
 * @author JokerLiu
 * @since 2019-03-05
 */
public interface InformationMapper extends BaseMapper<Information> {

}

IInformationService

package com.jokerliu.manage.service;

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

/**
 * <p>
 * 资讯表 服务类
 * </p>
 *
 * @author JokerLiu
 * @since 2019-03-18
 */
public interface IInformationService extends IService<Information> {

}

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;

/**
 * <p>
 * 资讯表 服务实现类
 * </p>
 *
 * @author JokerLiu
 * @since 2019-03-18
 */
@Service
public class InformationServiceImpl extends ServiceImpl<InformationMapper, Information> implements IInformationService {

}

InformationMapper.xml

<?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="com.jokerliu.manage.mapper.InformationMapper">

</mapper>

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

代码生成

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

在这里插入图片描述
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 {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    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<FileOutConfig> 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>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Slf4j
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName??>/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>

    @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})));
    }
}
</#if>

直接执行CodeGenerator的main函数,输入完整模块名、父目录名、表名,就可以生成所需的在这里插入图片描述

总结和注意点

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值