MybatisPlus概述 MyBatis Plus官网: https://baomidou.com/
MyBatis-Plus (简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatis Plus 支持的数据库
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 > <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 > <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 > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > <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; @Test public void insert () { User user = new User (100L , "Ken" , "0" ,20 ,"2012-01-01 23:59:59" ); userMapper.insert(user); } @Test public void update () { User user = new User (100L , "Ken" , "0" ,20 ,"2012-01-01 23:59:59" ); userMapper.updateById(user); } @Test public void selectById () { User user = userMapper.selectById(100L ); System.out.println(user); } @Test public void selectBatchIds () { List<User> userList = userMapper.selectBatchIds(Arrays.asList(1 , 2 , 3 )); for (User user : userList) { System.out.println(user); } } @Test public void selectByMap () { HashMap<String, Object> param = new HashMap <>(); param.put("id" ,10L ); param.put("name" ,"Kevin" ); List<User> userList = userMapper.selectByMap(param); for (User user : userList) { System.out.println(user); } } @Test public void deleteById () { userMapper.deleteById(100L ); } @Test public void deleteBatchIds () { userMapper.deleteBatchIds(Arrays.asList(1 , 2 , 3 )); } @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是条件构造抽象类,最顶端父类,其主要实现类有如下:
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 public static <T> QueryWrapper<T> query () 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; @Test public void test1 () { 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(); wrapper.eq("name" , "Jack" ); String name = "Jack" ; wrapper.eq(name != null , "name" , name); wrapper.ne("name" , "Jack" ); wrapper.gt("age" , 20 ); wrapper.lt("age" , 20 ); wrapper.lt("age" , 20 ); wrapper.between("age" , 20 , 24 ); wrapper.notBetween("age" , 20 , 24 ); wrapper.like("name" , "J" ); wrapper.notLike("name" , "J" ); wrapper.likeLeft("name" , "J" ); wrapper.likeRight("name" , "J" ); wrapper.isNull("name" ); wrapper.isNotNull("name" ); wrapper.in("name" , "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 () { QueryWrapper<User> wrapper = Wrappers.query(); wrapper.inSql("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 () { QueryWrapper<User> wrapper = Wrappers.query(); 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 () { QueryWrapper<User> wrapper = Wrappers.query(); wrapper.orderBy(true , true , "age" ); 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 () { QueryWrapper<User> wrapper = Wrappers.query(); 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 () { QueryWrapper<User> wrapper = Wrappers.query(); wrapper.like("name" , "%a%" ) .or() .lt("age" , 20 ) .eq("sex" , 0 ); 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(); 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(); 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" ); } }); 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(); wrapper.eq("id" , 1 ); wrapper.nested(new Consumer <QueryWrapper<User>>() { @Override public void accept (QueryWrapper<User> userQueryWrapper) { 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(); 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(); 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(); 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(); 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) select("id" , "name" , "age" ) 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" ); wrapper.in("id" , "1" , "2" ); 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) setSql(String sql) 例1 : set("name" , "老李头" ) set("name" , "" ) set("name" , 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(); wrapper.like("name" , "J" ); List<User> userList = userMapper.selectList(wrapper); for (User user : userList) { System.out.println(user); } } @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 ); userMapper.update(null , wrapper); } @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" ); userMapper.update(user, wrapper); } @Test public void test4 () throws Exception { User user = new User (); user.setId(1L ); UpdateWrapper<User> wrapper = Wrappers.update(user); wrapper.set("name" , "xiaohui" ); wrapper.set("sex" , "0" ); wrapper.set("age" , "22" ); userMapper.update(null , wrapper); } @Test public void test5 () throws Exception { UpdateWrapper<User> wrapper = Wrappers.update(); wrapper.setSql("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; @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); } } @Test public void test2 () throws Exception { LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(); 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; @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); } @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)); return interceptor; } }
分页查询方法 在BaseMapper中主要提供有如下方法来完成分页查询:
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)
:
解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为指定类型
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)
解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为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; @Test public void test1 () throws Exception { Page<User> page = new Page <>(1 , 3 ); 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 ); 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 ); 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(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()
:无条件分页
使用步骤
MyBatis Plus绑定Mapper.xml MyBatisPlus本质上也是MyBatis,因此也支持我们自定义SQL语句,编写Mapper.xml与Mapper接口进行绑定;
在application.yml中扫描Mapper.xml:
1 2 3 4 5 6 mybatis-plus: mapper-locations: classpath:pers/fulsun/mapper/*.xml type-aliases-package: pers.fulsun.entity
在Mapper接口中扩展方法:
1 2 3 4 @Repository public interface UserMapper extends BaseMapper <User> { List<User> findAll () ; }
编写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" > <mapper namespace ="pers.fulsun.mapper.UserMapper" > <select id ="findAll" resultType ="user" > select * from user </select > </mapper >
测试
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模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂
主要思想
每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录,通常表的每个字段在类中都有相应的Field;
ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;
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> { @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 { @Test public void test1 () throws Exception { User user = new User (100L , "xiaoming" , "0" , 20 , "2016-01-01" ); user.insert(); } @Test public void test2 () throws Exception { User user = new User (); user.setId(100L ); user.deleteById(); } @Test public void test3 () throws Exception { User user = new User (1L , "xiaohui" , "1" , 22 , "2016-01-21" ); user.updateById(); } @Test public void test4 () throws Exception { User user = new User (); user.setId(100L ); User dbUser = user.selectById(); System.out.println(dbUser); } @Test public void test5 () throws Exception { User user = new User (); List<User> userList = user.selectAll(); for (User dbUser : userList) { System.out.println(dbUser); } } @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); } } @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 <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 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 () { List<User> userList = userMapperMaster.selectList(null ); for (User user : userList) { System.out.println(user); } } @Test public void test2 () { List<User> userList = userMapperSlave.selectList(null ); for (User user : userList) { System.out.println(user); } } }