MyBatis-plus (MP) 使用

快速开始

导入依赖

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>

Mapper方法

1
2
3
4
// 继承 BaseMapper<T> 类
public interface EmployeeMapper extends BaseMapper<Employee> {

}

常用注解

@TableName

1
2
3
4
5
6
7
描述:表名注解
作用:指定当前实体类映射哪张数据库表, 默认是跟实体类名一致

@TableName("t_employee")
public class Employee {
//.....
}

@TableField

1
2
3
4
5
6
7
8
9
10
11
12
描述:字段注解(非主键)
作用:指定当前属性映射数据库表哪一列, 默认是跟属性名一致

@TableName("employee")
public class Employee {

@TableField(value="employename",exist = false) // 若字段和列名相同可以不写 value
private String name;

}

其中:exist属性表示当前属性是否映射数据库列

PS: MP在于字段匹配过程中自动将表列名中的下划线-排除, 并将-后面单词的首字母大写

@TableId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
描述:主键注解
作用:标记当前属性映射表主键。

@TableName("employee")
public class Employee {

@TableId(value="id", type=IdType.AUTO)
private Long id;

}

其中:type属性指定主键类型
IdType.AUTO 数据库ID自增

IdType.NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)

IdType.INPUT insert前自行set主键值

IdType.ASSIGN_ID 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)

IdType.ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)

@Version【拓展】

1
2
描述:乐观锁注解、标记 @Verison 在字段上
作用:用于标记乐观锁操作字段

通用Mapper接口

  • 通用 CRUD 封装BaseMapper接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
  • 泛型 T 为任意实体对象
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
  • 对象 Wrapper 为 条件构造器

日志 (yml文件)

1
2
3
4
5
6
7
8
9
10
11
第一种:
logging:
level:
cn:
wolfcode:
mapper: debug

第二种(除去多余的信息):
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

insert

insert(T entity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);

需求:添加一条用户信息

@Test
public void testSave(){
Employee employee = new Employee();
employee.setAdmin(1);
employee.setAge(18);
employee.setDeptId(1L);
employee.setEmail("xiaowen@gmail.com");
employee.setName("xiaowen");
employee.setPassword("123");
employeeMapper.insert(employee);
}

update

1
2
3
4
5
Wrapper: 是条件构造器, 用于构建各种where条件

updateById: 等价于:update employee set xxx=xxx where id = xxx

update: 等价于:update employee set xxx = xxx where 其他列= xxx, 具体的条件有updateWrapper提供

updateById(id)

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
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);

需求: 将id=1用户名字修改为xiaowen

@Test
public void testUpdateById(){
Employee employee = new Employee();
employee.setId(1L);
employee.setName("xiaowen");
employeeMapper.updateById(employee);
}

注意: 拼接sql时,所有非null 字段都进行set 拼接

打印sql: UPDATE employee SET name=?, age=?, admin=? WHERE id=?

改进:
1:先查
2:替换
3:更新

需求: 将id=1用户名字修改为xiaowen

@Test

public void testUpdateById2(){
Employee employee = employeeMapper.selectById(1L);
employee.setName("xiaowen");
employeeMapper.updateById(employee);
}

update(entity, wrapper)

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
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

需求:更新name=xiaowen员工年龄为18

@Test
public void testUpdate(){
Employee employee = new Employee();
employee.setAge(18);
//update 语句中的where 条件包装类
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("name", "xiaowen");
employeeMapper.update(employee, wrapper);
}

注意: 拼接sql时,所有非null 字段都进行set 拼接

打印sql: UPDATE employee SET name=?, age=?, admin=? WHERE id=?

升级:

需求:更新name=xiaowen员工年龄为18

@Test
public void testUpdate2(){
//update 语句中的where 条件包装类
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("name", "xiaowen").set("age", 18);
employeeMapper.update(null, wrapper);
}

需求:更新name=xiaowen,并且password=1111的员工年龄为18

