MybatisPlus:一款 Mybatis 的增强工具包
MybatisPlus 官网:https://mybatis.plus/ 或 https://mp.baomidou.com/
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景:我们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的 1P、2P,基友搭配,效率翻倍。
MybatisPlus 的特性
MybatisPlus 文档地址
0、参考资料
MybatisPlus 快速开始
1、创建数据库表
现有一张 User
表,其表结构如下
id | name | age | |
---|---|---|---|
1 | Jone | 18 | [email protected] |
2 | Jack | 20 | [email protected] |
3 | Tom | 28 | [email protected] |
4 | Sandy | 21 | [email protected] |
5 | Billie | 24 | [email protected] |
其对应的数据库 Schema 脚本和 Data 脚本如下:
-- 创建数据库
CREATE DATABASE mybatis_plus;
-- 使用数据库
USE mybatis_plus;
-- 创建数据库表
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, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
Question:如果从零开始用 MyBatis-Plus 来实现该表的增删改查我们需要做什么呢?
2、引入依赖
引入 Spring Boot Starter 作为当前工程的父工程:
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.2.RELEASEversion>
parent>
引入 spring-boot-starter
、spring-boot-starter-test
、mybatis-plus-boot-starter
、mysql
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
dependencies>
3、创建实体类以及 Mapper 映射接口
创建 User
实体类:实体类的字段最好使用包装类型,而不要使用基本类型。因为每个基本类型都有一个默认值,在查询的时候,就不好区分是数据库记录的值,还是字段的默认值。但是包装类型没有,如果没有为包装类型设置值,那么其值为 null
/**
* @Author Oneby
* @Date 2021/4/18 17:53
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
创建 UserMapper
映射接口,该接口继承了 BaseMapper
接口
/**
* @Author Oneby
* @Date 2021/4/18 17:53
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
}
4、创建主启动类
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描 Mapper 接口所在的包
/**
* @Author Oneby
* @Date 2021/4/18 17:51
*/
@SpringBootApplication
@MapperScan("com.oneby.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
5、编写配置文件
在 application.yml 配置文件中配置数据源信息。注意:低版本 MySQL 驱动为 com.mysql.jdbc.Driver
,无需配置时区;高版本 MySQL 驱动为 com.mysql.cj.jdbc.Driver
,需要增加时区的配置
# 数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: root
6、代码测试
编写测试代码
@RunWith(SpringRunner.class)
:这是 junit 提供的注解,该注解让测试运行于Spring测试环境,并让 SpringRunner
这个类提供 Spring 测试上下文@SpringBootTest
:SpringBoot 测试类的注解,该注解会启动 SpringBoot 应用,并在测试中创建 ApplicationContext
对象UserMapper
中的 selectList()
方法的参数为 MP 内置的条件封装器 Wrapper
,所以不填写就是无任何条件/**
* @Author Oneby
* @Date 2021/4/18 17:54
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class MybatisPlusEnvironmentTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
程序运行结果:通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!
User(id=1, name=Jone, age=18, [email protected])
User(id=2, name=Jack, age=20, [email protected])
User(id=3, name=Tom, age=28, [email protected])
User(id=4, name=Sandy, age=21, [email protected])
User(id=5, name=Billie, age=24, [email protected])
查看 SQL 日志
为了能够观察调用每个方法时,MybatisPlus 到底为我们做了什么?执行了什么样的 SQL 语句?我们就需要开启 SQL 日志
# SQL 日志配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
开启日志之后,再次运行测试代码,可以看到调用 Mapper 方法时执行的 SQL 语句
DBC Connection [HikariProxyConnection@81505591 wrapping com.mysql.cj.jdbc.ConnectionImpl@7b4acdc2] will not be managed by Spring
==> Preparing: SELECT id,name,age,email FROM user
==> Parameters:
<== Columns: id, name, age, email
<== Row: 1, Jone, 18, [email protected]
<== Row: 2, Jack, 20, [email protected]
<== Row: 3, Tom, 28, [email protected]
<== Row: 4, Sandy, 21, [email protected]
<== Row: 5, Billie, 24, [email protected]
<== Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@52a70627]
User(id=1, name=Jone, age=18, [email protected])
User(id=2, name=Jack, age=20, [email protected])
User(id=3, name=Tom, age=28, [email protected])
User(id=4, name=Sandy, age=21, [email protected])
User(id=5, name=Billie, age=24, [email protected])
我们都没有编写过CRUD 的接口以及实现,怎么就能执行基本的增删改查了呢?
我们自己编写的 UserMapper
类继承自 BaseMapper
接口,该接口中提供了基本的增删改查方法,因此我们自己编写的 Mapper 接口只需要继承 BaseMapper
父接口,就能获得基本的增删改查功能
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* 这个 Mapper 支持 id 泛型
*
* @author hubin
* @since 2016-01-23
*/
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
诶,不对啊,BaseMapper
中之定义了接口,没有定义 SQL 语句呀。MybatisPlus 注入 SQL 的原理在后面会讲解,这里简单提一下。
我们以 Debug 的方式调试程序,找到 userMapper
中的 sqlSession
--> sqlSessionFactory
--> configuration
--> mappedStatements
,mappedStatements
的类型为 StrictMap
,但其本质是 HashMap
,key
为 String
类型,存储 Mapper
接口的方法名;value
是 MappedStatement
类型,存储接口方法对应的 SQL 信息。
在 MappedStatement
中可以找到 com.oneby.mapper.UserMapper.selectById
方法对应的 SQL 语句为 SELECT id,name,age,email FROM user WHERE id=?
,也就是 MybatisPlus 在加载 Mapper 接口时就已经帮我们创建好了其对应的 SQL 语句(这仅仅针对于能提前确定下来的简单 SQL 语句)
对于 com.oneby.mapper.UserMapper.selectList
方法,其 SQL 语句不能提前确定下来,在 sqlSource
下就没有 sql
字段,取而代之的时 rootSqlNode
字段,该字段会根据条件构造器来动态拼接 SQL 语句