MyBatist 概述

MyBatis Plus官网:https://baomidou.com/

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

MyBatis Plus 支持的数据库

  • MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb

  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

MyBatis Plus的特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

MyBatis Plus的核心架构

MyBatis Plus快速入门

创建测试表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CREATE DATABASE if not exists `mybatisplus` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci ;


DROP TABLE IF EXISTS user;

CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`sex` char(1) DEFAULT NULL COMMENT '性别 0:男 1:女',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`birthday` date DEFAULT NULL COMMENT '生日',
PRIMARY KEY (`id`)
);


INSERT INTO `user` VALUES (1, 'Jone', '1', 27, '2001-10-04');
INSERT INTO `user` VALUES (2, 'Jack', '0', 20, '1999-10-04');
INSERT INTO `user` VALUES (3, 'Tom', '1', 28, '1996-08-12');
INSERT INTO `user` VALUES (4, 'Sandy', '1', 21, '2001-10-04');
INSERT INTO `user` VALUES (5, 'Billie', '0', 24, '1992-09-07');
INSERT INTO `user` VALUES (6, 'Jackson', '0', 18, '1996-08-12');
INSERT INTO `user` VALUES (7, 'Hardy', '1', 25, '1992-09-07');
INSERT INTO `user` VALUES (8, 'Rose', '1', 21, '1992-09-07');
INSERT INTO `user` VALUES (9, 'June', '0', 28, '1992-09-07');
INSERT INTO `user` VALUES (10, 'Aidan', '0', 17, '2001-10-04');

引入依赖

  • springBoot2

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.7</version>
    </dependency>
  • Spring Boot3

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.7</version>
    </dependency>

创建项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>pers.fulsun</groupId>
<artifactId>01_MyBatisPlus</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/>
</parent>

<dependencies>
<!--springboot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--测试场景-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis plus场景-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!--测试单元-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

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

<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package pers.fulsun.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String sex;
private Integer age;
private String birthday;
}

Mapper接口

1
2
3
4
5
6
7
8
9
10
package pers.fulsun.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import pers.fulsun.entity.User;

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

配置application.yml

1
2
3
4
5
6
7
8
9
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2b8
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

启动类

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package pers.fulsun;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("pers.fulsun.mapper")
public class MyBatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisPlusApplication.class, args);
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package pers.fulsun.mapper;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.Assert;
import pers.fulsun.entity.User;

import java.util.List;

@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;

@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.isTrue(5 == userList.size(), "");
userList.forEach(System.out::println);
}
}

MyBatis Plus的使用

BaseMapper的基本CURD

在MyBatis Plus中,我们编写的Mapper接口都继承与MyBatis Plus提供的BaseMapper接口,BaseMapper接口包含了MyBatis Plus帮我们提供操作数据库的一系列方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package pers.fulsun.mapper;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import pers.fulsun.entity.User;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

@SpringBootTest
public class Demo01_BaseMapper的基本CURD {
@Autowired
private UserMapper userMapper;

/**
* 新增
* INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
*/
@Test
public void insert() {
User user = new User(100L, "Ken", "0",20,"2012-01-01 23:59:59");
userMapper.insert(user);
}

/**
* 修改
* UPDATE user SET name=?, age=?, email=? WHERE id=?
*/
@Test
public void update() {
User user = new User(100L, "Ken", "0",20,"2012-01-01 23:59:59");
userMapper.updateById(user);
}


/**
* 根据id查询
* SELECT id,name,age,email FROM user WHERE id=?
*/
@Test
public void selectById() {
User user = userMapper.selectById(100L);
System.out.println(user);
}

/**
* 根据一批id查询
* SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )
*/
@Test
public void selectBatchIds() {
List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
for (User user : userList) {
System.out.println(user);
}
}

/**
* 根据条件查询数据
* SELECT id,name,age,email FROM user WHERE name = ? AND id = ?
*/
@Test
public void selectByMap() {
// 封装条件
HashMap<String, Object> param = new HashMap<>();
param.put("id",10L);
param.put("name","Kevin");

// 根据条件查询,map中的多个条件是并列关系 id=10 and name='小灰灰'
List<User> userList = userMapper.selectByMap(param);
for (User user : userList) {
System.out.println(user);
}
}

/**
* 根据id删除
* DELETE FROM user WHERE id=?
*/
@Test
public void deleteById() {
userMapper.deleteById(100L);
}

/**
* 根据id删除一批
* DELETE FROM user WHERE id IN ( ? , ? , ? )
*/
@Test
public void deleteBatchIds() {
userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
}

/**
* 根据条件删除数据
* DELETE FROM user WHERE name = ? AND id = ?
*/
@Test
public void deleteByMap() {
// 封装条件
HashMap<String, Object> param = new HashMap<>();
param.put("id",100L);
param.put("name","Kevin");

userMapper.deleteByMap(param);
}
}

Wrapper查询