@Test
public void testUpdate2(){
//update 语句中的where 条件包装类
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("name", "xiaowen")
.eq("password", "1111")
.set("age", 18);
employeeMapper.update(null, wrapper);
}

使用建议

  • 知道id,并且所有更新使用updateById

  • 部分字段更新,使用update使用这种方式:wrapper.条件.set更新字段

  • **updateById: **全量更新, 只要传入对象属性不为null, 都可以进行update更新, 条件是通过id匹配

正确操作姿势(前提: 必须知道id):

1.查 2.替换 3.更新

  • update+wrapper:部分字段更新, 通过wrapper对象拼接各种满足要求条件, 更新set的列由wrapper 决定

正确操作姿势:

1.设置更新条件 2.拼接更新列

delete

deleteById(id)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);


需求:删除id=1的员工信息
@Test
public void testDeleteById(){
employeeMapper.deleteById(1L);
}

deleteBatchIds(idList)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);


需求:删除id=1, id=2的员工信息
@Test
public void testDeleteBatchIds(){
employeeMapper.deleteBatchIds(Arrays.asList(1L,2L));
}

deleteByMap(map)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

需求:删除name=xiaowen并且age=18的员工信息
@Test
public void testDeleteByMap(){
Map<String, Object> map = new HashMap<>();
map.put("name", "xiaowen");
map.put("age", 18);
employeeMapper.deleteByMap(map);
}

打印:DELETE FROM employee WHERE name = ? AND age = ?

delete(wrapper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 根据 entity 条件,删除记录
*
* @param wrapper 实体对象封装操作类(可以为 null)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

需求:删除name=xiaowen并且age=18的员工信息
@Test
public void testDeleteWrapper(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.eq("name", "xiaowen").eq("age", 18);
employeeMapper.delete(wrapper);
}

select

selectById(id)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);

需求:查询id=1的员工信息

@Test
public void testSelectById(){
System.out.println(employeeMapper.selectById(1L));
}

selectBatchIds(idList)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);


需求:查询id=1,id=2的员工信息
@Test
public void testSelectBatchIds(){
System.out.println(employeeMapper.selectBatchIds(Arrays.asList(1L, 2L)));
}

selectByMap(map)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);


需求: 查询name=xiaowen, age=18的员工信息

@Test
public void testSelectByMap(){
Map<String, Object> map = new HashMap<>();
map.put("name", "xiaowen");
map.put("age", 18);
System.out.println(employeeMapper.selectByMap(map));
}

selectCount(wrapper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 根据 Wrapper 条件,查询总记录数
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);


需求: 查询满足条件的所有的员工个数

@Test
public void testSelectCount(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
System.out.println(employeeMapper.selectCount(wrapper));
//没有指定条件, 等价于传入null
//System.out.println(employeeMapper.selectCount(null));
}

selectList(wrapper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

需求: 查询满足条件的所有的员工信息, 返回List<Employee>

@Test
public void testSelectList(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
List<Employee> list = employeeMapper.selectList(wrapper);
list.forEach(System.out::println);
//没有指定条件, 等价于传入null
//List<Employee> list2 = employeeMapper.selectList(null);
//list2.forEach(System.out::println);
}

selectMaps(wrapper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);


需求: 查询满足条件的所有的员工信息, 返回List<Map<String, Object>> 底层将每条数据封装成HashMap

@Test
public void testSelectMap(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
List<Map<String, Object>> map = employeeMapper.selectMaps(wrapper);

map.forEach(System.out::println);
//没有指定条件, 等价于传入null
//List<Map<String, Object>> map2= employeeMapper.selectMaps(null);
//map2.forEach(System.out::println);
}

PS: 当查询的数据是对象中没有的列时, 可调selectMaps(wrapper)方法

selectPage(page, wrapper)

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
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);


需求:查询第二页员工数据, 每页显示3条, (分页返回的数据是实体对象)

步骤1:配置分页插件
启动类中:

//分页拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setOverflow(true); //合理化
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}

步骤2:编写分页代码

