目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

MyBatis-Plus

什么是 MyBatis?

 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。消除了 JDBC 的复杂性,MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis vs JPA

MyBatis 的优势

  • SQL 语句可以自由控制,更灵活、性能较高。
  • SQL 与代码分离、易于阅读和维护
  • 提供 XML 标签,支持编写动态 SQL 语句

MyBatis 的劣势

  • 简单的 CRUD 操作还得写 SQL 语句(XML 或注解)
  • XML 中有大量的 SQL 要维护
  • MyBatis 自身功能很有限,但支持 Plugin

MyBatis-Plus

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

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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、SQLServer2005、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

框架结构

mybatis-plus-framework

快速入门

建库建表

 1#创建用户表
 2CREATE TABLE user (
 3    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
 4    name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
 5    age INT(11) DEFAULT NULL COMMENT '年龄',
 6    email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
 7    manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
 8    create_time DATETIME DEFAULT NULL COMMENT '创建时间',
 9    CONSTRAINT manager_fk FOREIGN KEY (manager_id)
10        REFERENCES user (id)
11)  ENGINE=INNODB CHARSET=UTF8;
12
13#初始化数据:
14INSERT INTO user (id, name, age, email, manager_id
15	, create_time)
16VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL
17		, '2019-01-11 14:20:20'),
18	(1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
19		, '2019-02-05 11:12:22'),
20	(1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
21		, '2019-02-14 08:31:16'),
22	(1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
23		, '2019-01-14 09:15:15'),
24	(1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
25		, '2019-01-14 09:48:16');

引入依赖

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4    <modelVersion>4.0.0</modelVersion>
 5    <parent>
 6        <groupId>org.springframework.boot</groupId>
 7        <artifactId>spring-boot-starter-parent</artifactId>
 8        <version>2.1.8.RELEASE</version>
 9        <relativePath/> <!-- lookup parent from repository -->
10    </parent>
11    <groupId>com.soulboy</groupId>
12    <artifactId>mp-test-01</artifactId>
13    <version>0.0.1-SNAPSHOT</version>
14    <name>mp-test-01</name>
15    <description>Demo project for Spring Boot</description>
16
17    <properties>
18        <java.version>1.8</java.version>
19    </properties>
20
21    <dependencies>
22        <!-- SpringBoot启动类 -->
23        <dependency>
24            <groupId>org.springframework.boot</groupId>
25            <artifactId>spring-boot-starter</artifactId>
26        </dependency>
27
28        <!-- lombok -->
29        <dependency>
30            <groupId>org.projectlombok</groupId>
31            <artifactId>lombok</artifactId>
32            <optional>true</optional>
33        </dependency>
34
35        <!-- SpringBoot test 启动类 -->
36        <dependency>
37            <groupId>org.springframework.boot</groupId>
38            <artifactId>spring-boot-starter-test</artifactId>
39            <scope>test</scope>
40        </dependency>
41
42        <!-- Mysql JDBC 驱动类 -->
43        <dependency>
44            <groupId>mysql</groupId>
45            <artifactId>mysql-connector-java</artifactId>
46        </dependency>
47
48        <!-- Mybatis-Plus 启动器 -->
49        <dependency>
50            <groupId>com.baomidou</groupId>
51            <artifactId>mybatis-plus-boot-starter</artifactId>
52            <version>3.2.0</version>
53        </dependency>
54
55    </dependencies>
56
57    <build>
58        <plugins>
59            <plugin>
60                <groupId>org.springframework.boot</groupId>
61                <artifactId>spring-boot-maven-plugin</artifactId>
62            </plugin>
63        </plugins>
64    </build>
65</project>

配置
application.yml

编码
启动类

 1package com.soulboy.mptest01;
 2
 3import org.mybatis.spring.annotation.MapperScan;
 4import org.springframework.boot.SpringApplication;
 5import org.springframework.boot.autoconfigure.SpringBootApplication;
 6
 7@SpringBootApplication
 8@MapperScan("com.soulboy.mptest01.dao")
 9public class MpTest01Application {
10    public static void main(String[] args) {
11        SpringApplication.run(MpTest01Application.class, args);
12    }
13}

实体类

 1package com.soulboy.mptest01.domain;
 2
 3import lombok.Data;
 4
 5import java.time.LocalDateTime;
 6@Data
 7public class User {
 8    private Long id;
 9    private String name;
10    private Integer age;
11    private String email;
12    private Long managerId;
13    private LocalDateTime createTime;
14}

Mapper

1package com.soulboy.mptest01.dao;
2
3import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4import com.soulboy.mptest01.domain.User;
5
6public interface UserMapper extends BaseMapper<User>{
7}

