# queryWrapper

返回:mybatis

构造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")
    );
}
1
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();
}
1
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);
}
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

# 指定要查询的列

返回顶部

@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);
}
1
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")
    );
}
1
2
3
4
5
6
7
8
9
10
11

# 逻辑删除

返回顶部

  • 数据库中添加 deleted字段——"ALTER TABLE user ADD COLUMN deleted boolean 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
1
2
  • 在 MybatisPlusConfig 中注册 Bean——注册类中添加ISqlInjector
@Bean
public ISqlInjector sqlInjector(){
    return new LogicSqlInjector();
}
1
2
3
4
  • 测试逻辑删除:测试后发现,数据并没有被删除,deleted字段的值由0变成了1(注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
  • 测试逻辑删除后的查询:MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断,自动包含了WHERE deleted=0

# 业务层自动填充方案

返回顶部

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等

例如:在User表中添加datetime类型的新的字段 create_time、update_time

  • 自动填充方案一、数据级别的自动填充,分别设置默认值:
    • create_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    • update_time datetime 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);
    }
}
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

# 乐观锁

返回顶部

主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新

# 乐观锁实现方式

back

  • 取出记录时,获取当前version:假设取出的version=1
SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=1
1
  • 更新时,带上这个version(执行更新时, set version = newVersion where version = oldVersion)
UPDATE USER SET `name`='helen yao', `version`=`version` + 1 WHERE id=1 AND `version`=1
1

# 数据库中添加version字段

# 实体类添加version字段

back

支持的数据类型只有 int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1,newVersion 会回写到 entity 中仅支持 updateById(id) 与 update(entity, wrapper) 方法 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!

# 编写配置文件

back

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();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 测试乐观锁

back

# 几个易错方法

返回顶部

  • 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);
}
1
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);
1
2
3
4
5
6
7
where deleted=0 and name like '%h%' or age between 20 and 30
1
  • 嵌套or、嵌套and
  • last
    直接拼接到 sql 的最后(注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.last("limit 1");
1
2
select * from t where t.age=12 and t.name like '%hu%' limit 1;
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);
}
1
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%';
1