@Test
public void testSelectPage(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
//参数1:当前页, 参数2:每页显示条数
IPage<Employee> page = new Page<>(2, 3);
employeeMapper.selectPage(page, wrapper);
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示条数:" + page.getSize());
System.out.println("总页数:" + page.getPages());
System.out.println("总数:" + page.getTotal());
System.out.println("当前页数据:" + page.getRecords());
}

selectOne(wrapper)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

需求: 查询满足条件的所有的员工, 取第一条

@Test
public void testSelectOne(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
Employee employee = employeeMapper.selectOne(wrapper);
System.out.println(employee);
}

注意: 如果查出数据条数超过一条报错:Expected one result (or null) to be returned by selectOne(), but found: xxx

selectObjs(wrapper)【拓展】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

需求: 查询满足条件的所有的员工, 返回排在第一的列所有数据, 没特别指定, 一般返回时id

@Test
public void testSelectObjs(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
//wrapper.select("name"); //挑选返回的列
List<Object> list = employeeMapper.selectObjs(wrapper);
list.forEach(System.out::println);
}

selectMapsPage(page, wrapper)【拓展】

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
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

需求:查询第二页员工数据, 每页显示3条, (分页返回的数据是HashMap)

步骤1:配置分页插件
步骤2:编写分页代码

@Test
public void testSelectMapsPage(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
//参数1:当前页, 参数2:每页显示条数
IPage<Map<String, Object>> page = new Page<>(2, 3);
employeeMapper.selectMapsPage(page, wrapper);
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示条数:" + page.getSize());
System.out.println("总页数:" + page.getPages());
System.out.println("总数:" + page.getTotal());
System.out.println("当前页数据:" + page.getRecords());
}

条件构造器

继承体系

Wrapper: 条件构造抽象类,最顶端父类

  • AbstractWrapper: 用于查询条件封装,生成 sql 的 where 条件
  • QueryWrapper: Entity 对象封装操作类,不是用lambda语法
  • UpdateWrapper: Update 条件封装,用于Entity对象更新操作
  • AbstractLambdaWrapper: Lambda 语法使用 Wrapper统一处理解析 lambda - 获取 column。
  • LambdaQueryWrapper:看名称也能明白就是用于Lambda语法使用的查询- Wrapper
  • LambdaUpdateWrapper: Lambda 更新封装Wrapper

更新操作

普通更新

1
2
3
4
5
6
7
8
9
10
需求:将id=1的员工name改为xiaowen
@Test
public void testUpdate(){
Employee employee = new Employee();
employee.setId(1L);
employee.setName("xiaowen");
employeeMapper.updateById(employee);
}

可能导致数据丢失

UpdateWrapper更新

set

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
set(String column, Object val)
set(boolean condition, String column, Object val)

例: set("name", "xiaowen")

需求:将id=1的员工name改为xiaowen
@Test
public void testUpdate2(){
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 1L);
wrapper.set("name", "xiaowen");
employeeMapper.update(null, wrapper);
}

需求:将id=1的员工age改为18, 如果传入uname变量值不等于null或者“”,修改为员工name为uname变量值

@Test
public void testUpdate2(){
String uname="xxxx";
//String uname=null;
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 1L);
wrapper.set("age", 18);
if(StringUtils.hasLength(uname)){
wrapper.set("name", uname);
}
employeeMapper.update(null, wrapper);
}

setSql

1
2
3
4
5
6
7
8
9
10
11
12
setSql(String sql)
例: setSql("name = 'xiaowen'")


需求:将id=1的用户name改为xiaowen
@Test
public void testUpdate3(){
UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
wrapper.eq("id", 1L);
wrapper.setSql("name='xiaowen'");
employeeMapper.update(null, wrapper);
}

LambdaUpdateWrapper更新 (推荐使用)

1
2
3
4
5
6
7
8
9
需求:将id=1的用户name改为xiaowen 

@Test
public void testUpdate4(){
LambdaUpdateWrapper<Employee> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(Employee::getId, 1L);
wrapper.set(Employee::getName, "xiaowen");
employeeMapper.update(null, wrapper);
}