测试

 1package com.soulboy.mptest01;
 2
 3import com.soulboy.mptest01.dao.UserMapper;
 4import com.soulboy.mptest01.domain.User;
 5import org.junit.Assert;
 6import org.junit.Test;
 7import org.junit.runner.RunWith;
 8import org.springframework.beans.factory.annotation.Autowired;
 9import org.springframework.boot.test.context.SpringBootTest;
10import org.springframework.test.context.junit4.SpringRunner;
11
12import java.sql.SQLOutput;
13import java.util.List;
14
15@RunWith(SpringRunner.class)
16@SpringBootTest
17public class MpTest01ApplicationTests {
18    @Autowired
19    private UserMapper userMapper;
20
21    @Test
22    public void select() {
23        List<User> list = userMapper.selectList(null);
24        Assert.assertEquals(5,list.size());
25        list.forEach(System.out::println);
26    }
27}

Create

新增方法
BaseMapper

  • 默认 POJO 属性值为 null,生成 SQL 的语句中不会带有该列。
  • 默认采用雪花算法的自增 ID,填充主键字。
  • 下划线自动转驼峰(managerId => manager_id)
 1// 这里没有设置email属性,因此email属性的默认值为null,当数据的值为null时,sql语句中不会出现email列,相关列的数据不会改变。
 2    @Test
 3    public void insert(){
 4        User user = new User();
 5        user.setName("秃秃");
 6        user.setAge(31);
 7        user.setManagerId(1088248166370832385L);
 8        user.setCreateTime(LocalDateTime.now());
 9        int rows = userMapper.insert(user);
10        System.out.println("影响记录数: "+ rows); //影响记录数: 1
11    }

注解

  • @TableName:显式指定实体类对应的数据库表名。
 1@Data
 2@TableName("mp_user")
 3public class User {
 4    private Long id;
 5    private String name;
 6    private Integer age;
 7    private String email;
 8    private Long managerId;
 9    private LocalDateTime createTime;
10}
  • @TableId:显式指定主键。
 1@Data
 2@TableName("mp_user")
 3public class User {
 4    @TableId
 5    private Long id;
 6    private String name;
 7    private Integer age;
 8    private String email;
 9    private Long managerId;
10    private LocalDateTime createTime;
11}
  • @TableField:显式指定属性对应数据库中的列名。
 1@Data
 2@TableName("mp_user")
 3public class User {
 4    @TableId
 5    private Long id;
 6    @TableField("name")
 7    private String name;
 8    private Integer age;
 9    private String email;
10    private Long managerId;
11    private LocalDateTime createTime;
12}

排除非表字段
 实体类的有些数据只是为了存放临时数据。

  • transient:标识的属性不会参与序列化。
1private transient String remark;//备注(在数据库中没有对应的字段)
  • static:需要手动设置静态的 set 和 get 方法。
