# queryWrapper
构造wrapper
使用Wrappers.lambdaQuery(),减少直接使用new LambdaQueryWrapper<>()
LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda();
LambdaQueryWrapper<User> lambda2 = new LambdaQueryWrapper<>();
LambdaQueryWrapper<User> lambda3 = Wrappers.<User>lambdaQuery();
QueryWrapper
Mybatis-Plus 通过 QueryWrapper( MP 封装的一个查询条件构造器,继承自AbstractWrapper,AbstractWrapper 实现了 Wrapper等接口) 来让用户自由的构建查询条件,简单便捷,没有额外的负担,能够有效提高开发效率
| 查询方式 | 说明 | 查询方式 | 说明 |
|---|---|---|---|
| or | 或条件语句 | orderBy | 排序查询 |
| and | 且条件语句 | orderByAsc | ASC 排序 ORDER BY |
| like | 模糊查询 like | orderByDesc | DESC 排序 ORDER BY |
| notLike | 模糊查询 not Like | having | 分组后筛选 |
| likeLeft | likeRight | eq | 等于 = |
| exists | exists 条件语句 | allEq | 基于 map 内容等于= |
| notExists | not Exists 条件语句 | gt | 大于> |
| isNull | null 值查询 | ge | 大于等于>= |
| isNotNull | is Not Null 查询 | ne | 不等于 <> |
| in | in 查询 | lt | 小于< |
| notIn | not in 查询 | le | 小于等于<= |
| inSql | notinSql | between | between 条件语句 |
| groupBy | 分组查询 |
# 使用QueryWrapper的新增操作
MyBatis-Plus默认的主键策略是:
ID_WORKER全局唯一ID
自增策略
要想主键自增需要配置如下主键策略,需要在创建数据表的时候设置主键自增,同时在实体字段中配置`@TableId(type = IdType.AUTO)
int insert(T entity);:将构造的实体对象插入数据库(如果数据库有自增id,注意@TableId是否正确选择了IdType,@Transactional和@Rollback结合一起使用才不会对数据库产生影响)
# 使用QueryWrapper的更新操作
int updateById(@Param(Constants.ENTITY) T entity);:根据实体对象的id查找,再对查找到的记录进行修改,返回受影响的条数
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper updateWrapper);:根据updateWrapper查找,再根据entity对象设值,返回受影响的条数(updateWrapper为空时全表更新,用queryWrapper代替updateWrapper也行)
/**
* 条件构造器 更新操作
*/
@Test
public void testWrapperUpdate() {
Employee employee = new Employee();
employee.setLastName("XP");
employee.setEmail("xp@github.com");
employee.setGender(0);
employeeMapper.update(employee,
new QueryWrapper<Employee>()
.eq("age", 22)
.eq("last_name", "MP")
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// lambda
try {
LambdaUpdateWrapper<ContactBo> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.eq(ContactBo::getId, queryVo.getId())
.set(ContactBo::getStatus, DictUtil.codeToValue(DictConstants.CONTACT_READ_STATUS, DictConstants.READ));
this.update(lambdaUpdateWrapper);
return Result.getSuccess(queryVo.getId());
} catch (Exception e) {
log.error(e.getMessage(), e);
return Result.getFailure();
}
2
3
4
5
6
7
8
9
10
11
# 使用QueryWrapper的查询操作
| 方法 | 含义 |
|---|---|
T selectById(Serializable id); | 根据id查找,返回一个实体类对象(注意:@TableId注解是否已打上) |
List selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); | 根据id数组进行查找,返回一个List对象(注意:id数组不可为null,数组长度必须大于0) |
List selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); | 根据map对象查找,条件是等于,返回一个List对象(map为null或者map对象内没有元素时进行全表查询) |
T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据queryWrapper查找,返回一个实体类对象或者null(selectOne的查询结果数不是小于等于一条时会报错,如果根据id查询,通常用selectById,这里可能是联合索引/逻辑主键的一个使用) |
Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据queryWrapper统计条数(queryWrapper为null时统计全表记录数) |
List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据queryWrapper查找,返回一个List对象(queryWrapper为null时全表查询) |
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据queryWrapper查找,返回一个List对象,List内的元素是一个map对象(map对象内的key是对应表的字段名) |
List selectObjs(@Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据queryWrapper查找,返回一组id值 |
IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据queryWrapper分页查找,返回一个IPage对象(是否配置了分页拦截器) |
IPage<Map<String, Object>> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); | 根据Wrapper查找,返回一个IPage对象,records内元素是一个个map对象(map对象的key是对应表的字段名) |
/**
* 条件构造器 查询操作
*/
@Test
public void testWrapperSelect() {
// 分页查询 tbl_employee 表中,年龄在 18~50 之间性别为男且
// 姓名为 xx 的所有用户
IPage<Employee> page = employeeMapper.selectPage(new Page<Employee>(1, 3),
new QueryWrapper<Employee>()
.between("age", 18, 50)
.eq("gender", 1)
.eq("last_name", "MP")
);
System.out.println(page.getRecords());
// 查询 tbl_employee 表中,名字中带有M 性别为女 或者邮箱中带有a的
List<Employee> employees = employeeMapper.selectList(
new QueryWrapper<Employee>()
.eq("gender", 0)
.like("last_name", "M")
.or() // SQL:(gender = ? AND last_name LIKE ? OR email LIKE ?)
.like("email", "a")
);
System.out.println(employees);
// 带排序的查询
List<Employee> list = employeeMapper.selectList(
new QueryWrapper<Employee>()
.eq("gender", 1)
// .orderBy(true, true, "age")
.orderByDesc("age")
);
System.out.println(list);
}
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
# 指定要查询的列
@Test
public void testSelectListColumn() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name", "age");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
2
3
4
5
6
7
8
9
# 使用QueryWrapper的删除操作
| 方法 | 含义 |
|---|---|
int deleteById(Serializable id); | 根据id删除,返回受影响的条数 |
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); | 根据map对象删除,返回受影响的条数(key=columnName,value=columnValue) |
int delete(@Param(Constants.WRAPPER) Wrapper wrapper); | 根据wrapper删除,返回受影响的条数(wrapper为null时全表删除) |
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); | 根据id数组删除,返回受影响的条数(数组不能为null,且size()大于0) |
/**
* 条件构造器 删除操作
*/
@Test
public void testWrapperDelete() {
employeeMapper.delete(
new QueryWrapper<Employee>()
.eq("age", 22)
.eq("last_name", "MP")
);
}
2
3
4
5
6
7
8
9
10
11
# 逻辑删除
- 数据库中添加 deleted字段——"ALTER TABLE
userADD COLUMNdeletedboolean DEFAULT 0" - 实体类添加deleted 字段——"并加上 @TableLogic 注解"
- 修改配置文件application.properties
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
2
- 在 MybatisPlusConfig 中注册 Bean——注册类中添加ISqlInjector
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
2
3
4
- 测试逻辑删除:测试后发现,数据并没有被删除,deleted字段的值由0变成了1(
注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作) - 测试逻辑删除后的查询:
MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断,自动包含了WHERE deleted=0
# 业务层自动填充方案
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等
例如:在User表中添加datetime类型的新的字段 create_time、update_time
- 自动填充方案一、数据级别的自动填充,分别设置默认值:
create_timedatetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,update_timedatetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- 自动填充方案二、业务层处理(
@TableField(fill = FieldFill.INSERT)):
FieldFill很多类型可以选择
编写实现元对象处理器接口
package com.atguigu.mybatis_plus.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 乐观锁
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
# 乐观锁实现方式
- 取出记录时,获取当前version:假设取出的version=1
SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=1
- 更新时,带上这个version(执行更新时,
set version = newVersion where version = oldVersion)
UPDATE USER SET `name`='helen yao', `version`=`version` + 1 WHERE id=1 AND `version`=1
# 数据库中添加version字段
# 实体类添加version字段
支持的数据类型只有
int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1,newVersion 会回写到 entity 中仅支持 updateById(id) 与 update(entity, wrapper) 方法在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
# 编写配置文件
package com.atguigu.mybatis_plus.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 测试乐观锁
# 几个易错方法
- inSql
@Test
public void testSelectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.in("id", 1, 2, 3);
queryWrapper.inSql("id", "select id from user where id < 3");
List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
objects.forEach(System.out::println);
}
2
3
4
5
6
7
8
9
10
- 不调用or则默认为使用 and 连
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
2
3
4
5
6
7
where deleted=0 and name like '%h%' or age between 20 and 30
- 嵌套or、嵌套and
- last
直接拼接到 sql 的最后(注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.last("limit 1");
2
select * from t where t.age=12 and t.name like '%hu%' limit 1;
- set、setSql
最终的sql会合并user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段
@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(99);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '123@qq.com'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
update t set t.age=99,t.name='老李头',update_time='',email='123@qq.com' where t.name like'%h%';