查询操作

QueryWrapper查询

1
2
3
4
5
6
7
需求:查询name=xiaowen, age=18的用户
@Test
public void testQuery2(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.eq("name", "xiaowen").eq("age", 18);
System.out.println(employeeMapper.selectList(wrapper));
}

LambdaQueryWrapper查询 (推荐使用)

1
2
3
4
5
6
7
需求:查询name=xiaowen, age=18的用户
@Test
public void testQuery3(){
LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Employee::getName, "xiaowen").eq(Employee::getAge, 18);
System.out.println(employeeMapper.selectList(wrapper));
}

工具类:Wrappers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用于创建各种类型的wrapper

@Test
public void testWrappers(){
//update
UpdateWrapper<Employee> updateWrapper1 = new UpdateWrapper<>();
UpdateWrapper<Employee> updateWrapper2 = Wrappers.<Employee>update();

LambdaUpdateWrapper<Employee> lambdaUpdateWrapper1 = new LambdaUpdateWrapper<>();
LambdaUpdateWrapper<Employee> lambdaUpdateWrapper2 = Wrappers.<Employee>lambdaUpdate();
//UpdateWrapper -->LambdaUpdateWrapper
LambdaUpdateWrapper<Employee> lambdaUpdateWrapper3 = updateWrapper1.lambda();

//select
QueryWrapper<Employee> QueryWrapper1 = new QueryWrapper<>();
QueryWrapper<Employee> QueryWrapper2 = Wrappers.<Employee>query();

LambdaQueryWrapper<Employee> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
LambdaQueryWrapper<Employee> lambdaQueryWrapper2 = Wrappers.<Employee>lambdaQuery();
//QueryWrapper -->LambdaQueryWrapper
LambdaQueryWrapper<Employee> lambdaQueryWrapper3 = QueryWrapper1.lambda();
}

高级查询

QueryWrapper
LambdaQueryWrapper

常用的条件查询,UpdateWrapper涉及到的条件同理可得即可。

列投影

select

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
//参数:指定查询后返回的列
select(String... sqlSelect)

//参数:Predicate 函数, 满足指定判定逻辑列才返回
//要求:wrapper.setEntity(employee);
select(Predicate<TableFieldInfo> predicate)

//参数1:通过实体属性映射表中列
//参数2:Predicate 函数, 满足指定判定逻辑列才返回
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)

需求:查询所有员工, 返回员工name, age列

@Test
public void testQuery4(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.select("name", "age");
employeeMapper.selectList(wrapper);
}

需求:查询所有员工, 返回员工以a字母开头的列

@Test
public void testQuery4(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.select(Employee.class, tableFieldInfo->tableFieldInfo.getProperty().startsWith("a"));
employeeMapper.selectList(wrapper);
}

排序

orderByAsc/orderByDesc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
orderByAsc: 正序排序
等价SQL: select ..from table ORDER BY 字段, ... ASC

wrapper方法:
orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)

示例:
orderByAsc("id", "name")--->order by id ASC,name ASC

需求:查询所有员工信息按age正序排, 如果age一样, 按id正序排

@Test
public void testQuery5(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("age", "id");
employeeMapper.selectList(wrapper);
}

orderByDesc 跟 orderByAsc用法一样, 不过是倒序排

orderBy

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
orderBy:定制排序
等价SQL: select ..from table ORDER BY 字段,

//参数1:控制是否进行排序
//参数2:控制是不是正序
orderBy(boolean condition, boolean isAsc, R... columns)
排序:ORDER BY 字段, ...
例: orderBy(true, true, "id", "name")--->order by id ASC,name ASC


需求:查询所有员工信息按age正序排, 如果age一样, 按id正序排
@Test
public void testQuery5(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
//apper.orderByAsc("age", "id");
//等价于:
wrapper.orderBy(true, true, "age", "id");
employeeMapper.selectList(wrapper);
}


拓展

