SpringBoot 整合Mybatis-Plus

1,720 阅读9分钟

简介

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

快速上手

首先需要一个springboot 项目

在这里插入图片描述
引入MyBatis-Plus 的maven依赖(旧项目注意删除mybatis 相关的依赖):

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

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

在application.yml 里面配置你的mysql 连接信息和Mybatis-Plus 配置


spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/central_certification?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456

mybatis-plus:
  //自定义sql 路径
  mapper-locations: classpath*:/mapper/**/*.xml

我们在对应数据库新建一个user 表:

CREATE TABLE `central_certification`.`user`  (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '用户表自增id',
  `user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用户名称',
  `mail` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '用户邮箱',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '用户密码',
  `deleted` tinyint(1) NULL DEFAULT 0 COMMENT '是否删除(0 正常 1 删除)',
  `create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_mail_password`(`mail`) USING BTREE COMMENT '邮箱索引'
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

// 插入数据
INSERT INTO `user` VALUES (5, 'wt', '799957684@qq.com', '123456', 0, '2019-12-27 16:37:46', '2019-12-30 12:43:56');
INSERT INTO `user` VALUES (6, '唐江席', '1591226504@qq.com', '123456', 0, '2019-12-27 17:22:23', '2019-12-27 17:22:23');


在bean 目录下新建User 类:


import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@Builder
@TableName("user")
public class User extends Model<User> {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userName;
    private String password;
    private String mail;
    private Integer deleted;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

@TableName 用于指定类对应的数据库 @TableId 用于指定自增id 及其id 生产规则:

// 可指定的规则
   AUTO(0),
   NONE(1),
   INPUT(2),
   ASSIGN_ID(3),
   ASSIGN_UUID(4),
   /** @deprecated */
   @Deprecated
   ID_WORKER(3),
   /** @deprecated */
   @Deprecated
   ID_WORKER_STR(3),
   /** @deprecated */
   @Deprecated
   UUID(4);

Model<T> 类是Mybtais-Plus 的特性支持之一

支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作

什么意思呢,就是说我们可以直接使用类的对象来实现CRUD 操作,可以看看Model<T> 类中的方法:

在这里插入图片描述
这些方法不但方便还做了比较完备的检查,比如insertOrUpdate

// 根据id 来判断是否存在,存在更新,否则新增
public boolean insertOrUpdate() {
        return !StringUtils.checkValNull(this.pkVal()) && !Objects.isNull(this.selectById(this.pkVal())) ? this.updateById() : this.insert();
    }

我们来测试一下:

@org.junit.Test
    public void test2(){
        User user = User.builder()
                        .userName("test")
                        .mail("testMail")
                        .password("test")
                        .build();
        user.insert();
        System.out.println(userMapper.queryByMail("testMail"));
    }
    
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)

除了继承的方式,我们还可以通过接口来实现,在mapper 里面新建UserMapper:

package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper  extends BaseMapper<User> {
}

@Mapper 可以声明一个mapper ,除此之外可以在启动类指定扫描:

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

BaseMapper 也定义了一些常用的方法可以使用

在这里插入图片描述

@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {
    @Autowired
    private UserMapper userMapper;

    @org.junit.Test
    public void test(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deleted", 0);
        List<User> users = userMapper.selectList(queryWrapper);
        users.forEach(v -> {
            System.out.println(v);
        });
    }
}

User(id=5, userName=wt, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T16:37:46, updateTime=2019-12-30T12:43:56)
User(id=6, userName=唐江席, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T17:22:23, updateTime=2019-12-27T17:22:23)
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)

自定义sql

如果想自己写sql ,就和mybatis 一样

@Mapper
public interface UserMapper  extends BaseMapper<User> {
    User queryByMail(@Param("mail") String mail);
}

<?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.example.demo.mapper.UserMapper">

    <select id="queryByMail" resultType="com.example.demo.bean.User">
        select *
        from user
        where mail = #{mail}
    </select>
</mapper>

前面我们第一次测试的时候就用了这个方法,这里就不重复展示了。

记得在application.yml 里面配置映射

mybatis-plus:
  //自定义sql 路径
  mapper-locations: classpath*:/mapper/**/*.xml

批量操作

前面的两种方式我们发现这些方法多是些单体方法,当然还有批量的操作,在service 目录新建一个UserServiceImpl 实现:

package com.example.demo.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.bean.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {

}

在这里插入图片描述
直接测试吧:

package com.example.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.bean.User;
import com.example.demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.stream.Collectors;

@RunWith(SpringRunner.class)
@SpringBootTest
class UserServiceImplTest {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserServiceImpl userService;

    @Test
    public void test() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deleted", 0);
        List<User> userList = userMapper.selectList(queryWrapper);
        System.out.println("更新前:");
        userList.forEach(v -> {
            System.out.println(v);
        });
        // 批量更新userList
        userList = userList.stream().map(user -> {
            user.setUserName(user.getUserName() + "1");
            return user;
        }).collect(Collectors.toList());
        userService.updateBatchById(userList);
        List<User> userUpdateList = userMapper.selectList(queryWrapper);
        System.out.println("更新后:");
        userUpdateList.forEach(v -> {
            System.out.println(v);
        });
    }
}

更新前:
User(id=5, userName=wt, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T16:37:46, updateTime=2019-12-30T12:43:56)
User(id=6, userName=唐江席, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T17:22:23, updateTime=2019-12-27T17:22:23)
User(id=7, userName=test, password=test, mail=testMail, deleted=0, createTime=2020-02-01T20:11:37, updateTime=2020-02-01T20:11:37)
更新后:
User(id=5, userName=wt1, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T02:37:46, updateTime=2019-12-29T22:43:56)
User(id=6, userName=唐江席1, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T03:22:23, updateTime=2019-12-27T03:22:23)
User(id=7, userName=test1, password=test, mail=testMail, deleted=0, createTime=2020-02-01T06:11:37, updateTime=2020-02-01T06:11:37)

分页操作

使用分页需要先引入配置,不然分页不会生效:

//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

XML 自定义分页

UserMapper.java 方法内容

public interface UserMapper {//可以继承或者不继承BaseMapper
    /**
     * <p>
     * 查询 : 根据state状态查询用户列表,分页显示
     * </p>
     *
     * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
     * @param state 状态
     * @return 分页对象
     */
    IPage<User> selectPageVo(Page<?> page, Integer state);
}

UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页

<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
    SELECT id,name FROM user WHERE state=#{state}
</select>

Mybatis-Plus 分页

使用Mybatis-Plus 分页可能会报错提示无法代理

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.service.UserServiceImplTest': 
Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userServiceImpl' is expected to be of type 
'com.example.demo.service.UserServiceImpl' but was actually of type 'com.sun.proxy.$Proxy76'

需要在启动类指定使用cglib

/**
 * proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。
 * 如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用
 * (这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个
 * 属性被省略,那么标准的JDK 基于接口的代理
 *
 */
@EnableAsync(proxyTargetClass = true)
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
@Test
    public void pageTest(){
        int pageNum = 1;
        int pageSize = 2;
        Page<User> page = new Page<>(pageNum, pageSize);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deleted", 0);
        Page<User> userPage = userMapper.selectPage(page, queryWrapper);
        System.out.println("记录:");
        userPage.getRecords().forEach(v -> {
            System.out.println(v);
        });
        System.out.println("当前页码" + userPage.getCurrent());
        System.out.println("总数" + userPage.getTotal());
    }
记录:
User(id=5, userName=wt1, password=123456, mail=799957684@qq.com, deleted=0, createTime=2019-12-27T02:37:46, updateTime=2019-12-29T22:43:56)
User(id=6, userName=唐江席1, password=123456, mail=1591226504@qq.com, deleted=0, createTime=2019-12-27T03:22:23, updateTime=2019-12-27T03:22:23)
当前页码1
总数3

可见总数为3,只展示2 条记录,分页成功。 可以看一下Page 里的参数:

在这里插入图片描述

总结

总的来说MyBatis-Plus 就是一个增强版的mybatis,它整合很多比较好用的功能,比如类似Example 的动态sql、generate 的自动生成各个层级的代码、还有防攻击等新功能,最关键是MyBatis-Plus 是非侵入的,引入不需要修改旧代码,推荐一试。

Mybatis-Plus 的其他功能后续会继续尝试更新。