Spring Boot整合MyBatisPlus

    前言:?本博客主要是记录自己学习的过程,方便日后查看,当然也希望能够帮到大家。?

一, 说明:

    SpringBoot 2.7.0 整合MyBatis-Plus 3.5.2详细教程,并整合Alibaba Druid数据源,使用MySQL(版本5.7)数据库,使用hutool开源工具.

        1.MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。官网地址:戳我学习MyBatis-Plus

        2.Druid由阿里团队研发,后捐赠给Apache基金会,Druid是一个高效的数据查询系统,主要解决的是对于大量的基于时序的数据进行聚合查询。数据可以实时摄入,进入到Druid后立即可查,同时数据是几乎是不可变。通常是基于时序的事实事件,事实发生后进入Druid,外部系统就可以对该事实进行查询。官网地址:戳我学习Druid

        3.Hutool是国内程序员在工作中总结和积累而成的一套小而全的工具类库,相比于Guava,它更符合国内开发者的需求。Hutool首次发布于2014年,最新版本为5.6.5,到目前为止已经在github上收获了1.9万个赞。官网地址:戳我学习Hutool

二, 项目开始:

1.搭建项目

        为方便编写后续整合项目,直接搭建微服务式目录结构,新建父Maven项目如下图:

        如上图目录结构说明:

Spring Boot-demo
├── common-demo-- 工具类及通用代码模块
├── mybatisplus-demo -- 整合MyBatisPlus模块
├── sms-demo -- 整合阿里云短信模块(后续更新)
└── other-- 整合其他模块(后续更新)