需求:查询所有员工信息按age正序排, 如果age一样, 按id倒序排
@Test
public void testQuery7(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("age");
wrapper.orderByDesc("id");
employeeMapper.selectList(wrapper);
}

分组查询

groupBy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
groupBy : 分组:GROUP BY 字段, ...

groupBy(R... columns)
groupBy(boolean condition, R... columns)

例: groupBy("id", "name")--->group by id,name

需求: 以部门id进行分组查询,查每个部门员工个数
@Test
public void testQuery22(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.groupBy("dept_id");
wrapper.select("dept_id", "count(id) count");
employeeMapper.selectMaps(wrapper);
}

having

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
having : HAVING ( sql语句 ) 

having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)

例: having("sum(age) > 10")--->having sum(age) > 10
例: having("sum(age) > {0}", 11)--->having sum(age) > 11

需求: 以部门id进行分组查询,查每个部门员工个数, 将大于3人的部门过滤出来

@Test
public void testQuery23(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.select("dept_id", "count(id) count")
.groupBy("dept_id") // 注意: having在groupBy后面
//.having("count > {0}", 3)
.having("count >3");
employeeMapper.selectMaps(wrapper);
}

条件查询

比较运算符

allEq/eq/ne

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
allEq : 全等匹配

wrapper方法:
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)

参数说明:
params : key为数据库字段名,value为字段值
null2IsNull : 为true则在map的value为null时调用 isNull 方法,为false时则忽略value为null

1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null
2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'

需求:查询name=dafei, age=18的员工信息

@Test
public void testQuery8(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("name", "dafei");
map.put("age", 18);
wrapper.allEq(map);
employeeMapper.selectList(wrapper);
}

@Test
public void testQuery8(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("name", "dafei");
map.put("age", 18);
map.put("dept_id", null);
wrapper.allEq(map, true);
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
allEq : 全等匹配(带条件过滤的)

allEq(BiPredicate<R, V> filter, Map<R, V> params)
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)

参数说明:
filter : 过滤函数,是否允许字段传入比对条件中
1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})--->name = '老王' and age is null
2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)--->name = '老王'

需求:查询满足条件员工信息, 注意传入的map条件中, 包含a的列才参与条件查询
@Test
public void testQuery9(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("name", "dafei");
map.put("age", 18);
wrapper.allEq((k, v)-> k.contains("m"), map);
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
eq:单个参数判断是否相等

eq(R column, Object val)
eq(boolean condition, R column, Object val)

等于 =
例: eq("name", "老王")--->name = '老王'

需求:查询name=dafei员工信息
@Test
public void testQuery10(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.eq("name", "dafei");
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
ne: 不等于

ne(R column, Object val)
ne(boolean condition, R column, Object val)

不等于 <>
例: ne("name", "老王")--->name <> '老王'

需求:查询name !=dafei员工信息
@Test
public void testQuery11(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.ne("name", "dafei");
employeeMapper.selectList(wrapper);
}

gt/ge/lt/le

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
gt : 大于 >
gt(R column, Object val)
gt(boolean condition, R column, Object val)

例: gt("age", 18)--->age > 18

需求:查询age 大于18岁员工信息
--------------------------------------------------
ge:大于等于 >=
ge(R column, Object val)
ge(boolean condition, R column, Object val)

例: ge("age", 18)--->age >= 18
--------------------------------------------------
lt:小于 <
lt(R column, Object val)
lt(boolean condition, R column, Object val)

例: lt("age", 18)--->age < 18
--------------------------------------------------
le:小于等于 <=

le(R column, Object val)
le(boolean condition, R column, Object val)

例: le("age", 18)--->age <= 18

between/notBetween

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
between : BETWEEN 值1 AND 值2

between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)

例: between("age", 18, 30)--->age between 18 and 30

需求:查询年龄介于18~30岁的员工信息

@Test
public void testQuery13(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.between("age", 18, 30);
employeeMapper.selectList(wrapper);
}
-----------------------------------------------------------
notBetween : NOT BETWEEN 值1 AND 值2

notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)