1private static String remark;//备注(在数据库中没有对应的字段)
  • @TableField(:需要手动设置静态的 set 和 get 方法。
1@TableField(exist=false):推荐使用这种方式。
2private String remark;//备注(在数据库中没有对应的字段)

Select

基查询方法

 1@Test
 2    public void select() {
 3        List<User> list = userMapper.selectList(null);
 4        Assert.assertEquals(5,list.size());
 5        list.forEach(System.out::println);
 6    }
 7
 8    @Test
 9    public void selectById() {
10        User user = userMapper.selectById(1094590409767661570L);
11        System.out.println(user);
12    }
13
14    @Test
15    public void selectByIds() {
16        List<Long> idList = Arrays.asList(1094590409767661570L, 1088248166370832385L);
17        List<User> users = userMapper.selectBatchIds(idList);
18        for (User user : users) {
19            System.out.println(user);
20        }
21    }
22
23    @Test
24    public void selectByMap(){
25        //Map的key是数据库中的列名(不是实体类的属性名),Map的值是过滤条件
26        //SELECT id,create_time,name,manager_id,email,age FROM user WHERE name = ? AND age = ?
27        HashMap<String, Object> columnMap = new HashMap<>();
28        columnMap.put("name", "王天风");
29        columnMap.put("age", 25);
30        List<User> users = userMapper.selectByMap(columnMap);
31        users.forEach(System.out::println);
32    }

以条件构造器为参数的查询方法

select 中字段不全出现的处理方法
 默认查询返回表中全部的列。

条件构造器中 condition 的作用

 1@Test
 2    public void testCondition(){
 3        String name = "王";
 4        String email = "";
 5        //// Preparing: SELECT id,create_time,name,manager_id,email,age FROM user WHERE (name LIKE ?) 
 6        condition(name, email);
 7    }
 8
 9    private void condition(String name, String email){
10        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
11        userQueryWrapper.like(StringUtils.isNotEmpty(name),"name", name)
12                .like(StringUtils.isNotEmpty(email), "email", email);
13        List<User> userList = userMapper.selectList(userQueryWrapper);
14        userList.forEach(System.out::println);
15    }

创建条件构造器时传入实体对象
 默认传入 Entity 中属性值不为 null 的字段将作为查询条件,默认使用等值判断。

 1@Test
 2    public void selectByWrapperEntity(){
 3        User whereUser = new User();
 4        whereUser.setName("秃秃");
 5        whereUser.setAge(31);
 6
 7        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(whereUser);
 8        // Preparing: SELECT id,create_time,name,manager_id,email,age FROM user WHERE name=? AND age=?
 9        // Parameters: 秃秃(String), 31(Integer)
10        List<User> userList = userMapper.selectList(userQueryWrapper);
11        userList.forEach(System.out::println);
12
13    }

 如果需要该表 Entity 默认的等值条件判断,需要修改 Entity 实体类属性上面的默认规则。

条件构造器中 allEq 用法
 key 为数据库字段名,value 为字段值。

 1@Test
 2    public void selectByWrapperAllEq(){
 3        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
 4        HashMap<String, Object> params = new HashMap<>();
 5        params.put("name","王天风");
 6        params.put("age", 25);
 7        //params.put("age", null);  //is null
 8        //userQueryWrapper.allEq(params,false); //false代表:params中key为null的条件会被忽略
 9        userQueryWrapper.allEq(params);
10        List<User> userList = userMapper.selectList(userQueryWrapper);
11        userList.forEach(System.out::println);
12
13        //User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22)
14    }

其他以条件构造器为参数的查询方式

 1// List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
 2// 只需要实体类的少数字段的时候,如果返回整个实体类,不需要的字段为null,这样做并不优雅,这个时候可以使用QueryWrapper的selectMaps方法,并且之前使用select指定需要的列名。
 3
 4    @Test
 5    public void selectByWrapperMaps() {
 6        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
 7        userQueryWrapper.select("id","name").like("name", "雨").lt("age", 40);
 8
 9        List<Map<String, Object>> userList = userMapper.selectMaps(userQueryWrapper);
10        userList.forEach(System.out::println);
11
12        //{name=张雨琪, id=1094590409767661570}
13        //{name=刘红雨, id=1094592041087729666}
14    }

统计分组

 1/**
 2     * 按照直属上级分组,查询每组的平均年龄、最大年龄、最小年龄。
 3     * 并且只取年龄总和小于500的组。
 4     * select avg(age) avg_age, min(age) min_age, max(age) max_age from user group by manager_id having sum(age) < 500
 5     *
 6     * DEBUG==>  Preparing: SELECT avg(age) avg_age,min(age) min_age,max(age) max_age FROM user GROUP BY manager_id HAVING sum(age)<?
 7     * DEBUG==> Parameters: 500(Integer)
 8     */
 9    @Test
10    public void selectByWrapperMaps2() {
11        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
12        userQueryWrapper.select("avg(age) avg_age", "min(age) min_age", "max(age) max_age")
13                .groupBy("manager_id").having("sum(age)<{0}", 500);
14
15        List<Map<String, Object>> userList = userMapper.selectMaps(userQueryWrapper);
16        userList.forEach(System.out::println);
17
18        //{max_age=40, avg_age=40.0000, min_age=40}
19        //{max_age=25, avg_age=25.0000, min_age=25}
20        //{max_age=32, avg_age=30.5000, min_age=28}
21    }
22
23    /**
24     * 适用于条查询结果只需要第一列(每个实体中的第一列):查询的是2列,但是只有第一列被设置到List<Object>中。
25     * TRACE<==        Row: 1094590409767661570, 张雨琪
26     * TRACE<==        Row: 1094592041087729666, 刘红雨
27     */
28    @Test
29    public void selectByWrapperObjs() {
30        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
31        userQueryWrapper.select("id","name").like("name", "雨").lt("age", 40);
32
33        List<Object> userList = userMapper.selectObjs(userQueryWrapper);
34        userList.forEach(System.out::println);
35        // 1094590409767661570
36        // 1094592041087729666
37    }
38
39    /**
40     * 查询结果必须是一条记录或者没有结果,如果查询结果多余一条会报错:返回Entity。
41     */
42    @Test
43    public void selectByWrapperOne() {
44        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
45        userQueryWrapper.like("name", "红雨").lt("age", 40);
46
47        User user = userMapper.selectOne(userQueryWrapper);
48        System.out.println(user);
49    }

Lambda 条件构造器

自定义 SQL

使用条件构造器的自定义 SQL
注解的方式
UserMapper

 1import com.baomidou.mybatisplus.core.conditions.Wrapper;
 2import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 3import com.baomidou.mybatisplus.core.toolkit.Constants;
 4import com.soulboy.mptest01.domain.User;
 5import org.apache.ibatis.annotations.Param;
 6import org.apache.ibatis.annotations.Select;
 7
 8import java.util.List;
 9
10public interface UserMapper extends BaseMapper<User>{
11
12    @Select("select * from user ${ew.customSqlSegment}")
13    List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
14
15}

测试

1@Test
2    public void selectMy() {
3        LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
4        lambdaQueryWrapper.likeRight(User::getName, "王")
5                .and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
6        List<User> userList = userMapper.selectAll(lambdaQueryWrapper);
7        userList.forEach(System.out::println);
8	//User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22)
9    }

XML 的方式
pom
 mapper 的 XML 配置文件没有被编译,在 pom 文件中添加。

 1<build>
 2        <plugins>
 3            <plugin>
 4                <groupId>org.springframework.boot</groupId>
 5                <artifactId>spring-boot-maven-plugin</artifactId>
 6            </plugin>
 7        </plugins>
 8
 9        <resources>
10            <resource>
11                <directory>src/main/java/</directory>
12                <includes>
13                    <include>**/*.xml</include>
14                </includes>
15                <filtering>false</filtering>
16            </resource>
17        </resources>
18    </build>

application.yml

1mybatis-plus:
2    mapper-locations: classpath:com/soulboy/mptest01/mapper/*.xml

UserMapper 接口

 1import com.baomidou.mybatisplus.core.conditions.Wrapper;
 2import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 3import com.baomidou.mybatisplus.core.toolkit.Constants;
 4import com.soulboy.mptest01.domain.User;
 5import org.apache.ibatis.annotations.Param;
 6//import org.apache.ibatis.annotations.Select;
 7
 8import java.util.List;
 9
10public interface UserMapper extends BaseMapper<User>{
11
12    List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
13
14}

UserMapper.xml

1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
3<mapper namespace="com.soulboy.mptest01.dao.UserMapper">
4    <select id="selectAll" resultType="com.soulboy.mptest01.domain.User">
5        select * from user ${ew.customSqlSegment}
6    </select>
7</mapper>

测试

1@Test
2    public void selectMy() {
3        LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
4        lambdaQueryWrapper.likeRight(User::getName, "王")
5                .and(lqw -> lqw.lt(User::getAge, 40).or().isNotNull(User::getEmail));
6        List<User> userList = userMapper.selectAll(lambdaQueryWrapper);
7        userList.forEach(System.out::println);
8	//User(id=1088248166370832385, name=王天风, age=25, email=wtf@baomidou.com, managerId=1087982257332887553, createTime=2019-02-05T11:12:22)
9    }

分页

MyBatis 分页介绍
 MyBatis 的分页并非是物理分页,一次查询出所有数据存储在本地内存中,缺点如下:

  • 第一次查询速度慢
  • 如果表中的记录数多,则会占用大量的物理内存

MP 分页插件实现物理分页
配置类

 1import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
 2import org.springframework.context.annotation.Bean;
 3import org.springframework.context.annotation.Configuration;
 4
 5@Configuration
 6public class MybatisPlusConfig {
 7
 8    @Bean
 9    public PaginationInterceptor paginationInterceptor(){
10        return new PaginationInterceptor();
11    }

使用 MP 的分页插件(物理分页)

 1/**
 2     * selectPage()
 3     * 年龄大于等于26岁的
 4     */
 5    @Test
 6    public void selectByPages(){
 7        //QueryWrapper<User> query = Wrappers.<User>query();
 8        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
 9        //列名(不是实体类中的属性名)
10        userQueryWrapper.ge("age", 26);
11
12
13
14        //分页信息 (当前页码,每页条数)
15        Page<User> page = new Page<>(1, 2);
16
17        IPage<User> iPage = userMapper.selectPage(page, userQueryWrapper);
18        System.out.println("总页数: " + iPage.getPages()); //
19        System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
20        List<User> userList = iPage.getRecords();
21        
22        userList.forEach(System.out::println);
23        //User(id=1087982257332887553, name=大boss, age=40, email=boss@baomidou.com, managerId=null, createTime=2019-01-11T14:20:20)
24        //User(id=1088250446457389058, name=李艺伟, age=28, email=lyw@baomidou.com, managerId=1088248166370832385, createTime=2019-02-14T08:31:16)
25
26    }
27
28    /**
29     * selectMapsPage()
30     * 年龄大于等于26岁的
31     */
32    @Test
33    public void selectByPages2(){
34        //QueryWrapper<User> query = Wrappers.<User>query();
35        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
36        //列名(不是实体类中的属性名)
37        userQueryWrapper.ge("age", 26);
38
39        //分页信息 (当前页码,每页条数)
40        Page<User> page = new Page<>(1, 2);
41
42        IPage<Map<String, Object>> iPage = userMapper.selectMapsPage(page, userQueryWrapper);
43        System.out.println("总页数: " + iPage.getPages()); //
44        System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
45        List<Map<String, Object>> userList = iPage.getRecords();
46        userList.forEach(System.out::println);
47        //{create_time=2019-01-11 14:20:20.0, name=大boss, id=1087982257332887553, email=boss@baomidou.com, age=40}
48        //{create_time=2019-02-14 08:31:16.0, manager_id=1088248166370832385, name=李艺伟, id=1088250446457389058, email=lyw@baomidou.com, age=28}
49    }
50
51    /**
52     * 不需要总记录数:第三个参数可以传入false,这样每次查询就不会发出count()统计总记录数,避免性能损耗。
53     * 年龄大于等于26岁的
54     */
55    @Test
56    public void selectByPages3(){
57        //QueryWrapper<User> query = Wrappers.<User>query();
58        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
59        //列名(不是实体类中的属性名)
60        userQueryWrapper.ge("age", 26);
61
62        //分页信息 (当前页码,每页条数)
63        Page<User> page = new Page<>(1, 2,false);
64
65        IPage<Map<String, Object>> iPage = userMapper.selectMapsPage(page, userQueryWrapper);
66        System.out.println("总页数: " + iPage.getPages()); //
67        System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
68        List<Map<String, Object>> userList = iPage.getRecords();
69        userList.forEach(System.out::println);
70        //{create_time=2019-01-11 14:20:20.0, name=大boss, id=1087982257332887553, email=boss@baomidou.com, age=40}
71        //{create_time=2019-02-14 08:31:16.0, manager_id=1088248166370832385, name=李艺伟, id=1088250446457389058, email=lyw@baomidou.com, age=28}
72    }

多表自定义查询
Mapper

1import java.util.List;
2
3public interface UserMapper extends BaseMapper<User>{
4
5    List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
6
7    IPage<User> selectUserPage(Page<User> page,@Param(Constants.WRAPPER)Wrapper<User> wrapper);
8}

Mapper.xml

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
 3<mapper namespace="com.soulboy.mptest01.dao.UserMapper">
 4    <select id="selectAll" resultType="com.soulboy.mptest01.domain.User">
 5        select * from user ${ew.customSqlSegment}
 6    </select>
 7
 8    <select id="selectUserPage" resultType="com.soulboy.mptest01.domain.User">
 9        select * from user ${ew.customSqlSegment}
10    </select>
11</mapper>

测试方法

 1/**
 2     * 自定义方法
 3     */
 4    @Test
 5    public void selectMyPage(){
 6        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
 7        userQueryWrapper.ge("age", 26);
 8
 9        Page<User> page = new Page<>(1, 2);
10
11        IPage<User> iPage = userMapper.selectUserPage(page, userQueryWrapper);
12        System.out.println("总页数: " + iPage.getPages()); //
13        System.out.println("总记录数: " + iPage.getTotal()); //SELECT COUNT(1) FROM user WHERE (age >= ?)
14
15        List<User> userList = iPage.getRecords();
16        userList.forEach(System.out::println);
17        
18//        总页数: 3
19//        总记录数: 5
20//        User(id=1087982257332887553, name=大boss, age=40, email=boss@baomidou.com, managerId=null, createTime=2019-01-11T14:20:20)
21//        User(id=1088250446457389058, name=李艺伟, age=28, email=lyw@baomidou.com, managerId=1088248166370832385, createTime=2019-02-14T08:31:16)
22    }

Update

 1/**
 2     * updateById
 3     * 属性为null的字段不会参与Set操作
 4     */
 5    @Test
 6    public void updateByIds(){
 7        User user = new User();
 8        user.setId(1088248166370832385L);
 9        user.setAge(26);
10        user.setEmail("wtf2@baomidou.com");
11        int rows = userMapper.updateById(user);
12        System.out.println("影响记录数: "+ rows);
13
14//        DEBUG==>  Preparing: UPDATE user SET email=?, age=? WHERE id=?
15//        DEBUG==> Parameters: wtf2@baomidou.com(String), 26(Integer), 1088248166370832385(Long) DEBUG<==    Updates: 1
16//        影响记录数: 1
17    }
18
19    /**
20     * updateById
21     * 属性为null的字段不会参与Set操作
22     */
23    @Test
24    public void updateByWrapper(){
25        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
26        updateWrapper.eq("name", "李艺伟").eq("age", 28);
27
28        User user = new User();
29        user.setEmail("lyw2019@baomidou.com");
30        user.setAge(29);
31        int rows = userMapper.update(user, updateWrapper);
32
33        System.out.println("影响记录数: "+ rows);
34
35//        DEBUG==>  Preparing: UPDATE user SET email=?, age=? WHERE (name = ? AND age = ?)
36//        DEBUG==> Parameters: lyw2019@baomidou.com(String), 29(Integer), 李艺伟(String), 28(Integer)
37//        DEBUG<==    Updates: 1
38//        影响记录数: 1
39    }
40
41
42    /**
43     * 当只需要更新少数字段的时候,创建实体类则会显得比较麻烦。
44     * 可以在Wrapper后面使用链式调用增加新的设置条件
45     */
46    @Test
47    public void updateByWrapper2(){
48        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
49        updateWrapper.eq("name", "李艺伟").eq("age", 29).set("age",30);
50
51        int rows = userMapper.update(null, updateWrapper);
52
53        System.out.println("影响记录数: "+ rows);
54
55//        DEBUG==>  Preparing: UPDATE user SET age=? WHERE (name = ? AND age = ?)
56//        DEBUG==> Parameters: 30(Integer), 李艺伟(String), 29(Integer)
57//                DEBUG<==    Updates: 1
58//        影响记录数: 1
59    }
60
61    /**
62     * lambdaUpdate
63     */
64    @Test
65    public void updateByLambda(){
66        LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.<User>lambdaUpdate();
67        lambdaUpdateWrapper.eq(User::getName, "李艺伟").eq(User::getAge, 30).set(User::getAge, 31);
68
69        int rows = userMapper.update(null, lambdaUpdateWrapper);
70        System.out.println("影响记录数: " + rows);
71    }
72
73    /**
74     * lambdaUpdate
75     */
76    @Test
77    public void updateByLambdaChain(){
78        //是否更新成功:大于0会返回true,否则会返回false
79        boolean status = new LambdaUpdateChainWrapper<User>(userMapper)
80                .eq(User::getName, "李艺伟").eq(User::getAge, 31).set(User::getAge, 32).update();
81
82        System.out.println("更新是否成功: " + status);
83
84//    DEBUG==>  Preparing: UPDATE user SET age=? WHERE (name = ? AND age = ?)
85//    DEBUG==> Parameters: 32(Integer), 李艺伟(String), 31(Integer)
86//    DEBUG<==    Updates: 1
87//    更新是否成功: true
88    }

Delete

 1/**
 2     * deleteById
 3     * 根据主键删除
 4     */
 5    @Test
 6    public void deleteById(){
 7        int rows = userMapper.deleteById(1178959693804662786L);
 8        System.out.println("删除记录数: " + rows);
 9    }
10
11    /**
12     * deleteByMap
13     * 条件删除
14     */
15    @Test
16    public void deleteByMap(){
17        HashMap<String, Object> columMap = new HashMap<>();
18        columMap.put("name", "斑秃");
19        columMap.put("age", 39);
20        int rows = userMapper.deleteByMap(columMap);
21        System.out.println("删除记录数: " + rows);
22
23//        DEBUG==>  Preparing: DELETE FROM user WHERE name = ? AND age = ?
24//        DEBUG==> Parameters: 斑秃(String), 39(Integer)
25//                DEBUG<==    Updates: 1
26//        删除记录数: 1
27    }
28
29    /**
30     * deleteBatchIds
31     * 批量删除
32     */
33    @Test
34    public void deleteBatchIds(){
35        int rows = userMapper.deleteBatchIds(Arrays.asList(1094592041087729666L, 1088250446457389058L));
36        System.out.println("删除记录数: " + rows);
37    }
38
39    /**
40     * deleteByWrapper
41     * 根据条件包装器封装删除条件
42     */
43    @Test
44    public void deleteByWrapper(){
45        LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.<User>lambdaQuery();
46        userLambdaQueryWrapper.eq(User::getAge, 27).or().gt(User::getAge, 39);
47        int rows = userMapper.delete(userLambdaQueryWrapper);
48        System.out.println("删除记录数: " + rows);
49    }

ActiiveRecord

 Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。简而言之就是可以通过模型了进行 CRUD。

  • 实体类继续 Model 抽象类
 1import com.baomidou.mybatisplus.annotation.TableField;
 2import com.baomidou.mybatisplus.annotation.TableId;
 3import com.baomidou.mybatisplus.annotation.TableName;
 4import com.baomidou.mybatisplus.extension.activerecord.Model;
 5import lombok.Data;
 6import lombok.EqualsAndHashCode;
 7
 8import java.time.LocalDateTime;
 9@Data
10@EqualsAndHashCode(callSuper = false)
11//@TableName("mp_user")
12public class User extends Model<User> {
13    private static final long serialVersionUID = 1L;
14    @TableId
15    private Long id;
16//  @TableField("name")
17    private String name;
18    private Integer age;
19    private String email;
20    private Long managerId;
21    private LocalDateTime createTime;
22
23    //private transient String remark;//备注(在数据库中没有对应的字段)
24}
  • Mapper 类需要继承 BaseMapper 接口
 1import com.baomidou.mybatisplus.core.conditions.Wrapper;
 2import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 3import com.baomidou.mybatisplus.core.metadata.IPage;
 4import com.baomidou.mybatisplus.core.toolkit.Constants;
 5import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 6import com.soulboy.mptest01.domain.User;
 7import org.apache.ibatis.annotations.Param;
 8//import org.apache.ibatis.annotations.Select;
 9
10import java.util.List;
11
12public interface UserMapper extends BaseMapper<User>{
13
14    List<User> selectAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
15
16    IPage<User> selectUserPage(Page<User> page,@Param(Constants.WRAPPER)Wrapper<User> wrapper);
17}

测试

 1/**
 2     * 新增方法
 3     */
 4    @Test
 5    public void insertAR(){
 6        User user = new User();
 7        user.setName("大斑秃");
 8        user.setAge(35);
 9        user.setEmail("datutu@bantu.com");
10        user.setManagerId(1088248166370832385L);
11        user.setCreateTime(LocalDateTime.now());
12        int rows = userMapper.insert(user);
13        System.out.println("插入是否成功: "+ rows); //插入成功返回true
14//        DEBUG==>  Preparing: INSERT INTO user ( id, create_time, name, manager_id, email, age ) VALUES ( ?, ?, ?, ?, ?, ? )
15//        DEBUG==> Parameters: 1181047125463207937(Long), 2019-10-07T11:22:31.581(LocalDateTime), 大斑秃(String), 1088248166370832385(Long), datutu@bantu.com(String), 35(Integer)
16//                DEBUG<==    Updates: 1
17    }
18
19    /**
20     * 查询方法
21     * 查询出来的是新对象user1,没有把值设置到user中,user是空对象。
22     * selectById()
23     */
24    @Test
25    public void selectByIdAR(){
26        User user = new User();
27        User user1 = user.selectById("1181047125463207937L");
28        System.out.println(user1);
29    }
30
31    /**
32     * 查询方法
33     * 查询出来的是新对象user1,没有把值设置到user中,user是空对象。
34     * selectById()
35     */
36    @Test
37    public void selectByIDAR2(){
38        User user = new User();
39        user.setId(1181047125463207937L);
40        User user1 = user.selectById();
41        System.out.println(user1);
42    }
43
44    /**
45     * 更新方法
46     * updateById()
47     */
48    @Test
49    public void updateByIdAR(){
50        User user = new User();
51        user.setId(1181047125463207937L);
52        user.setName("小斑秃");
53        boolean status = user.updateById();
54        System.out.println("是否更新成功: " + status);
55    }
56
57    /**
58     * 删除方法:删除不存在的逻辑上属于成功
59     * deleteById
60     */
61    @Test
62    public void deleteByIdAR(){
63        User user = new User();
64        user.setId(1181047125463207937L);
65        boolean status = user.deleteById();
66        System.out.println("是否删除成功: " + status);
67    }
68
69    /**
70     * 如果不设置ID就是插入;如果设置ID会根据ID查询数据库,如果存在执行update,不存在依然执行insert
71     */
72    @Test
73    public void insertOrUpdate(){
74        User user = new User();
75        //user.setId(xxx);  //如果设置ID会根据ID查询数据库,如果存在执行update,不存在依然执行insert
76        user.setName("张强");
77        user.setAge(28);
78        user.setEmail("zq@baomidou.com");
79        user.setManagerId(1088248166370832385L);
80        user.setCreateTime(LocalDateTime.now());
81        boolean status = user.insertOrUpdate();
82        System.out.println("插入or更新是否成功: " + status);
83    }

主键策略

 策略生效需要修改为表的主键为自增

1alter table user modify id bigint(20) auto_increment primary key;

局部主键策略实现

 1import com.baomidou.mybatisplus.annotation.IdType;
 2import com.baomidou.mybatisplus.annotation.TableField;
 3import com.baomidou.mybatisplus.annotation.TableId;
 4import com.baomidou.mybatisplus.annotation.TableName;
 5import com.baomidou.mybatisplus.extension.activerecord.Model;
 6import lombok.Data;
 7import lombok.EqualsAndHashCode;
 8
 9import java.time.LocalDateTime;
10@Data
11@EqualsAndHashCode(callSuper = false)
12//@TableName("mp_user")
13public class User extends Model<User> {
14    private static final long serialVersionUID = 1L;
15    //@TableId(type = IdType.NONE) //无主键策略  需要自己生成主键
16    //以下三种算法只有当主键ID为空才会自动填充
17    //@TableId(type = IdType.ID_WORKER)  //雪花算法
18    //@TableId(type = IdType.ID_WORKER_STR)  //雪花算法字符串类型
19    //@TableId(type = IdType.UUID) //数据库中主键类型需要改为varchar   实体类中的属性类型需要改为String
20    //@TableId
21    private Long id;
22//  @TableField("name")
23    private String name;
24    private Integer age;
25    private String email;
26    private Long managerId;
27    private LocalDateTime createTime;
28
29    //private transient String remark;//备注(在数据库中没有对应的字段)
30}

全局主键策略实现
 application.yml 局部的 @TableId(type = IdType.UUID) 可以注释掉
 如果全局策略和局部策略都设置了,局部策略优先级更高。

1mybatis-plus:
2    mapper-locations: classpath:com/soulboy/mptest01/mapper/*.xml
3    global-config:
4      db-config:
5        id-type: uuid

MP 配置

基本配置

通用 Service

UserService 接口(需要继承 IService

1import com.baomidou.mybatisplus.extension.service.IService;
2import com.soulboy.mptest01.domain.User;
3
4public interface UserService extends IService<User> {
5}

UserServiceImpl 实现类 (需要继承 ServiceImpl<TMapper,T> 并且实现 UserService )

 1import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 2import com.soulboy.mptest01.dao.UserMapper;
 3import com.soulboy.mptest01.domain.User;
 4import com.soulboy.mptest01.service.UserService;
 5import org.springframework.stereotype.Service;
 6
 7@Service
 8public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
 9
10}

通用 Service

 1import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 2import com.soulboy.mptest01.domain.User;
 3import com.soulboy.mptest01.service.UserService;
 4import org.junit.Test;
 5import org.junit.runner.RunWith;
 6import org.springframework.beans.factory.annotation.Autowired;
 7import org.springframework.boot.test.context.SpringBootTest;
 8import org.springframework.test.context.junit4.SpringRunner;
 9
10import java.sql.Wrapper;
11import java.util.Arrays;
12import java.util.List;
13
14@RunWith(SpringRunner.class)
15@SpringBootTest
16public class ServiceTest {
17
18    @Autowired
19    private UserService userService;
20
21    @Test
22    public void getOne(){
23        User one = userService.getOne(Wrappers.<User>lambdaQuery().gt(User::getAge, 25),false);
24        System.out.println(one);
25    }
26
27    /**
28     * saveBatch
29     */
30    @Test
31    public void Batch(){
32        User user1 = new User();
33        user1.setName("爱因斯坦1");
34        user1.setAge(28);
35
36        User user2 = new User();
37        user2.setName("爱因斯坦2");
38        user2.setAge(29);
39
40        List<User> userList = Arrays.asList(user1, user2);
41        boolean status = userService.saveBatch(userList);//成功则返回true
42        System.out.println(status);
43//        DEBUG==>  Preparing: INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
44//        DEBUG==> Parameters: 1181459698335604738(Long), 爱因斯坦1(String), 28(Integer)
45//        DEBUG==> Parameters: 1181459698494988289(Long), 爱因斯坦2(String), 29(Integer)
46//        true
47    }
48
49    /**
50     * saveOrUpdateBatch
51     */
52    @Test
53    public void BatchMix(){
54        User user1 = new User();
55        user1.setName("爱因斯坦4");
56        user1.setAge(31);
57
58        User user2 = new User();
59        user2.setId(1181459698494988289L);
60        user2.setName("爱因斯坦2");
61        user2.setAge(32);
62
63        List<User> userList = Arrays.asList(user1, user2);
64        boolean status = userService.saveOrUpdateBatch(userList);//成功则返回true
65        System.out.println(status);
66    }
67
68    /**
69     * lambdaQuery()
70     * 条件查询
71     */
72    @Test
73    public void BatchChain(){
74        List<User> userList = userService.lambdaQuery().gt(User::getAge, 25).like(User::getName, "雨").list();
75        userList.forEach(System.out::println);
76    }
77
78    /**
79     * lambdaUpdate()
80     * 条件更新
81     */
82    @Test
83    public void BatchChain2(){
84        boolean status = userService.lambdaUpdate().eq(User::getAge, 25).set(User::getAge, 26).update();
85        System.out.println(status);
86    }
87
88    /**
89     * lambdaUpdate().where().remove()
90     * 条件删除
91     */
92    @Test
93    public void BatchChain3(){
94        boolean status = userService.lambdaUpdate().eq(User::getAge, 24).remove();
95        System.out.println(status);
96    }
97
98}

作者:Soulboy