1.引入相关依赖:

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.0</spring-boot.version>
        <fastjson.version>1.2.73</fastjson.version>
        <druid.version>1.2.11</druid.version>
        <mybatisPlus.version>3.5.2</mybatisPlus.version>
        <mysql.version>5.1.47</mysql.version>
        <pagehelper.version>1.4.2</pagehelper.version>
        <validation.version>2.7.0</validation.version>
        <hutool.version>5.4.1</hutool.version>
        <lang3.version>3.12.0</lang3.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--spring boot-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--fastjson-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>

            <!--druid-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>

            <!--mybatisPlus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatisPlus.version}</version>
            </dependency>

            <!-- mysql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            
            <!--validation-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
                <version>${validation.version}</version>
            </dependency>

           <!--hutool-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>

            <!--lang3-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${lang3.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

        :包含基础依赖,直接引入项目即可,后续继续增加其他依赖。

2.Cmmon-demo-- 工具类及通用代码模块介绍

        Cmmon目录结构如下图:

Cmmon-demo
├── enums-- 枚举类包
├── page -- 分页对象类包
├── response -- 响应类包
└── other-- 其他包(后续更新)

        引入相关依赖:

dependencies>
        <!--validation-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!--lang3-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <!--mybatisPlus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
    </dependencies>

        enums枚举类:

package com.wisesliu.enums;

import java.util.HashMap;
import java.util.Map;

/**
 * @author jixia
 * @date 2022/6/16 20:26
 * @description 全局异常枚举
 */
public enum GlobalErrorCode {
    SUCCESS(200, "操作成功"),
    FAIL(-2, "操作失败"),
    UNKNOWN(-1, "未知的错误"),
    ;

    private int errorCode;
    private String errorMsg;
    private static final Map<Integer, GlobalErrorCode> errorCodes = new HashMap<>();

    static {
        for (GlobalErrorCode errorCode : GlobalErrorCode.values()) {
            errorCodes.put(errorCode.getErrorCode(), errorCode);
        }
    }

    GlobalErrorCode(int errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public static GlobalErrorCode valueOf(int code) {
        GlobalErrorCode globalErrorCode = errorCodes.get(code);
        if (globalErrorCode != null) {
            return globalErrorCode;
        }
        return UNKNOWN;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }
}

        page分页类对象:
        (1)前端请求分页类:

/**
 * @author jixia
 * @date 2022/6/16 20:48
 * @description 前端传参分页对象
 */
public class FrontPage<T> {

    /**
     * 每页显示条数
     */
    @NotNull(message = "每页显示条数不能为空")
    private Integer size;

    /**
     * 当前页数
     */
    @NotNull(message = "当前页数不能为空")
    private Integer current;

    /**
     * 排序的字段,多个逗号分割
     */
    private String order;

    /**
     * 排序方式 true 升序  false 降序
     */
    private Boolean asc;

    public Integer getSize() {
        return size;
    }

    public void setSize(Integer size) {
        this.size = size;
    }

    public Integer getCurrent() {
        return current;
    }

    public void setCurrent(Integer current) {
        this.current = current;
    }

    public String getOrder() {
        return order;
    }

    public void setOrder(String order) {
        this.order = order;
    }

    public Boolean getAsc() {
        return asc;
    }

    public void setAsc(Boolean asc) {
        this.asc = asc;
    }

    /**
     * 构造mybatisPlus封装的Page对象
     *
     * @return
     */
    public Page<T> getPage() {
        Page<T> page = new Page<>();
        page.setCurrent(this.current);
        page.setSize(this.size);
        // 多排序字段处理
        if (StringUtils.isNotBlank(this.order)) {
            List<OrderItem> orders = new ArrayList<>();
            List<String> orderList = ListUtil.toList(this.order.split(","));
            for (String str : orderList) {
                OrderItem orderItem = new OrderItem();
                orderItem.setColumn(str);
                orderItem.setAsc(Objects.nonNull(this.asc) ? this.asc : false);
                orders.add(orderItem);
            }
            page.setOrders(orders);
        }
        return page;
    }
}

        (2)后端相应分页类:

/**
 * @author jixia
 * @date 2022/6/16 20:29
 * @description 后端响应分页对象
 */
public class CustomPage<T> {

    /**
     * 当前页数
     */
    private Long current;

    /**
     * 每页显示数量
     */
    private Long size;


    /**
     * 总页数
     */
    private Long total;

    /**
     * 总条数
     */
    private Long records;

    /**
     * 数据列表
     */
    private List<T> rows;

    public Long getCurrent() {
        return current;
    }

    public void setCurrent(Long current) {
        this.current = current;
    }

    public Long getSize() {
        return size;
    }

    public void setSize(Long size) {
        this.size = size;
    }

    public Long getRecords() {
        return records;
    }

    public void setRecords(Long records) {
        this.records = records;
    }

    public List<T> getRows() {
        return rows;
    }

    public void setRows(List<T> rows) {
        this.rows = rows;
    }

    public Long getTotal() {
        return total;
    }

    public void setTotal(Long total) {
        this.total = total;
    }

    public CustomPage(Page<T> page) {
        this.current = page.getCurrent();
        this.size = page.getSize();
        this.records = page.getTotal();
        this.rows = page.getRecords();
        this.total = page.getPages();
    }
}

        response后端响应类对象:
        (1)基础响应类对象:

/**
 * @author jixia
 * @date 2022/6/16 20:24
 * @description 接口响应基础类
 */
public class BaseResponse {

    private Integer status = 200;
    private String message;

    public BaseResponse() {
    }

    public BaseResponse(Integer status, String message) {
        this.status = status;
        this.message = message;
    }


    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public static BaseResponse success(String moreInfo) {
        int status = GlobalErrorCode.SUCCESS.getErrorCode();
        return new BaseResponse(status, moreInfo);
    }

    public static BaseResponse success() {
        int status = GlobalErrorCode.SUCCESS.getErrorCode();
        String msg = GlobalErrorCode.SUCCESS.getErrorMsg();
        return new BaseResponse(status, msg);
    }

    public static BaseResponse error(GlobalErrorCode errorCode, String moreInfo) {
        int status = errorCode.getErrorCode();
        return new BaseResponse(status, moreInfo);
    }

    public static BaseResponse error(GlobalErrorCode errorCode) {
        int status = errorCode.getErrorCode();
        String msg = errorCode.getErrorMsg();
        return new BaseResponse(status, msg);
    }

    public static BaseResponse error(String moreInfo) {
        int status = GlobalErrorCode.FAIL.getErrorCode();
        return new BaseResponse(status,moreInfo);
    }
}

        (1)返回响应类对象:

/**
 * @author jixia
 * @date 2022/6/16 20:32
 * @description 接口响应类
 */
public class ObjectRestResponse<T> extends BaseResponse {

    T data;

    private ObjectRestResponse() {
    }

    private ObjectRestResponse(int status, String msg, T data) {
        super(status, msg);
        this.data = data;
    }

    private ObjectRestResponse(int status, String msg) {
        super(status, msg);
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public static <T> ObjectRestResponse success(String moreInfo, T data) {
        int status = GlobalErrorCode.SUCCESS.getErrorCode();
        return new ObjectRestResponse<>(status, moreInfo, data);
    }

    public static <T> ObjectRestResponse success(T data) {
        int status = GlobalErrorCode.SUCCESS.getErrorCode();
        String msg = GlobalErrorCode.SUCCESS.getErrorMsg();
        return new ObjectRestResponse<>(status, msg, data);
    }

    public static <T> ObjectRestResponse error(GlobalErrorCode errorCode, String moreInfo, T data) {
        int status = errorCode.getErrorCode();
        return new ObjectRestResponse<>(status, moreInfo, data);
    }

    public static <T> ObjectRestResponse error(GlobalErrorCode errorCode, T data) {
        int status = errorCode.getErrorCode();
        String msg = errorCode.getErrorMsg();
        return new ObjectRestResponse<>(status, msg, data);
    }
}

3.mybatisplus-demo模块

        mybatisplus目录结构如下图:

mybatisplus-demo
├── config-- 配置类类包
├── entity--实体类包
├── mapper-- 数据操作接口类包
├── service-- 数据操作实现接口类包
├── controller-- 端点接口类包
└── -- 其他包(后续更新)

        引入相关依赖:

<dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>

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

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!---->
        <dependency>
            <artifactId>common-demo</artifactId>
            <groupId>com.wisesliu</groupId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

        详细配置文件:application.yml

server:
  port: 8001

spring:
  application:
    name: mybatisPlus-demo-server
  #配置数据源
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    # 连接池的配置信息
    druid:
      # 初始化大小,最小,最大 
      initial-size: 5
      min-idle: 5
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      # 配置DruidStatFilter
      web-stat-filter:
        enabled: true
        url-pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      # 配置DruidStatViewServlet
      stat-view-servlet:
        enabled: true
        url-pattern: "/druid/*"
        # IP白名单(没有配置或者为空,则允许所有访问)
        allow: 127.0.0.1
        # IP黑名单 (存在共同时,deny优先于allow)
        deny: 192.168.1.73
        #  禁用HTML页面上的“Reset All”功能
        reset-enable: false
        # 登录名
        login-username: druid
        # 登录密码
        login-password: druid

mybatis-plus:
    #外部化xml配置
    #config-location: classpath:mybatis-config.xml
    #指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
    #configuration-properties: classpath:mybatis/config.properties
    #xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
    #MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
    #type-aliases-package:
    #如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
    #type-aliases-super-type: java.lang.Object
    #枚举类 扫描路径,如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
    #type-enums-package: com.baomidou.mybatisplus.samples.quickstart.enums
    #项目启动会检查xml配置存在(只在开发时候打开)
    check-config-location: true
    configuration:
      # 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
      map-underscore-to-camel-case: false
      # 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true
      cache-enabled: false
      #懒加载
      #aggressive-lazy-loading: true
      #NONE:不启用自动映射 PARTIAL:只对非嵌套的 resultMap 进行自动映射 FULL:对所有的 resultMap 都进行自动映射
      #auto-mapping-behavior: partial
      #NONE:不做任何处理 (默认值)WARNING:以日志的形式打印相关警告信息 FAILING:当作映射失败处理,并抛出异常和详细信息
      #auto-mapping-unknown-column-behavior: none
      #如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
      call-setters-on-nulls: true
      # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    global-config:
      db-config:
        #表名下划线命名默认true
        table-underline: true
        #id类型
        id-type: auto
        #是否开启大写命名,默认不开启
        #capital-mode: false
        #逻辑已删除值,(逻辑删除下有效) 需要注入逻辑策略LogicSqlInjector@Bean方式注入
        logic-not-delete-value: 0
        #逻辑未删除值,(逻辑删除下有效)
        logic-delete-value: 1
     mapper-locations: classpath*:/mapper/*.xml

        数据表t_user

CREATE TABLE `t_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `fullname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户姓名',
  `mobile` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

        config

/**
 * @author jixia
 * @date 2022/6/16 20:22
 * @description 分页插件配置
 */
@Configuration
public class MyBatisPlusConfig {
    /**
     * 分页插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

:分页配置类,必须实现,详细查看官方文档:戳我学习MyBatis-Plus

        entity

entity
├── form-- 表单提交类
├── param--请求参数类
└── -- 对象类

form类

/**
 * @author jixia
 * @date 2022/6/16 20:43
 * @description 用户提交对象
 */
@Data
public class UserForm implements Serializable {

    /**
     * 用户名
     */
    @NotBlank
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 真实名
     */
    @NotBlank
    private String fullname;

    /**
     * 手机号
     */
    @NotBlank
    private String mobile;
    private static final long serialVersionUID = -8978461117607917157L;
}

param类

/**
 * @author jixia
 * @date 2022/6/16 20:47
 * @description 用户查询参数
 */
@Data
public class UserParam implements Serializable {

    /**
     * 用户名
     */
    private String username;
    /**
     * 真实名
     */
    private String fullname;

    /**
     * 手机号
     */
    private String mobile;
}

user类

/**
 * @author jixia
 * @date 2022/6/16 20:54
 * @description 系统用户
 */
@Data
@TableName("t_user")
public class User implements Serializable {
    /**
     * ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 用户名
     */
    @TableField("username")
    private String username;

    /**
     * 密码
     */
    @TableField("password")
    private String password;

    /**
     * 真实名
     */
    @TableField("fullname")
    private String fullname;

    /**
     * 手机号
     */
    @TableField("mobile")
    private String mobile;

    private static final long serialVersionUID = 4791568101260386736L;
}

        mapper

/**
 * @author jixia
 * @date 2022/6/16 20:46
 * @description
 */
@Mapper
public interface UserMapper  extends BaseMapper<User> {
}

        service

/**
 * @author jixia
 * @date 2022/6/16 20:47
 * @description
 */
public interface UserService extends IService<User> {
}

        serviceImpl

/**
 * @author jixia
 * @date 2022/6/16 20:47
 * @description
 */
@Service
public class UserServiceImpl  extends ServiceImpl<UserMapper, User> implements UserService {
}

        controller

/**
 * @author jixia
 * @date 2022/6/16 20:48
 * @description
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    /**
     * 分页查询用户
     *
     * @param page
     * @param param
     * @return
     */
    @GetMapping("/page")
    public ObjectRestResponse<CustomPage<User>> page(@Valid FrontPage<User> page, UserParam param) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(param.getUsername())) {
            wrapper.lambda().like(User::getUsername, param.getUsername());
        }
        if (StringUtils.isNotBlank(param.getFullname())) {
            wrapper.lambda().like(User::getFullname, param.getFullname());
        }
        if (StringUtils.isNotBlank(param.getMobile())) {
            wrapper.lambda().like(User::getMobile, param.getMobile());
        }
        Page<User> pageList = userService.page(page.getPage(), wrapper);
        return ObjectRestResponse.success(new CustomPage<>(pageList));
    }

    /**
     * 添加用户
     *
     * @param form
     * @return
     */
    @GetMapping("add")
    public ObjectRestResponse add(UserForm form) {
        User user = new User();
        BeanUtils.copyProperties(form, user);
        user.setPassword(StringUtils.isNotBlank(user.getPassword()) ? user.getPassword() : IdUtil.simpleUUID());
        userService.save(user);
        return ObjectRestResponse.success("操作成功", null);
    }
}

        启动类

**
 * @author jixia
 * @date 2022/6/16 20:15
 * @description
 */
@SpringBootApplication
public class MybatisPlusApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }
}

3.Druid效果:

1.Druid访问地址:

        http://127.0.0.1:8001/druid/login.html

        登录效果:

        SQL执行监控效果:

4.查询效果:

        请求地址:

http://127.0.0.1:8001/user/page?current=1&size=10

        请求方式::

GET

        参数::

current:当前页
size:分页数量

        完整代码地址:正在搭建中,后续公布。

        :此工程包含多个module,本文所用代码均在mybatisplus-demo模块下

        后记:?本次分享到此结束,本博主水平有限,难免有错误或遗漏之处,望大家指正和谅解,欢迎评论留言。?


版权声明:本文为LSXXN原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。