例: notBetween("age", 18, 30)--->age not between 18 and 30

需求:查询年龄小于18或者大于30岁的员工信息【用between实现】

@Test
public void testQuery13(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.notBetween("age", 18, 30);
employeeMapper.selectList(wrapper);
}

isNull/isNotNull

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
isNull :  字段 IS NULL

isNull(R column)
isNull(boolean condition, R column)

例: isNull("name")--->name is null

需求: 查询dept_id 为null 员工信息
@Test
public void testQuery16(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.isNull("dept_id");
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
isNotNull : 字段 IS NOT NULL

isNotNull(R column)
isNotNull(boolean condition, R column)

例: isNotNull("name")--->name is not null

需求: 查询dept_id 为不为null 员工信息
@Test
public void testQuery16(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.isNotNull("dept_id");
employeeMapper.selectList(wrapper);
}

in/notIn/inSql/notInSql

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
in : 字段 IN (value1, value2, ...)

in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)

例: in("age",{1,2,3})--->age in (1,2,3)

需求: 查询id为1, 2 的员工信息

@Test
public void testQuery18(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.in("id", 1L, 2L);
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
notIn : 字段 NOT IN (value1, value2, ...)

notIn(R column, Object... values)
notIn(boolean condition, R column, Object... values)

例: notIn("age",{1,2,3})--->age not in (1,2,3)

需求: 查询id不为1, 2 的员工信息
@Test
public void testQuery19(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.notIn("id", 1L, 2L);
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
inSql : 字段 IN ( sql语句 )

inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)

例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)

需求: 查询id为1, 2 的员工信息
@Test
public void testQuery20(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.inSql("id", "1,2");
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
notInSql : 字段 NOT IN ( sql语句 )

notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)

例: notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)
例: notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)

需求: 查询id不为1, 2 的员工信息
@Test
public void testQuery21(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.notInSql("id", "1,2");
employeeMapper.selectList(wrapper);
}

exists/notExists【拓展】

1
2
3
4
5
6
7
8
9
10
11
12
13
exists: 拼接 EXISTS ( sql语句 )

exists(String existsSql)
exists(boolean condition, String existsSql)

例: exists("select id from table where age = 1")--->exists (select id from table where age = 1)

notExists: 拼接 NOT EXISTS ( sql语句 )

notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)

例: notExists("select id from table where age = 1") ---> not exists (select id from table where age = 1)

模糊查询

like/notLike

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
like: LIKE '%值%'

like(R column, Object val)
like(boolean condition, R column, Object val)

例: like("name", "王")--->name like '%王%'

需求: 查询name中含有fei字样的员工

@Test
public void testQuery14(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.like("name", "fei");
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
notLike : NOT LIKE '%值%'

notLike(R column, Object val)
notLike(boolean condition, R column, Object val)

例: notLike("name", "王")--->name not like '%王%'

需求: 查询name中不含有fei字样的员工
@Test
public void testQuery14(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.notLike("name", "fei");
employeeMapper.selectList(wrapper);
}

likeLeft/likeRight

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
likeLeft : LIKE '%值'

likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)

例: likeLeft("name", "王")--->name like '%王'

需求: 查询name以fei结尾的员工信息
@Test
public void testQuery15(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.likeLeft("name", "fei");
employeeMapper.selectList(wrapper);
}
--------------------------------------------------------------------
likeRight : LIKE '值%'

likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)

例: likeRight("name", "王")--->name like '王%'

需求: 查询姓王的员工信息
@Test
public void testQuery16(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.likeRight("name", "王");
employeeMapper.selectList(wrapper);
}

逻辑运算符

or

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
or : 拼接 OR

or()
or(boolean condition)

例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'

注意事项:
主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

需求: 查询age = 18 或者 name=dafei 或者 id =1 的用户

@Test
public void testQuery24(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.eq("age", 18)
.or()
.eq("name", "dafei")
.or()
.eq("id", 1L);
employeeMapper.selectList(wrapper);
}
-------------------------------------------
嵌套OR:

