目录
一、简介
1.1 概况
大家在日常开发中应该能发现,单表的CRUD功能代码重复度很高,也没有什么难度。而这部分代码量往往比较大,开发起来比较费时。
因此,目前企业中都会使用一些组件来简化或省略单表的CRUD开发工作。目前在国内使用较多的一个组件就是MybatisPlus.
1.2 特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
这里比较重要的几点就是,MybatisPlus不会对现有项目造成影响,也就是说你原有的项目使用的是Mybatis,再导入mybatisPlus之后也是可以继续运行的。
我们平时使用MybatisPlus的场景大多数是对单表的CRUD。
二、快速入门
1.建表
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');
2.引依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
3.application.ymal文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2b8
username: root
password: admin
#配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4.定义mapper继承BaseMapper
5.总结
三、Mybatis Plus的使用
1.常见注解
在刚刚的入门案例中,我们仅仅引入了依赖,继承了BaseMapper就能使用MybatisPlus,非常简单。但是问题来了: MybatisPlus如何知道我们要查询的是哪张表?表中有哪些字段呢?
大家回忆一下,UserMapper在继承BaseMapper的时候指定了一个泛型:
泛型中的User就是与数据库对应的PO.
MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:
-
MybatisPlus会把PO实体的类名驼峰转下划线作为表名
-
MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
-
MybatisPlus会把名为id的字段作为主键
但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。
那万一你的表名、字段名、主键名都和数据库中的表、字段、主键都不一样,那Mybatis Plus怎么映射过去呢?(虽然说我们开发中一般不会故意出现这种情况,因为不规范,但有时候就是无法避免)
这时候就要使用MybatisPlus提供给我们的注释了
1.1 @TableName
-
描述:表名注解,标识实体类对应的表
-
使用位置:实体类
@TableName("user")
public class User {
private Long id;
private String name;
}
该注解还有其他的属性配置,如下表:
value | String | 否 | "" | 表名 |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定) |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名 @since 3.3.1 |
1.2 @TableId
-
描述:主键注解,标识实体类中的主键字段
-
使用位置:实体类的主键字段
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
}
TableId
注解支持两个属性:
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 表名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
IdType
支持的类型有:
值 | 描述 |
---|---|
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法) |
| 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID) |
| 32 位 UUID 字符串(please use ASSIGN_UUID) |
| 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID) |
这里比较常见的有三种:
-
AUTO
:利用数据库的id自增长 -
INPUT
:手动生成id -
ASSIGN_ID
:雪花算法生成Long
类型的全局唯一id,这是默认的ID策略
1.3 @TableField
说明:普通字段注解
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
@TableField("isMarried")
private Boolean isMarried;
@TableField("concat")
private String concat;
}
一般情况下我们并不需要给字段添加@TableField
注解,一些特殊情况除外:
-
成员变量名与数据库字段名不一致
-
成员变量是以
isXXX
命名,按照JavaBean
的规范,MybatisPlus
识别字段时会把is
去除,这就导致与数据库不符。 -
成员变量名与数据库一致,但是与数据库的关键字冲突。使用
@TableField
注解给字段名添加转义字符:``
支持的其它属性如下:
属性 | 类型 | 必填 | 默认值 | 描述 |
value | String | 否 | "" | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考(opens new window) |
update | String | 否 | "" | 字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性) |
insertStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:IGNORED update table_a set column=#{columnProperty} |
whereStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC 类型 (该默认值不代表会按照该值生效) |
typeHandler | TypeHander | 否 | 类型处理器 (该默认值不代表会按照该值生效) | |
numericScale | String | 否 | "" | 指定小数点后保留的位数 |
2.常见配置
MybatisPlus也支持基于yaml文件的自定义配置,详见官方文档:
https://www.baomidou.com/pages/56bac0/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE
大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:
-
实体类的别名扫描包
-
全局id类型
mybatis-plus:
type-aliases-package: com.scau.mp.domain.po
global-config:
db-config:
id-type: auto # 全局id类型为自增长
需要注意的是,MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:
mybatis-plus:
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。
3.BaseMapper的基础CRUD方法
官方文档案例如下:https://baomidou.com/pages/49cc81/#mapper-crud-接口
代码测试用例:
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
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);
userMapper.insert(user);
}
/**
* 修改
* UPDATE user SET name=?, age=?, email=? WHERE id=?
*/
@Test
public void update() {
User user = new User(100L, "Kevin", "0", 25);
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);
}
}
4.Wrapper的使用(条件构造器)
通过BaseMapper提供的一些方法我们可以完成一些基本的CRUD,但无法完成复杂条件的查询;对于复杂条件的查询,MyBatis Plus提供了Wrapper接口来处理;
Wrapper是MyBatis Plus提供的一个条件构造器,主要用于构建一系列条件,当Wrapper构建完成后,可以使用Wrapper中的条件进行查询、修改、删除等操作;
官方文档如下:条件构造器 | MyBatis-Plus
Wrapper是条件构造抽象类,最顶端父类,其主要实现类有如下:
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : Query条件封装
UpdateWrapper : Update条件封装
AbstractLambdaWrapper : 使用Lambda语法
LambdaQueryWrapper :基于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : 基于Lambda语法使用的更新Wrapper
4.1Wrapper的方法集
方法名 | 解释 | 示例 |
---|---|---|
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 |
having | HAVING ( sql语句 ) | 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 = '老王' |
这些方法不用背,要构造Wrapper的时候查官方文档即可:
4.2QueryWapper的使用
创建Wrapper对象:
- Wrappers静态方法:
public static <T> QueryWrapper<T> query()
- 通过QueryWrapper对象的构造方法:
public QueryWrapper()
代码如下:
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
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");
/*
参数1: 是否要进行name条件的拼接
*/
String name = "Jack";
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);
}
4.3UpdateWrapper的使用
代码如下:
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
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);
// 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);
}
}
4.4LambdaQueryWrapper
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值
。这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?
其中一种办法是基于变量的gettter
方法结合反射技术。因此我们只要将条件对应的字段的getter
方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用
和Lambda
表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
-
LambdaQueryWrapper
-
LambdaUpdateWrapper
分别对应QueryWrapper和UpdateWrapper
@Test
void testLambdaQueryWrapper() {
// 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
5.Mapper分页查询
在MyBatis中提供有Page对象来帮助我们实现分页查询,在Page对象中有如下成员:
方法 | 描述 |
---|---|
getRecords() | 查询当前页的数据记录,返回类型是列表 |
getCurrent() | 返回当前页码 |
getTotal() | 获取总记录数 |
getSize() | 获取页面大小 |
getPages() | 获取总页数 |
hasPrevious() | 判断当前页是否有上一页 |
hasNext() | 判断当前页是否有下一页 |
而在MybatisPlus中则是使用分页插件来实现分页查询的,因此我们首先要配置MyBatisPlus的分页插件;
- 配置分页插件:
@Configuration
@MapperScan("com.scau.mapper") // 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):
参数1:分页配置类
参数2:分页查询条件
解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为指定类型
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper)
参数1:分页配置类
参数2:分页查询条件
解释:根据分页配置和分页查询条件来完成分页查询,当前页数据为Map类型
- 1)无条件分页查询:
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
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());
}
}
查询结果如下:
# 首先查询总记录数
==> Preparing: SELECT COUNT(*) FROM user
==> Parameters:
<== Columns: COUNT(*)
<== Row: 10
<== Total: 1
# 再查询分页
==> Preparing: SELECT id,name,sex,age FROM user LIMIT ?
==> Parameters: 3(Long)
<== Columns: id, name, sex, age
<== Row: 1, Jone, 1, 27
<== Row: 2, Jack, 0, 20
<== Row: 3, Tom, 1, 28
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@226eba67]
User(id=1, name=Jone, sex=1, age=27)
User(id=2, name=Jack, sex=0, age=20)
User(id=3, name=Tom, sex=1, age=28)
------------
当前页:1
每页显示的条数:3
总记录数:10
总页数:4
是否有上一页:false
是否有下一页:true
2022-11-15 15:28:16.946 INFO 8724 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2022-11-15 15:28:16.948 INFO 8724 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Process finished with exit code 0
2)带条件查询
/**
* 带条件分页查询
*
* @throws Exception
*/
@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());
}
查询结果如下:
==> Preparing: SELECT COUNT(*) FROM user WHERE (name LIKE ?)
==> Parameters: %a%(String)
<== Columns: COUNT(*)
<== Row: 5
<== Total: 1
==> Preparing: SELECT id,name,sex,age FROM user WHERE (name LIKE ?) LIMIT ?
==> Parameters: %a%(String), 3(Long)
<== Columns: id, name, sex, age
<== Row: 2, Jack, 0, 20
<== Row: 4, Sandy, 1, 21
<== Row: 6, Jackson, 0, 18
<== Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@58a2b4c]
User(id=2, name=Jack, sex=0, age=20)
User(id=4, name=Sandy, sex=1, age=21)
User(id=6, name=Jackson, sex=0, age=18)
------------
当前页:1
每页显示的条数:3
总记录数:5
总页数:2
是否有上一页:false
是否有下一页:true
3)将分页结果以map的形式返回
/**
* 查询结果以Map返回
*
* @throws Exception
*/
@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());
}
6.MyBatis Plus的Service查询
通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行、remove删除、list 查询集合、page查询分页
架构图如下:
使用步骤:
- 1)定义一个UserService接口继承与MyBatisPlus提供的IService接口:
public interface IUserService extends IService<User> {
}
- 2)定义一个UserService的实现类,并且继承与MyBatisPlus提供的ServiceImpl:
-
@Service("userService") public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { }
常用的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():无条件分页
测试代码如下:
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class Demo07_Service查询 {
@Autowired
private IUserService userService;
/**
* 新增
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
User user = new User(null, "xiaohui", "0", 20);
userService.save(user);
}
/**
* 如果id存在则修改,不存在则新增
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
User user = new User(1L, "xiaohui", "0", 20);
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);
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();
// 根据条件查询总记录数
int 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();
}
}