认识MyBatis的好兄弟 MyBatis – Plus
- 2019 年 10 月 4 日
- 筆記
快速搭建
我们先通过一个简单的Demo 来认识一下 MyBatis – Plus 的搭建和用法:
现在数据库中创建一张 User
表,表结构和数据如下
id |
name |
age |
|
---|---|---|---|
1 |
lx |
21 |
|
2 |
cxuan |
20 |
|
3 |
liux |
28 |
|
4 |
xiaona |
21 |
|
5 |
lxuan |
24 |
对应的建表语句和SQL语句如下
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) ); INSERT INTO `user` VALUES (1, 'lx', 21, '[email protected]'); INSERT INTO `user` VALUES (2, 'cxuan', 20, '[email protected]'); INSERT INTO `user` VALUES (3, 'liux', 28, '[email protected]'); INSERT INTO `user` VALUES (4, 'xiaona', 21, '[email protected]'); INSERT INTO `user` VALUES (5, 'lxuan', 24, '[email protected]');
使用 IDEA 新建一个 SpringBoot 项目,构建完成后,在 pom.xml
添加如下依赖:
<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>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> </dependencies>
新建 application.yml
,在yml 文件上添加如下数据库驱动信息
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis username: root password: 123456
在主方法上添加 @MapperScan
注解,扫描mapper 包下面的内容
@SpringBootApplication @MapperScan("com.mybatis.plus.mapper") public class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }
新建一个 User
类,使用 @Data lombok 中的注解,如下
@Data public class User { private long id; private String name; private Integer age; private String email; }
然后新建一个接口,这个接口实现 BaseMapper
接口
public interface UserMapper extends BaseMapper<User> {}
使用单元测试进行测试:
@RunWith(SpringRunner.class) @SpringBootTest public class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test public void contextLoads() { System.out.println("----- selectAll method test -----"); List<User> userList = userMapper.selectList(null); Assert.assertEquals(5,userList.size()); userList.forEach(System.out::println); } }
输出:
User(id=1, name=lx, age=21, [email protected]) User(id=2, name=cxuan, age=20, [email protected]) User(id=3, name=liux, age=28, [email protected]) User(id=4, name=xiaona, age=21, [email protected]) User(id=5, name=lxuan, age=24, [email protected])
通过以上几个简单的步骤,我们就实现了 User 表的 查询 功能,你没有看到一条 SQL 语句和 映射的编写。
从以上步骤中,我们可以看到集成MyBatis-Plus
非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。
特性
- 无侵入: 只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小: 启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作: 内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用: 通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成: 支持多达 4 种主键策略(内含分布式唯一 ID 生成器 – Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式: 支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作: 支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器: 采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件: 基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库: 支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
- 内置性能分析插件: 可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件: 提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
MyBatis 核心功能
Mapper CRUD 接口
“说明:
- 通用 CRUD 封装BaseMapper[1]接口,为
Mybatis-Plus
启动时自动解析实体表关系映射转换为Mybatis
内部对象注入容器 - 泛型
T
为任意实体对象 - 参数
Serializable
为任意类型主键Mybatis-Plus
不推荐使用复合主键约定,每一张表都有自己的唯一id
主键 - 对象
Wrapper
为 条件构造器[2]
看下其基本使用
Insert
insert 只有一种用法,直接插入对象的实体
/** * 插入一条记录 * @param entity 实体对象 * @return 插入成功记录数 */ int insert(T entity);
验证
我们先为 User
实体类添加 @AllArgsConstructor
注解,如果 Lombok 不熟的同学请移步至 这些极简的注解你都清楚吗 这篇文章一探究竟。
下面是测试所用的代码
@Test public void insertUserEntity(){ userMapper.insert(new User(6,"cxuanxuan",25,"[email protected]")); }
“注意 Junit 中测试的 insert 插入方法是没有返回值的,不然会报错,所以返回类型是void 。但其实 insert 方法是有返回值的,返回的是插入成功的记录数。
Delete
delete 有四种用法,分别是根据 id 删除、通过某个表字段删除、通过实体类删除和批量删除,比我们最初只是通过 id 删除功能强大很多。
/** * 根据 ID 删除 * @param id 主键ID * @return 删除成功记录数 */ int deleteById(Serializable id); /** * 根据 columnMap 条件,删除记录 * @param columnMap 表字段 map 对象 * @return 删除成功记录数 */ int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** * 根据 entity 条件,删除记录 * @param wrapper 实体对象封装操作类(可以为 null) * @return 删除成功记录数 */ int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); /** * 删除(根据ID 批量删除) * @param idList 主键ID列表(不能为 null 以及 empty) * @return 删除成功记录数 */ int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
验证
验证示例如下:
// 根据某个特定的 id 进行删除。 @Test public void deleteUserById(){ userMapper.deleteById(6); } // deleteByMap 参数是 Map<String, Object> 类型 // 第一个范型是字段名称,第二个范型是字段对应的值 @Test public void deleteByColumnMap(){ Map<String,Object> columnMap = new HashMap<>(); columnMap.put("id",4); userMapper.deleteByMap(columnMap); } // 使用 QueryWrapper 进行条件筛选 @Test public void deleteEntity(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); // 删除 name 不为 null ,并且 age 大于等于 28的记录 queryWrapper.isNotNull("name").ge("age",28); userMapper.delete(queryWrapper); } // 根据 Collection 集合进行删除,也可以是 Collection 的子类 @Test public void deleteBatch(){ List list = new ArrayList(); // 批量删除 1 和 2 的数据 list.add(1); list.add(2); userMapper.deleteBatchIds(list); }
上面是 Delete 方法的测试例子,其中 **deleteByColumnMap 和 deleteEntity ** 方法如果不传任何值,默认删除表中所有的数据。deleteBatch
如果传递 null 的话,测试会报错,无法通过单元测试。
Update
Update 更新方法只有两种形式,一种是直接根据 id 修改实体属性的值,一种是直接更新实体类
/** * 根据 ID 修改 * @param entity 实体对象 * @return 修改成功记录数 */ int updateById(@Param(Constants.ENTITY) T entity); /** * 根据 whereEntity 条件,更新记录 * @param entity 实体对象 (set 条件值,可为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) * @return 修改成功记录数 */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
验证
// 根据id更新User表 @Test public void updateUserById(){ userMapper.updateById(new User(5,"cxuaner",25,"[email protected]")); } // 更新 User 实体类信息 @Test public void updateUserEntity(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper(); updateWrapper.eq("age",28); userMapper.update(new User(3,"cxuan",22,"[email protected]"),updateWrapper); }
- 第一条 updateUserById 语句是根据 id 进行更新,传入一个实体类的意思是更新 id = 5的这条记录。 传入 null 会报错,如果没有 id = 5的这条记录,测试会成功,但是不会更新任何记录
- 第二条 updateUserEntity 语句是根据实体类的条件进行更新,
UpdateWrapper
是一个条件构造器,测试的是 age 为 28 的这条记录进行更新。 条件构造器后面会进行更加详细的介绍。
Select
Select 用法比较多,现在来一起看一下Select 有哪些用法
/** * 根据 ID 查询 * @param id 主键ID * @return 实体 */ T selectById(Serializable id); /** * 查询(根据ID 批量查询) * @param idList 主键ID列表(不能为 null 以及 empty) * @return 实体集合 */ List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); /** * 查询(根据 columnMap 条件) * @param columnMap 表字段 map 对象 * @return 实体集合 */ List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** * 根据 entity 条件,查询一条记录 * @param queryWrapper 实体对象 * @return 实体 */ T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询总记录数 * @param queryWrapper 实体对象 * @return 满足条件记录数 */ Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 entity 条件,查询全部记录 * @param queryWrapper 实体对象封装操作类(可以为 null) * @return 实体集合 */ List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 * @param queryWrapper 实体对象封装操作类(可以为 null) * @return 字段映射对象 Map 集合 */ List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录 * 注意:只返回第一个字段的值 * @param queryWrapper 实体对象封装操作类(可以为 null) * @return 字段映射对象集合 */ List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 entity 条件,查询全部记录(并翻页) * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) * @return 实体分页对象 */ IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); /** * 根据 Wrapper 条件,查询全部记录(并翻页) * @param page 分页查询条件 * @param queryWrapper 实体对象封装操作类 * @return 字段映射对象 Map 分页对象 */ IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
验证
下面就对上面的示例方法进行验证
/** * 查询id = 1 的一条记录 */ @Test public void selectById(){ User user = userMapper.selectById(1); System.out.println("user = " + user); } /** * 批量查询id = 1,2,3 的记录 */ @Test public void selectUserBatchId(){ List batchIds = new ArrayList(); batchIds.add(1); batchIds.add(2); batchIds.add(3); List list = userMapper.selectBatchIds(batchIds); System.out.println("list = " + list); } /* * 查询年龄为 21 岁的记录,并循环遍历输出 */ @Test public void selectUserByMap(){ Map<String,Object> userMap = new HashMap<>(); userMap.put("age",21); List<User> users = userMapper.selectByMap(userMap); users.forEach(p-> { System.out.println(p); }); } /** * 根据 entity 条件,查询一条id = 1 的记录,需要手动添加 User(long id)的构造器 */ @Test public void selectOneUser(){ // 需要在 User 类上添加一个 id 的构造函数 User user = userMapper.selectOne(new QueryWrapper<>(new User(1))); System.out.println("user = " + user); } /** * 根据 Wrapper 条件,查询年龄大于 22 的总条目数 */ @Test public void selectCountNum(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); Integer integer = userMapper.selectCount(queryWrapper); System.out.println("查询出来的总记录数 = " + integer); } /* * 查询年龄大于22 的所有记录数,返回一个List */ @Test public void selectUserList(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); List<User> users = userMapper.selectList(queryWrapper); users.forEach(p-> { System.out.println(p); }); } /* * 查询年龄大于22 的所有记录数,返回的是一个List<Map> */ @Test public void selectUserMaps(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper); maps.forEach(p-> { System.out.println(p); }); } /** * 查询年龄大于22 的所有记录数,返回的是一个List<Object> * 注意:只返回第一个字段的值 */ @Test public void selectUserObject(){ QueryWrapper<User> queryWrapper = new QueryWrapper(); queryWrapper.ge("age",22); List<Object> objects = userMapper.selectObjs(queryWrapper); objects.forEach(p-> { System.out.println(p); }); }
参考资料
[1]
BaseMapper: https://gitee.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/mapper/BaseMapper.java
[2]
条件构造器: https://mp.baomidou.com/guide/wrapper.html