通过BaseMapper提供的一些方法我们可以完成一些基本的CRUD,但无法完成复杂条件的查询;对于复杂条件的查询,MyBatis Plus提供了Wrapper接口来处理;

Wrapper是MyBatis Plus提供的一个条件构造器,主要用于构建一系列条件,当Wrapper构建完成后,可以使用Wrapper中的条件进行查询、修改、删除等操作;

Wrapper是条件构造抽象类,最顶端父类,其主要实现类有如下:

  • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

    • QueryWrapper : Query条件封装

    • UpdateWrapper : Update条件封装

    • AbstractLambdaWrapper : 使用Lambda语法

      • LambdaQueryWrapper :基于Lambda语法使用的查询Wrapper

      • LambdaUpdateWrapper : 基于Lambda语法使用的更新Wrapper

Wrapper的基本方法

AbstractWrapper是其他常用Wrapper的父类,用于生成 sql 的 where 条件,AbstractWrapper提供了很多公有的方法,其子类全部具备这些方法,方法列表如下:

方法名 解释 示例
eq 等于 = eq(“name”, “老王”)—> name = ‘老王’
ne 不等于 <> ne(“name”, “老王”)—> name <> ‘老王’
gt 大于 > gt(“age”, 18)—> age > 18
ge 大于等于 >= ge(“age”, 18)—> age >= 18
lt 小于 < lt(“age”, 18)—> age < 18
le 小于等于 <= le(“age”, 18)—> age <= 18
between between 值1 and 值2 between(“age”, 18, 30)—> age between 18 and 30
notBetween not between 值1 and 值2 notBetween(“age”, 18, 30)—> age not between 18 and 30
like LIKE ‘%值%’ like(“name”, “王”)—> name like ‘%王%’
notLike NOT LIKE ‘%值%’ notLike(“name”, “王”)—> name not like ‘%王%’
likeLeft LIKE ‘%值’ likeLeft(“name”, “王”)—> name like ‘%王’
likeRight LIKE ‘值%’ likeRight(“name”, “王”)—> name like ‘王%’
isNull 字段 IS NULL isNull(“name”)—> name is null
isNotNull 字段 IS NOT NULL isNotNull(“name”)—> name is not null
in 字段 IN (v0, v1, …) in(“age”, 1, 2, 3)—> age in (1,2,3)
notIn 字段 NOT IN (v0, v1, …) notIn(“age”, 1, 2, 3)—> age not in (1,2,3)
inSql 字段 IN ( sql语句 ) inSql(“id”, “select id from table where id < 3”)—> id in (select id from table where id < 3)
notInSql 字段 NOT IN ( sql语句 ) notInSql(“id”, “select id from table where id < 3”)—> id not in (select id from table where id < 3)
groupBy 分组:GROUP BY 字段, … groupBy(“id”, “name”)—> group by id,name
orderByAsc 排序:ORDER BY 字段, … ASC orderByAsc(“id”, “name”)—> order by id ASC,name ASC
orderByDesc 排序:ORDER BY 字段, … DESC orderByDesc(“id”, “name”)—> order by id DESC,name DESC
orderBy 排序:ORDER BY 字段, … orderBy(true, true, “id”, “name”)—> order by id ASC,name ASC
having HAVING ( sql语句 ) 例1:having(“sum(age) > 10”)—> having sum(age) > 10 例2:having(“sum(age) > {0}”, 11)—> having sum(age) > 11
func 主要解决条件拼接 func(i -> if(true) {i.eq(“id”, 1)} else {i.ne(“id”, 1)})
or 拼接 OR eq(“id”,1).or().eq(“name”,”老王”)—> id = 1 or name = ‘老王’
and AND 嵌套 and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))—> and (name = ‘李白’ and status <> ‘活着’)
nested 用于多条件拼接时 nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”))
—> (name = ‘李白’ and status <> ‘活着’)
apply 用于拼接SQL语句 例1:apply(“id = 1”)—> id = 1
例2:apply(“id = {0}”,1)—> id = 1
例3:apply(“name like {0} and age > {1}”,”%J%”,18) —> name like ‘%J%’ and age > 18
last 无视优化规则直接拼接到 sql 的最后 last(“limit 1”) —> 在SQL语句最后面拼接:limit 1
exists 拼接 EXISTS ( sql语句 ) exists(“select id from table where age = 1”)—> exists (select id from table where age = 1)
notExists 拼接 NOT EXISTS ( sql语句 ) notExists(“select id from table where age = 1”)—> not exists (select id from table where age = 1)

创建Wrapper对象:

1
2
3
4
5
// 1. Wrappers静态方法:
public static <T> QueryWrapper<T> query()

// 2.通过QueryWrapper对象的构造方法:
public QueryWrapper()

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package pers.fulsun.mapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import pers.fulsun.entity.User;

import java.util.List;

