简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
快速开始
通过以下几个简单的步骤,我们就可以实现 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!
- 创建数据表
- 添加依赖
- 配置数据源(DataSource)
- 在启动类添加 @MapperScan 注解
- 编写实体类(类名一定要与表名一致)
- 编写接口继承 BaseMapper 类
- 编写测试类进行测试
创建数据表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| DROP TABLE if EXISTS user;
CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) );
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');
|
添加依赖
1 2 3 4 5 6 7 8 9
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
|
配置数据源
1 2 3 4 5 6
| spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/daily username: daily password: daily
|
在启动类中添加 MapperScan 注解,扫描 mapper 文件夹
1 2 3 4 5 6 7 8 9
| @SpringBootApplication @MapperScan("com.pushihao.mapper") public class Springboot06MybatisplusApplication {
public static void main(String[] args) { SpringApplication.run(Springboot06MybatisplusApplication.class, args); }
}
|
编写实体类(此处使用了lombok)
注意:数据库的表名一定要和实体类的类名保持一致
1 2 3 4 5 6 7 8 9
| @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
|
编写 mapper 接口继承 BaseMapper 类
提示:此处即使不写 @Repository 注解也能正确运行,但在编写代码时 IDE 会报错
1 2 3
| @Repository public interface UserMapper extends BaseMapper<User> { }
|
编写测试类测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @SpringBootTest class Springboot06MybatisplusApplicationTests {
@Autowired private UserMapper userMapper;
@Test public void testSelect() { System.out.println("-----selectAll method test-----"); List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); }
}
|
常用注解
@TableName
用在实体类上,标识实体类对应的表
1 2 3 4 5 6 7
| @TableName("user") public class User { private Long id; private String name; private Integer age; private String email; }
|
@Tabled
用在实体类的某个属性上,表名该属性为实体类的主键字段
1 2 3 4 5 6 7 8
| @TableName("user") public class User { @TableId private Long id; private String name; private Integer age; private String email; }
|
常用 CRUD 接口
接口中很大一部分用到了条件构造器,在第五部分
Mapper CRUD 接口
说明:此接口为 dao/mapper 层的接口,创建自己的接口基层 Mybatis-Plus 提供的 BaseMapper 接口即可
实现
UserMapper.java
1 2 3
| @Repository("userMapper") public interface UserMapper extends BaseMapper<User> { }
|
测试使用
1 2 3 4 5 6 7 8 9 10 11
| @Autowired @Qualifier("userMapper")
private UserMapper userMapper;
@Test public void testSelect() { System.out.println("-----selectAll method test-----"); List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); }
|
常用 API
Insert
Delete
1 2 3 4 5 6 7 8
| int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
int deleteById(Serializable id);
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
|
Update
1 2 3 4
| int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
int updateById(@Param(Constants.ENTITY) T entity);
|
Select
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| T selectById(Serializable id);
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
|
四种操作:增删改查
Service CRUD 接口
说明:此接口为 Service
层的接口,创建自己的接口继承 Mybatis-Plus 提供的 IService 接口,并需要实现类
实现
UserService.java
1 2 3 4 5 6 7
| package com.pushihao.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.pushihao.pojo.User;
public interface UserService extends IService<User> { }
|
UserServiceImpl.java
1 2 3 4 5 6 7 8 9 10
| package com.pushihao.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.pushihao.mapper.UserMapper; import com.pushihao.pojo.User; import org.springframework.stereotype.Service;
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
|
测试使用
1 2 3 4 5 6 7 8 9 10
| @Autowired
private UserService userService;
@Test public void testIServiceMethod() { System.out.println("-----IService selectAll method test-----"); List<User> userList = userService.list(); userList.forEach(System.out::println); }
|
常用 API
Save
1 2 3 4 5 6
| boolean save(T entity);
boolean saveBatch(Collection<T> entityList);
boolean saveBatch(Collection<T> entityList, int batchSize);
|
其中,batchSize 指插入批次数量,也就是一次插入多少条数据
演示
插入数据为2条,saveBatch为1
1 2 3 4
| User user2 = new User(7L, "张三", 26, "zhangsan@qq.com"); User user3 = new User(8L, "李四", 27, "lisi@qq.com"); List<User> users = Arrays.asList(user2, user3); userService.saveBatch(users, 1);
|
1 2 3 4
| ==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 7(Long), 张三(String), 26(Integer), zhangsan@qq.com(String) ==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 8(Long), 李四(String), 27(Integer), lisi@qq.com(String)
|
插入数据为2条,saveBatch为2
1 2 3
| ==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 7(Long), 张三(String), 26(Integer), zhangsan@qq.com(String) ==> Parameters: 8(Long), 李四(String), 27(Integer), lisi@qq.com(String)
|
SaveOrUpdate
1 2 3 4 5 6 7 8
| boolean saveOrUpdate(T entity);
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
boolean saveOrUpdateBatch(Collection<T> entityList);
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
|
存在就是修改,不存在就是插入
Remove
1 2 3 4 5 6 7 8
| boolean remove(Wrapper<T> queryWrapper);
boolean removeById(Serializable id);
boolean removeByMap(Map<String, Object> columnMap);
boolean removeByIds(Collection<?> extends Serializable> idList);
|
MyBatisPlus提供了较多的重载方法:

Update
1 2 3 4 5 6 7 8 9 10
| boolean update(Wrapper<T> updateWrapper);
boolean update(T updateEntity, Wrapper<T> whereWrapper);
boolean updateById(T entity);
boolean updateBatchById(Collection<T> entityList);
boolean updateBatchById(Collection<T> entityList, int batchSize);
|
Get
1 2 3 4 5 6 7 8 9 10
| T getById(Serializable id);
T getOne(Wrapper<T> queryWrapper);
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
Map<String, Object> getMap(Wrapper<T> queryWrapper);
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
|
List
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| List<T> list();
List<T> list(Wrapper<T> queryWrapper);
Collection<T> listByIds(Collection<? extends Serializable> idList);
Collection<T> listByMap(Map<String, Object> columnMap);
List<Map<String, Object>> listMaps();
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
List<Object> listObjs();
<V> List<V> listObjs(Function<? super Object, V> mapper);
List<Object> listObjs(Wrapper<T> queryWrapper);
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
|
Page
1 2 3 4 5 6 7 8
| IPage<T> page(IPage<T> page);
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
IPage<Map<String, Object>> pageMaps(IPage<T> page);
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
|
注意:
Ipage是一个接口,继承关系为:
service 中 page 的创建方式由多种:

page 中可以获得很多属性:

演示使用:
1 2 3 4 5
| @Test public void testIPage() { IPage<User> page = userService.page(new Page<>(1, 2), null); page.getRecords().forEach(System.out::println); }
|
Count
1 2 3 4
| int count();
int count(Wrapper<T> queryWrapper);
|
链式查询
类似于 Stream 流式操作
条件构造器
条件构造器层级关系如下图:

插件使用
所有的插件都实现了此接口:InnerInterceptor
目前已有的功能:
- 自动分页: PaginationInnerInterceptor
- 多租户: TenantLineInnerInterceptor
- 动态表名: DynamicTableNameInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
- sql 性能规范: IllegalSQLInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
配置(以分页插件为例):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Configuration @MapperScan("com.pushihao.mapper") public class MyBatisPlusConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
InnerInterceptor pagePlugin = new PaginationInnerInterceptor(DbType.MYSQL);
interceptor.addInnerInterceptor(pagePlugin); return interceptor; } }
|
分页插件的使用
UserMapper.java
1 2 3 4 5 6 7 8 9 10 11 12
| @Repository("userMapper") public interface UserMapper extends BaseMapper<User> {
Page<User> selectPageByAge(@Param("page")Page<User> page, @Param("age")Integer age);
}
|
UserMapper.xml
1 2 3 4 5 6 7 8
| <mapper namespace="com.pushihao.mapper.UserMapper">
<select id="selectPageByAge" resultType="User"> select * from user where age>#{age} </select>
</mapper>
|
pluginTestController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @RestController @RequestMapping("/plugin") public class pluginTestController { @Autowired private UserMapper userMapper;
@RequestMapping("/page") public List<User> page() { Page<User> userPage = new Page<>(1, 2); userMapper.selectPage(userPage, null); return userPage.getRecords(); }
@RequestMapping("/page2") public List<User> page2() { Page<User> userPage = new Page<>(1, 2); userMapper.selectPageByAge(userPage, 20); return userPage.getRecords(); } }
|
注意:
使用多个功能需要注意顺序关系,建议使用如下顺序
- 多租户,动态表名
- 分页,乐观锁
- sql 性能规范,防止全表更新与删除
总结: 对 sql 进行单次改造的优先放入,不对 sql 进行改造的最后放入