or(Consumer<Param> consumer)
or(boolean condition, Consumer<Param> consumer)

例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')

需求:查询name含有fei字样的,或者 年龄在1830之间的用户

@Test
public void testQuery25(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.like("name", "fei")
.or(wr -> wr.le("age", 30).ge("age", 18));
employeeMapper.selectList(wrapper);
}

and

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
and : 默认是and操作

需求:查询年龄介于18~30岁的员工信息
@Test
public void testQuery26(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.le("age", 30).ge("age", 18);
employeeMapper.selectList(wrapper);
}
---------------------
嵌套and:

and(Consumer<Param> consumer)
and(boolean condition, Consumer<Param> consumer)

需求:查询name含有fei字样的并且 年龄在小于18或者大于30的用户
@Test
public void testQuery27(){
QueryWrapper<Employee> wrapper = new QueryWrapper<>();
wrapper.like("name", "fei")
.and(wr -> wr.le("age", 30)
.or()
.ge("age", 18));
employeeMapper.selectList(wrapper);
}

自定义SQL

注解方式【拓展】

单表查询

1
2
@Select("select e.* from employee e")
List<Employee> listByAnnoSingle();

关联查询

1
2
3
4
5
6
7
@Select("select e.*, d.id d_id, d.name d_name, d.sn d_sn from employee e left join department d on e.dept_id = d.id")
@Results({
@Result(column="d_id", property = "dept.id"),
@Result(column="d_name", property = "dept.name"),
@Result(column="d_sn", property = "dept.sn")
})
List<Employee> listByAnnoJoin();

PS: 在开发中,我们使用mybatis+mp搭配开发, 它们会发挥各自的优势,并弥补对方的短板,这就是最好的方式。

通用Service接口

通用方式

1
2
3
4
5
6
7
8
9
10
11
1:自定义服务接口集成IService接口
public interface IEmployeeService extends IService<Employee> {
}

2:服务接口实现类集成IService接口实现类ServiceImpl同时实现自定义接口
注意ServiceImpl实现类泛型:
泛型1:实体类的mapper接口
泛型2:实体类
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
}

常用方法

getBaseMapper() : 获取引用的XxxxMapper对象

getOne(wrapper) : 指定条件查询单个, 结果数据超过1个报错

list(wrapper) : 指定条件查询多个

page(page, wrapper) : 分页+高级查询

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
步骤1:配置分页插件
//分页
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setOverflow(true); //合理化
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}

步骤2:编写分页代码
1>EmployeeQuery
2>query(EmployeeQuery)方法定义
3>query(EmployeeQuery)方法实现

@Override
public IPage<Employee> query(EmployeeQuery qo) {
IPage<Employee> page = new Page<>(qo.getCurrentPage(), qo.getPageSize()); //设置分页信息
QueryWrapper<Employee> wrapper = Wrappers.<Employee>query(); //拼接条件
return super.page(page,wrapper);
}


需求:查询第2页员工信息, 每页显示3条, 按id排序
@Test
public void testPage(){
EmployeeQuery qo = new EmployeeQuery();
qo.setPageSize(3);
qo.setCurrentPage(2);
IPage<Employee> page = employeeService.query(qo);
System.out.println("当前页:" + page.getCurrent());
System.out.println("总页数:" + page.getPages());
System.out.println("每页显示条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("当前页显示记录:" + page.getRecords());
}

PS : 分页信息封装对象,里面有各种分页相关信息, 等价于之前的PageInfo

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
需求:在IEmployeeService 定义一个tran方法, 执行save2次, 在中间模拟事务

步骤1:定义tran方法, 实现逻辑
步骤2:在EmployeeServiceImpl类头顶贴@Transactional

public void tran() {
Employee employee = new Employee();
employee.setName("dafei1");
super.save(employee);

System.out.println(1/0);

employee = new Employee();
employee.setName("dafei2");
super.save(employee);
}