@SpringBootTest
public class Demo02_Wrapper基本方法 {
@Autowired
private UserMapper userMapper;

/**
* QueryMapper的创建
* SELECT id,name,age,email FROM user
*/
@Test
public void test1() {
// 创建QueryMapper,默认情况下查询所有数据
QueryWrapper<User> wrapper = Wrappers.query();
QueryWrapper<User> wrapper2 = new QueryWrapper<>();

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

@Test
public void test2() {
QueryWrapper<User> wrapper = Wrappers.query();
// name ='Jack'
wrapper.eq("name", "Jack");

String name = "Jack";
// 参数1: 是否要进行name条件的拼接
wrapper.eq(name != null, "name", name);

// name != 'Jack'
wrapper.ne("name", "Jack");

// age > 20
wrapper.gt("age", 20);

// age < 20
wrapper.lt("age", 20);

// age=20
wrapper.lt("age", 20);

// age between 20 and 24
wrapper.between("age", 20, 24);

// age not between 20 and 24
wrapper.notBetween("age", 20, 24);

// name like "%J%" 自动拼接左右的%
wrapper.like("name", "J");

// name not like "%J%"
wrapper.notLike("name", "J");

// name like "%J"
wrapper.likeLeft("name", "J");

// name like 'J%'
wrapper.likeRight("name", "J");

// name is null
wrapper.isNull("name");

// name is not null
wrapper.isNotNull("name");

// name in ('Jack','Tom','Jone')
wrapper.in("name", "Jack", "Tom", "Jone");

// name not in ('Jack','Tom','Jone')
wrapper.notIn("name", "Jack", "Tom", "Jone");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
}

子查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test3() {
// 创建wrapper对象
QueryWrapper<User> wrapper = Wrappers.query();

// 相当于: name in (select name from user where age > 21)
wrapper.inSql("name", "select name from user where age>21");

// 相当于: name not in (select name from user where age > 21)
// wrapper.notInSql("name", "select name from user where age>21");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

分组

通过Wrapper.query()构建的查询字段默认是表中的所有字段,因此在这种情况下分组是没有意义的,分组具体的用法我们后面再详细介绍;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test4() {
// 创建wrapper对象
QueryWrapper<User> wrapper = Wrappers.query();

/*
相当于 select * from user where gropu sex
这是一个没有意义的分组,分组必须结合查询的字段来体现,后续学QueryWrapper的select方法再介绍
*/
// wrapper.groupBy("sex");

// group by sex having sex = 0
wrapper.groupBy("sex");
wrapper.having("sex", "0");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test5() {
// 创建wrapper对象
QueryWrapper<User> wrapper = Wrappers.query();

/**
* 参数1: 条件
* 参数2:是否是Asc排序(升序), true : asc排序, false: desc排序
* 参数3: 排序的字段
*/
wrapper.orderBy(true, true, "age");
// wrapper.orderByAsc("age"); // order by age asc
// wrapper.orderByDesc("age"); // order by age desc

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

多条件的拼接

Wrapper对象在调用每一个方法时都会返回当前对象(Wrapper),这样可以很好的方便我们链式编程;另外MyBatis Plus在拼接多个条件时默认使用and拼接,如果需要使用or,那么需要显示的调用or()方法;

and拼接条件

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test7() {
// 创建wrapper对象
QueryWrapper<User> wrapper = Wrappers.query();
// 默认情况下,多条件是以and拼接
// SQL语句: name LIKE "%a%" AND age > 20 AND sex = 0
wrapper.like("name", "%a%")
.lt("age", 20)
.eq("sex", 0);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

or拼接条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test8() {
// 创建wrapper对象
QueryWrapper<User> wrapper = Wrappers.query();

/*
默认情况下,多条件是以and拼接
SQL语句: name LIKE "%a%" OR age > 20 AND sex = 0
*/
wrapper.like("name", "%a%")
.or()
.lt("age", 20)
.eq("sex", 0); // 该条件仍然以AND拼接

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

and方法拼接其他的整体条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void test9() {
QueryWrapper<User> wrapper = Wrappers.query();

// 生成的SQL为: (age < ? AND (sex = ? OR name LIKE ?))
wrapper.lt("age", 20);
wrapper.and(new Consumer<QueryWrapper<User>>() {
@Override
public void accept(QueryWrapper<User> userQueryWrapper) {
userQueryWrapper.eq("sex", 0).or().like("name", "J");
}
});
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

其他方法

func方法

用于多条件的拼接,直接使用之前的方法也可以做到这个功能;示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void test10() {
QueryWrapper<User> wrapper = Wrappers.query();

// 生成的SQL语句条件: (age < ? AND name LIKE ? AND sex = ?)
wrapper.lt("age", 20);
wrapper.func(new Consumer<QueryWrapper<User>>() {
@Override
public void accept(QueryWrapper<User> userQueryWrapper) {
userQueryWrapper.like("name", "a");
userQueryWrapper.eq("sex", "0");
}
});
// 等价于:
// wrapper.lt("age", 20).like("name", "a").eq("sex", "0");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

nested方法

功能等价于and方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
public void test11() {
QueryWrapper<User> wrapper = Wrappers.query();

// 生成的SQL语句条件为: (id = ? AND (name LIKE ? OR age > ?))
// nested()等价于and方法()
wrapper.eq("id", 1);
wrapper.nested(new Consumer<QueryWrapper<User>>() {
@Override
public void accept(QueryWrapper<User> userQueryWrapper) {
// 默认情况下是用and来拼接多个条件
userQueryWrapper
.like("name", "a")
.or()
.gt("age", 20);
}
});

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

apply方法

可以使用占位符进行参数传参, apply方法可以防止SQL注入;示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test12() {
QueryWrapper<User> wrapper = Wrappers.query();

// SQL: (name like ? and age > ?)
// wrapper.apply("name like {0} and age > {1}", "%J%", 18);

// SQL: (date_format(birthday, '%Y-%m-%d') = ?)
wrapper.apply("date_format(birthday, '%Y-%m-%d') = {0}", "2001-10-04");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

last方法

无视优化规则直接拼接到 sql 的最后,只能调用一次,多次调用以最后一次为准 有sql注入的风险

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
public void test13() {
QueryWrapper<User> wrapper = Wrappers.query();
// 无视优化规则直接拼接到 sql 的最后,只能调用一次,多次调用以最后一次为准 有sql注入的风险
// SQL: name = 'Jone'
// String name = "Jone";
// wrapper.last("where name = '" + name + "'");

// 出现SQL注入问题
// SQL: SELECT id,name,sex,age FROM user where name ='' or 1=1; -- '
String name = "' or 1=1; -- ";
wrapper.last("where name ='" + name + "'");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

exists方法

1
2
3
4
5
6
7
8
9
10
@Test
public void test14() {
QueryWrapper<User> wrapper = Wrappers.query();

// SQL: (EXISTS (select 1))
wrapper.exists("select 1");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

notExists方法

1
2
3
4
5
6
7
8
9
10
@Test
public void test17() {
QueryWrapper<User> wrapper = Wrappers.query();

// SQL: (NOT EXISTS (select 1))
wrapper.notExists("select 1");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

QueryMapper

QueryMapper是AbstractWrapper的子类,主要用于查询指定字段

1
2
3
4
5
6
7
// 设置查询字段
select(String... sqlSelect)

// 例1:
select("id", "name", "age")
// 例2:
select(i -> i.getProperty().startsWith("test"))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SpringBootTest
public class Demo04_QueryWrapper {
@Autowired
private UserMapper userMapper;

// 选择查询的字段
@Test
public void test1() throws Exception {
QueryWrapper<User> wrapper = Wrappers.query();
// 确定要查询的字段
wrapper.select("id", "name", "sex");
// in
wrapper.in("id", "1", "2");
// SQL: SELECT id,name,sex FROM user WHERE (id IN (?,?))
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
}

UpdateWrapper

UpdateWrapper也是AbstractWrapper的子类,因此UpdateWrapper也具备之前的那些查询方法,不同的是,UpdateMapper在那些方法基础之上还提供了很多有关于更新操作的方法;

1
2
3
4
5
6
7
8
9
10
11
12
// 设置查询字段
set(String column, Object val)

// 设置set子句的部分SQL
setSql(String sql)

1
set("name", "老李头")
set("name", "")
set("name", null) // 数据库字段值变为null
setSql("name = '老李头'")
setSql("name = '老李头',age=20 where id=1")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@SpringBootTest
public class Demo05_UpdateWrapper {
@Autowired
private UserMapper userMapper;

@Test
public void test1() throws Exception {
UpdateWrapper<User> wrapper = Wrappers.update();

// UpdateWrapper也是AbstractWrapper的子类,因此也具备一些基本的查询方法
wrapper.like("name", "J");

List<User> userList = userMapper.selectList(wrapper);

for (User user : userList) {
System.out.println(user);
}
}


/**
* 第一种方法: 使用wrapper来修改,并且指定查询条件
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
UpdateWrapper<User> wrapper = Wrappers.update();
wrapper.set("name", "Jackson");
wrapper.set("age", "16");
wrapper.set("sex", "1");
wrapper.eq("id", 2L);

// SQL: UPDATE user SET name=?, sex=?, age=? WHERE (id = ?)
userMapper.update(null, wrapper);
}


/**
* 第二种方法: 使用wrapper来封装条件,使用entity来封装修改的数据
*
* @throws Exception
*/
@Test
public void test3() throws Exception {
UpdateWrapper<User> wrapper = Wrappers.update();
wrapper.eq("id", 2L);

User user = new User(null, "Jack", "0", 28, "2020-01-01");

// SQL: UPDATE user SET name=?, sex=?, age=? WHERE (id = ?)
userMapper.update(user, wrapper);

}

/**
* 第三种方法: Wrappers.update(user)传递查询的条件,使用wrapper来修改
*
* @throws Exception
*/
@Test
public void test4() throws Exception {
User user = new User();
user.setId(1L);

// user当做查询条件
UpdateWrapper<User> wrapper = Wrappers.update(user);
wrapper.set("name", "xiaohui");
wrapper.set("sex", "0");
wrapper.set("age", "22");

// SQL : UPDATE user SET name=?,sex=?,age=? WHERE id=?
userMapper.update(null, wrapper);
}

/**
* setSql方法
*
* @throws Exception
*/
@Test
public void test5() throws Exception {
UpdateWrapper<User> wrapper = Wrappers.update();
wrapper.setSql("name='abc',sex='0',age=18 where id=1");

// SQL: UPDATE user SET name='abc',sex='0',age=18 where id=1
userMapper.update(null, wrapper);
}
}

LambdaQueryWrapper

LambdaQueryWrapper是QueryWrapper的子类,具备QueryWrapper的所有方法,QueryWrapper的方法上提供了一系列有关于方法引的操作;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@SpringBootTest
public class Demo06_LambdaQueryMapper {
@Autowired
private UserMapper userMapper;

/**
* 使用QueryWrapper
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
QueryWrapper<User> wrapper = Wrappers.query();
wrapper.eq("id", "1");
List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user);
}
}

/**
* 使用LambdaQueryWrapper
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
// id=1
// wrapper.eq(User::getId, 1);

// select id,name,age from user where id in (1,2,3) and name like "%a%"
wrapper.in(User::getId, "1", "2", "3")
.like(User::getName, "a")
.select(User::getId, User::getName, User::getAge);

List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user);
}
}
}

LambdaUpdateMapper

LambdaUpdateMapper同样是UpdateMapper的子类,具备UpdateMapper的所有方法,UpdateMapper的方法上提供了一系列有关于方法引的操作;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@SpringBootTest
public class Demo07_LambdaUpdateWrapper {

@Autowired
private UserMapper userMapper;

/**
* 使用UpdateWrapper
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
UpdateWrapper<User> wrapper = Wrappers.update();
wrapper.eq("id", "1");
wrapper.set("name", "xiaohui");
wrapper.set("age", 20);

userMapper.update(null, wrapper);
}

/**
* 使用LambdaUpdateWrapper
*
* @throws Exception
*/
@Test
public void test2() throws Exception {

LambdaUpdateWrapper<User> wrapper = Wrappers.lambdaUpdate();
wrapper.eq(User::getId, "1");
wrapper.set(User::getName, "xiaolan");
wrapper.set(User::getAge, 18);

userMapper.update(null, wrapper);
}
}

Mapper的分页查询

Mapper分页查询配置

在MyBatis中提供有Page对象来帮助我们实现分页查询,在Page对象中有如下成员:

成员变量 说明
List getRecords() 当前页数据
public long getTotal() 总记录数
public long getSize() 页大小
public long getCurrent() 当前页
default long getPages() 总页数
public boolean hasNext() 是否有上一页
public boolean hasPrevious() 是否有上一页

MyBatisPlus的分页逻辑底层是通过分页插件 PaginationInnerInterceptor 来完成的,因此我们首先要配置MyBatisPlus的分页插件;

PaginationInnerInterceptor 提供了以下属性来定制分页行为,建议单一数据库类型的均设置 dbType:

属性名 类型 默认值 描述
overflow boolean false 溢出总页数后是否进行处理
maxLimit Long 单页分页条数限制
dbType DbType 数据库类型
dialect IDialect 方言实现类

配置分页插件

在 Spring Boot 项目中,你可以通过 Java 配置来添加分页插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package pers.fulsun.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("pers.fulsun.mapper")
public class MyBatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(
new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
}

分页查询方法

在BaseMapper中主要提供有如下方法来完成分页查询:

  • <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)

    • 参数1:分页配置类
    • 参数2:分页查询条件
    • 解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为指定类型
  • <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)

    • 参数1:分页配置类
    • 参数2:分页查询条件
    • 解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为Map类型

无条件分页查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@SpringBootTest
public class Demo06_BaseMapper的分页查询 {

@Autowired
private UserMapper userMapper;

/**
* 无条件分页查询
*
* @throws Exception
*/
@Test
public void test1() throws Exception {

// 封装分页信息
Page<User> page = new Page<>(1, 3);

/*
执行分页查询,并将结果封装到page中
参数1: 分页配置
参数2: 查询条件
*/
userMapper.selectPage(page, null);

// 当前页数据
List<User> pageData = page.getRecords();
for (User user : pageData) {
System.out.println(user);
}

System.out.println("------------");

System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
}

带条件分页查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
public void test2() throws Exception {

QueryWrapper<User> wrapper = Wrappers.query();
wrapper.like("name", "a");

// 封装分页信息
Page<User> page = new Page<>(1, 3);

/*
执行分页查询,并将结果封装到page中
参数1: 分页配置
参数2: 查询条件
*/
userMapper.selectPage(page, wrapper);

// 当前页数据
List<User> pageData = page.getRecords();
for (User user : pageData) {
System.out.println(user);
}

System.out.println("------------");

System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}

查询结果以Map类型返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void test3() throws Exception {
// 封装分页信息
Page page = new Page<>(1, 3);

userMapper.selectMapsPage(page, null);

// 每一条记录都是一个HashMap
List<HashMap<String, Object>> pageData = page.getRecords();
for (HashMap userMap : pageData) {
System.out.println(userMap);
}

System.out.println("------------");

System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}

MyBatis Plus的Service查询

通用Service简介

通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行、remove删除、list 查询集合、page查询分页

通用service常用方法

  • 新增:

    • default boolean save(T entity):新增记录
    • boolean saveBatch(Collection<T> entityList):批量插入
    • saveBatch(Collection<T> entityList, int batchSize):一次性批量插入batchSize条记录
  • 删除:

    • boolean removeById(Serializable id):根据id删除
    • boolean removeByMap(Map<String, Object> columnMap):根据条件删除
    • boolean remove(Wrapper<T> queryWrapper):使用Wrapper封装条件删除
    • boolean removeByIds(Collection<? extends Serializable> idList):删除一批
  • 修改:

    • boolean updateById(T entity):修改
    • boolean update(Wrapper<T> updateWrapper):根据Wrapper修改
    • boolean update(T entity, Wrapper<T> updateWrapper):使用Wrapper查询出结果,修改为entity
    • boolean updateBatchById(Collection<T> entityList):批量修改
    • updateBatchById(Collection<T> entityList, int batchSize):一次性批量修改batchSize条记录
    • boolean saveOrUpdate(T entity):如果id存在则修改,如果id不存在则新增
  • 查询:

    • T getById(Serializable id):根据id查询
    • List<T> listByIds(Collection<? extends Serializable> idList):根据一批id查询多条记录
    • List<T> listByMap(Map<String, Object> columnMap):根据条件查询多条记录
    • T getOne(Wrapper<T> queryWrapper):根据Wrapper查询一条记录,如果查询到多条则抛出异常
    • T getOne(Wrapper<T> queryWrapper, boolean throwEx):根据Wrapper查询一条记录,通过throwEx决定是否抛出异常
    • int count():查询总记录数
    • int count(Wrapper<T> queryWrapper):根据条件查询总记录数
  • 分页:

    • <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper):带条件分页查询,当前页数据为T类型
    • <E extends IPage<T>> E page(E page):无条件分页
    • List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper):带条件分页查询,当前页数据为HashMap类型
    • List<Map<String, Object>> listMaps():无条件分页

使用步骤

  • 定义一个UserService接口继承与MyBatisPlus提供的IService接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package pers.fulsun.service.impl;

    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.stereotype.Service;
    import pers.fulsun.entity.User;
    import pers.fulsun.mapper.UserMapper;
    import pers.fulsun.service.IUserService;

    @Service("userService")
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    }
  • 定义一个UserService的实现类,并且继承与MyBatisPlus提供的ServiceImpl:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    package pers.fulsun.service.impl;

    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import pers.fulsun.entity.User;
    import pers.fulsun.mapper.UserMapper;
    import pers.fulsun.service.IUserService;

    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    }
  • 测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    package pers.fulsun.mapper;

    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
    import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import pers.fulsun.entity.User;
    import pers.fulsun.service.IUserService;

    import java.util.HashMap;
    import java.util.List;

    @SpringBootTest
    public class Demo07_Service查询 {
    @Autowired
    private IUserService userService;

    /**
    * 新增
    *
    * @throws Exception
    */
    @Test
    public void test1() throws Exception {
    User user = new User(null, "xiaohui", "0", 20, "2015-01-01");
    userService.save(user);
    }

    /**
    * 如果id存在则修改,不存在则新增
    *
    * @throws Exception
    */
    @Test
    public void test2() throws Exception {
    User user = new User(1L, "xiaohui", "0", 20, "2015-01-01");
    userService.saveOrUpdate(user);
    }

    /**
    * 根据id删除
    *
    * @throws Exception
    */
    @Test
    public void test3() throws Exception {
    userService.removeById(1L);
    }

    /**
    * 根据id修改
    *
    * @throws Exception
    */
    @Test
    public void test4() throws Exception {

    User user = new User(1L, "xiaolan", "1", 18, "2015-01-01");
    userService.updateById(user);
    }

    /**
    * 根据id查询
    *
    * @throws Exception
    */
    @Test
    public void test5() throws Exception {
    User user = userService.getById(1L);
    System.out.println(user);
    }

    /**
    * 查询列表
    *
    * @throws Exception
    */
    @Test
    public void test6() throws Exception {

    QueryWrapper<User> wrapper = Wrappers.query();
    wrapper.in("id", "1", "2");

    // 查询所有
    // List<User> userList = userService.list();

    // 通过wrapper查询
    List<User> userList = userService.list(wrapper);

    for (User user : userList) {
    System.out.println(user);
    }
    }


    /**
    * 查询总记录数
    *
    * @throws Exception
    */
    @Test
    public void test7() throws Exception {

    QueryWrapper<User> wrapper = Wrappers.query();
    wrapper.like("name", "a");

    // 查询总记录数
    // int count = userService.count();

    // 根据条件查询总记录数
    Long count = userService.count(wrapper);

    System.out.println(count);
    }

    /**
    * 分页查询(当前页类型为指定类型)
    *
    * @throws Exception
    */
    @Test
    public void test8() throws Exception {

    Page<User> page = new Page<>(1, 3);

    userService.page(page);

    // 当前页数据
    List<User> pageData = page.getRecords();
    for (User user : pageData) {
    System.out.println(user);
    }

    System.out.println("------------");

    System.out.println("当前页:" + page.getCurrent());
    System.out.println("每页显示的条数:" + page.getSize());
    System.out.println("总记录数:" + page.getTotal());
    System.out.println("总页数:" + page.getPages());
    System.out.println("是否有上一页:" + page.hasPrevious());
    System.out.println("是否有下一页:" + page.hasNext());
    }


    /**
    * 分页查询(当前页结果为HashMap类型)
    *
    * @throws Exception
    */
    @Test
    public void test9() throws Exception {

    Page page = new Page<>(1, 3);

    userService.pageMaps(page);

    // 当前页数据
    List<HashMap<String, Object>> pageData = page.getRecords();
    for (HashMap userMap : pageData) {
    System.out.println(userMap);
    }

    System.out.println("------------");

    System.out.println("当前页:" + page.getCurrent());
    System.out.println("每页显示的条数:" + page.getSize());
    System.out.println("总记录数:" + page.getTotal());
    System.out.println("总页数:" + page.getPages());
    System.out.println("是否有上一页:" + page.hasPrevious());
    System.out.println("是否有下一页:" + page.hasNext());
    }


    /**
    * 链式查询
    *
    * @throws Exception
    */
    @Test
    public void test10() throws Exception {

    QueryChainWrapper<User> chainWrapper = userService.query();

    // SQL: SELECT id,name,age FROM user WHERE (id IN (?,?,?) AND name LIKE ?)
    List<User> userList = chainWrapper.select("id", "name", "age")
    .in("id", "1", "2", "3")
    .like("name", "a")
    .list();

    for (User user : userList) {
    System.out.println(user);
    }
    }


    /**
    * 链式修改
    *
    * @throws Exception
    */
    @Test
    public void test11() throws Exception {

    UpdateChainWrapper<User> chainWrapper = userService.update();

    // SQL: UPDATE user SET age=? WHERE (id IN (?,?) OR sex = ?)
    chainWrapper.in("id", "1", "2")
    .or()
    .eq("sex", "0")
    .set("age", 20).
    update();
    }
    }

MyBatis Plus绑定Mapper.xml

MyBatisPlus本质上也是MyBatis,因此也支持我们自定义SQL语句,编写Mapper.xml与Mapper接口进行绑定;

  1. 在application.yml中扫描Mapper.xml:

    1
    2
    3
    4
    5
    6
    #配置日志
    mybatis-plus:
    # 扫描mapper.xml文件
    mapper-locations: classpath:pers/fulsun/mapper/*.xml
    # 配置包别名
    type-aliases-package: pers.fulsun.entity
  2. 在Mapper接口中扩展方法:

    1
    2
    3
    4
    @Repository
    public interface UserMapper extends BaseMapper<User> {
    List<User> findAll();
    }
  3. 编写UserMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?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">
    <!--namespace 名称空间,指定对哪个接口进行映射-->
    <mapper namespace="pers.fulsun.mapper.UserMapper">
    <select id="findAll" resultType="user">
    select *
    from user
    </select>
    </mapper>
  4. 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @SpringBootTest
    class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testfindAll() {
    List<User> userList = userMapper.findAll();
    userList.forEach(System.out::println);
    }
    }

ActiveRecord的使用

ActiveRecord简介

ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂

主要思想

  1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录,通常表的每个字段在类中都有相应的Field;
  2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;
  3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑。

简而言之,AR建立了Java对象与数据库表逻辑上的直接映射,方便了程序的编写。而在MyBatisPlus中使用AR非常简单,只需让JavaBean继承Model类即可。

Model类的使用

在MyBatisPlus中,只要将我们的JavaBean继承与Model类即可获取AR功能,即JavaBean自身实现自身的CURD;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package pers.fulsun.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User extends Model<User> { // 继承Model
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String sex;
private Integer age;
private String birthday;
}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package pers.fulsun.mapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import pers.fulsun.entity.User;

import java.util.List;

@SpringBootTest
public class Demo12_ActiveRecord {

/**
* 新增
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
User user = new User(100L, "xiaoming", "0", 20, "2016-01-01");
user.insert();
}

/**
* 删除
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
User user = new User();
user.setId(100L);
user.deleteById();
}

/**
* 修改
*
* @throws Exception
*/
@Test
public void test3() throws Exception {
User user = new User(1L, "xiaohui", "1", 22, "2016-01-21");
user.updateById();
}

/**
* 根据id查询
*
* @throws Exception
*/
@Test
public void test4() throws Exception {
User user = new User();
user.setId(100L);
User dbUser = user.selectById();
System.out.println(dbUser);
}


/**
* 查询全部
*
* @throws Exception
*/
@Test
public void test5() throws Exception {
User user = new User();
List<User> userList = user.selectAll();
for (User dbUser : userList) {
System.out.println(dbUser);
}
}

/**
* 根据Wrapper条件查询
*
* @throws Exception
*/
@Test
public void test6() throws Exception {

QueryWrapper<User> wrapper = Wrappers.query();
wrapper.like("name", "a");

User user = new User();

// 根据条件查询
List<User> userList = user.selectList(wrapper);
for (User dbUser : userList) {
System.out.println(dbUser);
}
}

/**
* 分页查询
*
* @throws Exception
*/
@Test
public void test7() throws Exception {

QueryWrapper<User> wrapper = Wrappers.query();
wrapper.like("name", "a");

Page<User> page = new Page<>(1, 3);
User user = new User();

// 分页查询
user.selectPage(page, wrapper);

List<User> records = page.getRecords();
for (User record : records) {
System.out.println(record);
}
}
}

多数据源配置

引入多数据源依赖

1
2
3
4
5
6
<!--MyBatisPlus多数据源依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>

准备两个数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
DROP database IF EXISTS test1;
create database test1;

use test1;

DROP TABLE IF EXISTS user;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`sex` char(1) DEFAULT NULL COMMENT '性别 0:男 1:女',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`birthday` date DEFAULT NULL COMMENT '生日',
PRIMARY KEY (`id`)
);

INSERT INTO `user` VALUES (1, 'Jone', '1', 27, '2001-10-04');
INSERT INTO `user` VALUES (2, 'Jack', '0', 20, '1999-10-04');
INSERT INTO `user` VALUES (3, 'Tom', '1', 28, '1996-08-12');

-- -----------------------------------------------

DROP database IF EXISTS test2;
create database test2;

use test2;

DROP TABLE IF EXISTS user;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`sex` char(1) DEFAULT NULL COMMENT '性别 0:男 1:女',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`birthday` date DEFAULT NULL COMMENT '生日',
PRIMARY KEY (`id`)
);

INSERT INTO `user` VALUES (4, 'Sandy', '1', 21, '2001-10-04');
INSERT INTO `user` VALUES (5, 'Billie', '0', 24, '1992-09-07');
INSERT INTO `user` VALUES (6, 'Jackson', '0', 18, '1996-08-12');

多数据源配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
spring:
datasource:
dynamic:
# 没有匹配的数据源时默认匹配的数据源
primary: master
# 当没有匹配的数据源是是否采用默认的数据源,
# true: 严格匹配,如果没有匹配到数据源则抛出异常
# false(默认值): 如果没有匹配到数据源则使用默认的数据源
strict: false
datasource:
# 数据源的名称(名字任意)
master:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?serverTimezone=GMT%2b8
username: root
password: admin
# 数据源的名称(名字任意)
slave:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2b8
username: root
password: admin
#配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

编写两个Mapper

1
2
3
4
5
6
7
8
9
10
11
package pers.fulsun.mapper;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import pers.fulsun.entity.User;
import org.springframework.stereotype.Repository;

@DS("master") // 指定数据源的名称(若指定的数据源不存在则使用默认的)
@Repository
public interface UserMapperMaster extends BaseMapper<User> {
}
1
2
3
4
5
6
7
8
9
10
11
12
package pers.fulsun.mapper;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import pers.fulsun.entity.User;
import org.springframework.stereotype.Repository;


@DS("slave") // 指定数据源的名称(若指定的数据源不存在则使用默认的)
@Repository
public interface UserMapperSlave extends BaseMapper<User> {
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package pers.fulsun;

import pers.fulsun.entity.User;
import pers.fulsun.mapper.UserMapperMaster;
import pers.fulsun.mapper.UserMapperSlave;
import org.junit.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;

@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class Demo01_测试多数据源 {

@Autowired
private UserMapperMaster userMapperMaster;

@Autowired
private UserMapperSlave userMapperSlave;


@Test
public void test1() {
// 使用的是master数据源
List<User> userList = userMapperMaster.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
@Test
public void test2() {
// 使用slave数据源
List<User> userList = userMapperSlave.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
}