mybatis-plus中QueryWrapper的使用技巧

前言:mybatis-plus使用场景

mybatis大家都有使用过,既面向对又灵活可配。不友好的地方是,会随着使用出现大量xml文件和SQL语句。
此时,mybatis-plus应运而生,对mybatis做了无侵入增强,还可以简化SQL语句,或者不写SQL语句。

MyBatis-Plus 官网:https://mp.baomidou.com/
MyBatis-Plus 官方文档:https://mp.baomidou.com/guide/
码云项目地址:https://gitee.com/baomidou/mybatis-plus
GitHub地址:https://github.com/baomidou/mybatis-plus
MyBatis-Plus开发组织:https://gitee.com/baomidou


QueryWrapper的使用技巧

  • 演示表、数据和相关类
  • mybatis-plus使用的几个场景
    • 1、分页查询多表数据
    • 2、自定义分页查询接口
    • 3、${ew.customSqlSegment}的使用
      • 扩展1:Update语句与${ew.customSqlSegment}结合的使用
      • 扩展2:跟${ew.sqlSegment}等价
    • 4、逻辑删除(or软删除)的使用
      • 方式一:在配置文件设置全局配置
      • 方式二:字段添加@TableLogic注解局部设置
      • 扩展1:如果两种方式都使用了,以@TableLogic为准
      • 扩展2:删除字段加@TableField(exist = false)
    • 5、@TableField注解的使用
    • 6、查询指定的列或者排除指定的列
    • 7、where子句嵌套查询条件
    • 8、where子句IN子查询
    • 9、where子句使用SQL函数
    • 10、除了QueryWrapper还可以用Map作为参数
    • 11、service的batch批量操作方法
    • 12、service的getOne方法
    • 13、基于lambda的相关操作
    • 14、乐观锁的使用
    • 15、字段为空update修改不生效的问题
    • 16、Wrappers.query()使简单查询简化


演示表、数据和相关类

#职位表
CREATE TABLE `position` (
    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
    position_no VARCHAR(20) DEFAULT NULL COMMENT '职位编码',
    position_name VARCHAR(30) DEFAULT NULL COMMENT '职位名称',
    create_time DATETIME DEFAULT NULL COMMENT '创建时间'
) ENGINE=INNODB CHARSET=UTF8;

INSERT INTO `position`(id, position_no, position_name, create_time) VALUES
(1, 'P001', '大boss', NOW()),
(2, 'P002', '经理', NOW()),
(3, 'P003', '艺人', NOW());

#用户表
CREATE TABLE `user` (
    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
    `name` VARCHAR(30) DEFAULT NULL COMMENT '姓名',
    age INT(11) DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
    position_no VARCHAR(20) NOT NULL COMMENT '职位编码',
    manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
    create_time DATETIME DEFAULT NULL COMMENT '创建时间',
    CONSTRAINT manager_fk FOREIGN KEY (manager_id) REFERENCES `user` (id)
)  ENGINE=INNODB CHARSET=UTF8;
 
INSERT INTO `user` (id, `name`, age, email, position_no, manager_id, create_time) VALUES 
(1087982257332887553, '张靓颖', 40, '[email protected]', 'P001', NULL, NOW()),
(1088248166370832385, '陶子', 25, '[email protected]', 'P002', 1087982257332887553, NOW()),
(1088250446457389058, '李艺伟', 28, NULL,  'P003',1088248166370832385, NOW()),
(1094590409767661570, '张雨琪', 31, '[email protected]', 'P003', 1088248166370832385, NOW()),
(1094592041087729666, '刘红雨', 32, '[email protected]', 'P003', 1088248166370832385, NOW());

在这里插入图片描述

mybatis-plus中QueryWrapper的使用技巧_第1张图片

/**
 *  实体类
 */
import lombok.Data;
import java.util.Date;

@Data
public class User {
 
    private Long id;
 
    private String name;
 
    private Integer age;
 
    private String email;
 
    private Long managerId;
 
    private Date createTime;

    @TableField(exist = false)
    private String positionName;
 
}

/**
 *  Mapper 接口
 */
public interface UserMapper extends BaseMapper<User> {

}

/**
 *  Mapper xml
 */
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.UserMapper">

</mapper>

/**
 *  Service 接口
 */
public interface IUserService extends IService<User> {

}

/**
 *  Service 实现类
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

mybatis-plus使用的几个场景

常规用法这里就不演示了,下面分析一下工作中使用到的技巧。


1、分页查询多表数据

mybatis-plus是支持单表的分页查询的,如果分页查询多表数据,需要自己重写一下。
演示场景:分页查询用户列表时,想展示用户信息和用户的职位名称,并支持按职位名称模糊匹配过滤用户数据。重写代码片段如下:

Controller类:

@RestController
@RequestMapping("/user")
@Api(value = "测试用户", tags = "测试用户")
public class UserController {

    @Autowired
    private IUserService userService;

    @ApiOperation(value = "分页查询", notes = "分页查询")
    @RequestMapping(value = "/listByPage", method = RequestMethod.POST)
    public Result<IPage<User>> listByPage(@RequestBody(required = false) QueryParam<JSONObject> queryParam) {
        if (queryParam == null) {
            queryParam = new QueryParam();
        }
        QueryWrapper qw = new QueryWrapper<>();
        JSONObject jsonParam = queryParam.getParam();
        if (jsonParam != null) {
            if (StringUtils.isNotBlank(jsonParam.getStr("name"))) {
                qw.likeRight("u.name", jsonParam.getStr("name"));
            }
            if (StringUtils.isNotBlank(jsonParam.getStr("positionName"))) {
                qw.like("p.position_name", jsonParam.getStr("positionName"));
            }
        }
        Page<User> page = userService.page(new Page(queryParam.getCurrent(), queryParam.getSize()), qw);
        return Result.ok(page);
    }

}

扩展:

一、关闭分页时执行count()查询总数
new Page(queryParam.getCurrent(), queryParam.getSize())
构造函数的两个参数:当前页、每页数量
如果业务需求只需要查询分页做上下页切换而不需要记录总数,可以设置第三个参数false就可以不查询count(),以提高性能。new Page(queryParam.getCurrent(), queryParam.getSize(), false) 。
 
二、物理分页、逻辑分页的概念
物理分页就是每次查库做分页,逻辑分页是一次查出以后在代码里做分页。


Mapper xml:
上面代码userService.page(),使用的是mybatis-plus的page()方法,所以我们重写BaseMapper的selectPage()方法的SQL语句就可以了。

<mapper namespace="com.test.UserMapper">

    <select id="selectPage" resultType="com.test.User">
        SELECT u.*,p.position_name FROM `user` u LEFT JOIN `position` p ON p.position_no=u.position_no ${ew.customSqlSegment}
    select>
    
mapper>

这样就实现了演示场景的需求。验证一下,输入入参:

{
  "current": 1,
  "param": {
    "name": "张",
    "positionName": "boss"
  },
  "size": 10
}

响应数据:

{
  "success": true,
  "message": "返回成功",
  "code": 200,
  "data": {
    "records": [
      {
        "id": 1087982257332887600,
        "name": "张靓颖",
        "age": 40,
        "email": "[email protected]",
        "managerId": null,
        "createTime": "2021-03-31T18:15:54",
        "positionName": "大boss"
      }
    ],
    "total": 0,
    "size": 10,
    "current": 1,
    "orders": [],
    "optimizeCountSql": true,
    "hitCount": false,
    "countId": null,
    "maxLimit": null,
    "searchCount": true,
    "pages": 0
  },
  "timestamp": 1617188664412
}

SQL语句:

SELECT u.*,p.position_name FROM user u LEFT JOIN position p ON p.position_no=u.position_no 
WHERE u.name LIKE '张%' AND p.position_name LIKE '%boos%';

2、自定义分页查询接口

上面我们是通过在xml重写了mybatis-plus的page()方法来实现多表分页查询,如果我们不想破坏mybatis-plus的page()方法,就想自己定义一个分页查询可以吗?当然可以。
Mapper 接口:

public interface UserMapper extends BaseMapper<User> {

    List<User> selectUserPage(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
    
}

Mapper xml:

<mapper namespace="com.test.UserMapper">

    <select id="selectUserPage" resultType="com.test.User">
        SELECT u.*,p.position_name FROM `user` u LEFT JOIN `position` p ON p.position_no=u.position_no ${ew.customSqlSegment}
    select>
    
mapper>

IUserService接口:

public interface IUserService extends IService<User> {

	Page<User> listPage(IPage<User> page, QueryWrapper<User> qw);
	
}

UserServiceImpl实现类:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

	@Override
	public Page<User> listPage(IPage<User> page, QueryWrapper<User> qw) {
	    return userMapper.selectUserPage(page, qw);
	}
	
}

3、${ew.customSqlSegment}的使用

上面两个示例中都有使用${ew.customSqlSegment} ,可以用来替换xml里拼接where子句,同理,我们写一些特殊的自定义SQL语句时也可以这么用。

假设场景:比如我们想查询职位是艺人、姓名包含雨、年龄小于等于31岁且邮箱不为空的用户。

Mapper 接口:

public interface UserMapper extends BaseMapper<User> {

    List<User> selectListByWrapper(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
    
}

Mapper xml:

<mapper namespace="com.test.UserMapper">

    <select id="selectListByWrapper" resultType="com.test.User">
        SELECT u.*,p.position_name FROM `user` u LEFT JOIN `position` p ON p.position_no=u.position_no ${ew.customSqlSegment}
    select>
    
mapper>

查询:

// 让 JUnit 运行 Spring 的测试环境, 获得 Spring 环境的上下文的支持
@RunWith(SpringRunner.class)
// 获取启动类,加载配置,确定装载 Spring 程序的装载方法,它回去寻找 主配置启动类(被 @SpringBootApplication 注解的)
@SpringBootTest(classes = DemoApplication.class)
@Slf4j(topic = "MyTest")
public class MyTest {

    @Resource
    private UserMapper userMapper;

    @Test
    public void method1() {
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq("p.position_name", "艺人");
        qw.like("u.name", "雨").le("u.age", 31);
        qw.isNotNull("u.email");
        List<User> userList = userMapper.selectListByWrapper(qw);
        userList.forEach(System.out::println);
    }

}

输出结果:

User(id=1094590409767661570, name=张雨琪, age=31, [email protected], managerId=1088248166370832385, createTime=2021-03-31T18:15:54, positionName=艺人)


扩展1:Update语句与${ew.customSqlSegment}结合的使用

这个只是演示示例,实际使用可以灵活调整。

@Test
public void method1() {
    // 条件构造器
    QueryWrapper<User> qw = new QueryWrapper();
    qw.eq("name", "张靓颖");

    // 修改后的对象
    User user = new User();
    user.setAge(18);
    user.setEmail("[email protected]");

    int i = userMapper.updateByWrapper(user, qw);
    System.out.println(i);
}

Mapper 接口:

public interface UserMapper extends BaseMapper<User> {

    int updateByWrapper(@Param(Constants.ENTITY) User entity, @Param(Constants.WRAPPER) Wrapper<User> updateWrapper);
}

Mapper xml:

<mapper namespace="com.test.UserMapper">

	<update id="updateByWrapper">
        UPDATE user SET age=#{et.age},email=#{et.email} ${ew.customSqlSegment}
    update>
    
mapper>

对应的SQL语句:

UPDATE user SET age=18,email='[email protected]' WHERE name = '张靓颖'

扩展2:跟${ew.sqlSegment}等价

Mapper 接口:

public interface UserMapper extends BaseMapper<User> {

    List<User> selectListByWrapper(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);
    
}

Mapper xml:

<mapper namespace="com.test.UserMapper">

    <select id="selectListByWrapper" resultType="com.test.User">
        SELECT u.*,p.position_name FROM `user` u LEFT JOIN `position` p ON p.position_no=u.position_no 
        <where>${ew.sqlSegment}where>
    select>
    
mapper>

4、逻辑删除(or软删除)的使用

DB有些数据 我们希望做逻辑删除,通常会加一个列,比如:deleted是否删除:0否1是。这个逻辑删除字段,mybatis-plus可以帮助我们自动管理,比如:调用mybatis-plus的删除方法时就会改变硬删除为软删除;调用mybatis-plus的查询、修改方法时会自动在where语句后面加上“AND deleted=0”。
那么怎么让mybatis-plus知道deleted就是逻辑删除字段,0、1是删除标识?

方式一:在配置文件设置全局配置

logging:
  level:
   com.test*: debug
mybatis-plus:
  mapper-locations: classpath*:mybatis/mapper/**/*.xml
  global-config:
    db-config:
      #过滤查询条件空字符串和NULL
      select-strategy: not_empty
      #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
      field-strategy: 2
      logic-delete-field: deleted  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

方式二:字段添加@TableLogic注解局部设置

/**
  * 是否删除: 0 未删除, 1已删除
  */
 @TableLogic
 private Integer deleted;

@TableLogic注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableLogic {

    /**
     * 默认逻辑未删除值(该值可无、会自动获取全局配置)、逻辑未删除值
     */
    String value() default "";

    /**
     * 默认逻辑删除值(该值可无、会自动获取全局配置)、逻辑删除值
     */
    String delval() default "";
}


扩展1:如果两种方式都使用了,以@TableLogic为准

可以理解为就近原则。源码见:

@Getter
@ToString
@EqualsAndHashCode
@SuppressWarnings("serial")
public class TableFieldInfo implements Constants {

	/**
	  * 逻辑删除初始化
	  *
	  * @param dbConfig 数据库全局配置
	  * @param field    字段属性对象
	  */
	 private void initLogicDelete(GlobalConfig.DbConfig dbConfig, Field field, boolean existTableLogic) {
	     /* 获取注解属性,逻辑处理字段 */
	     TableLogic tableLogic = field.getAnnotation(TableLogic.class);
	     if (null != tableLogic) {
	         if (StringUtils.isNotBlank(tableLogic.value())) {
	             this.logicNotDeleteValue = tableLogic.value();
	         } else {
	             this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
	         }
	         if (StringUtils.isNotBlank(tableLogic.delval())) {
	             this.logicDeleteValue = tableLogic.delval();
	         } else {
	             this.logicDeleteValue = dbConfig.getLogicDeleteValue();
	         }
	         this.logicDelete = true;
	     } else if (!existTableLogic) {
	         String deleteField = dbConfig.getLogicDeleteField();
	         if (StringUtils.isNotBlank(deleteField) && this.property.equals(deleteField)) {
	             this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
	             this.logicDeleteValue = dbConfig.getLogicDeleteValue();
	             this.logicDelete = true;
	         }
	     }
	 }

}

扩展2:删除字段加@TableField(exist = false)

@TableField(exist = false)
private Integer deleted;

如果删除字段加上@TableField(exist = false),会造成执行mybatisPlush的removeById(id),无差别的执行了delete的SQL操作,即物理删除。

DELETE FROM t_user WHERE id=?

如果没有加,那么执行mybatisPlush的removeById(id)时,会转换成update的SQL操作,即逻辑删除。

UPDATE t_user SET deleted=1 WHERE id=? AND deleted=0

5、@TableField注解的使用

使用场景一:标识扩展字段
比如上面的示例,我们查询用户列表时想包含职位名称,但user表没有position_name,如果直接在User对象添加positionName字段,那insert、update操作时就会报错,提示没有该列。怎么办?
在positionName字段上添加@TableField(exist = false),标识这是一个扩展字段。

/**
 * 职位名称
 */
@TableField(exist = false)
private String positionName;

使用场景二:查询时排除列
比如上面的示例,我们查询用户数据时不需要查询deleted字段,那么通过@TableField(select = false)标识该字段,mybatis-plus在生成select语句时就不会包含deleted。

/**
 * 是否删除:0否1是,默认0
 */
@TableField(select = false)
private Integer deleted;

6、查询指定的列或者排除指定的列

查询指定的列:
qw.select(“name, email”) 指定只查询name, email。

@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.select("name, email");
    qw.and(w -> {
        w.likeLeft("email", "@baomidou.com").or().likeLeft("email", "@qq.com");
    });
    qw.orderByDesc("create_time");
    qw.last("LIMIT 1");
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT name,email FROM user

排除指定的列:
方式一:上面的示例中,在字段上添加@TableField(select = false) 标识该列不参与查询;
方式二:使用select(Class entityClass, Predicate predicate) 方法,除了create_time、manager_id 其他列都查询。

@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.select(User.class, p -> !p.getColumn().equals("create_time") && !p.getColumn().equals("manager_id"));
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT id,name,age,email FROM user

7、where子句嵌套查询条件

假设场景:查询最新录入的一条邮箱是“@baomidou.com”或者“@qq.com”结尾的艺人的姓名和邮件。

@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.select("name, email");
    qw.eq("position_no", "P003");
    qw.and(w -> {
        w.likeLeft("email", "@baomidou.com").or().likeLeft("email", "@qq.com");
    });
    qw.orderByDesc("create_time");
    qw.last("LIMIT 1");
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

分析:
1.通过qw.select()选择只查询用户姓名和邮件;
2.qw.eq(“position_no”, “P003”);过滤艺人;
3.qw.and(w -> {xxx});实现邮箱“或者”关系的嵌套;
4. w.likeLefe()实现邮件后缀的匹配;
5. qw.orderByDesc()根据创建时间倒序结合LIMIT 1,实现或者最新一条;


对应的SQL语句:

SELECT u.name, u.email FROM `user` u 
WHERE u.position_no='P003' 
AND (u.email LIKE '%@baomidou.com' OR u.email LIKE '%@qq.com') 
ORDER BY u.create_time DESC 
LIMIT 1;

上面嵌套的另一种写法:
@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.select("name, email");
    qw.nested(w -> {
        w.likeLeft("email", "@baomidou.com").or().likeLeft("email", "@qq.com");
    });
    qw.eq("position_no", "P003");
    qw.orderByDesc("create_time");
    qw.last("LIMIT 1");
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT u.name, u.email FROM `user` u 
WHERE (u.email LIKE '%@baomidou.com' OR u.email LIKE '%@qq.com') 
AND u.position_no='P003' 
ORDER BY u.create_time DESC 
LIMIT 1;

如果不用qw.and(w -> {xxx});嵌套,
@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.select("id, name, email");
    qw.eq("position_no", "P003");
    qw.likeLeft("email", "@baomidou.com").or().likeLeft("email", "@qq.com");
    qw.orderByDesc("create_time");
    qw.last("LIMIT 1");
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

SQL语句就变成:

SELECT u.name, u.email FROM `user` u 
WHERE u.position_no='P003' 
AND u.email LIKE '%@baomidou.com' OR u.email LIKE '%@qq.com'
ORDER BY u.create_time DESC 
LIMIT 1;

qw.and(w -> {xxx});嵌套里面还可以通过for循环动态拼接where子句。
@Test
public void method1() {
    List<Long> ids = Arrays.asList(new Long[]{1087982257332887553L, 1088248166370832385L, 1088250446457389058L});

    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.eq("position_no", "P003");
    qw.and(w -> {
        for (Long id : ids) {
            w.or().eq("id", id);
        }
    });
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT id,name,age,email,manager_id,create_time FROM USER 
WHERE position_no ='P003' 
AND (id = '1087982257332887553' OR id = '1088248166370832385' OR id = '1088250446457389058')

8、where子句IN子查询

假设场景:查询年龄在20至30岁的艺人。

@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.between("age", 20, 30);
    qw.inSql("position_no", "SELECT p.position_no FROM `position` p WHERE p.position_name='艺人'");
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT id,name,age,email,manager_id,create_time FROM user
WHERE age BETWEEN 20 AND 40 AND position_no IN (SELECT p.position_no FROM position p WHERE p.position_name='艺人')

或者先获取职位编码后直接使用mybatis-plus的in()。
@Test
public void method1() {
    List<String> positionNos = Arrays.asList("P002", "P003");
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.between("age", 20, 30);
    qw.in("position_no", positionNos);
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

9、where子句使用SQL函数

数据create_time是包含年月日时分秒的,现在通过日期匹配。

@Test
public void method1() {
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.apply("date_format(create_time,'%Y-%m-%d') = {0}", "2021-03-31");
    List<User> userList = userMapper.selectList(qw);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT id,`name`,age,email,manager_id,create_time FROM `user` 
WHERE DATE_FORMAT(create_time,'%Y-%m-%d') = '2021-03-31'

10、除了QueryWrapper还可以用Map作为参数

@Test
public void method1() {
    Map<String, Object> params = new HashMap<>();
    params.put("name", "张靓颖");
    List<User> userList = userMapper.selectByMap(params);
    userList.forEach(System.out::println);
}

对应的SQL语句:

SELECT id,name,age,email,manager_id,create_time FROM user
WHERE name = '张靓颖'

11、service的batch批量操作方法

mybatis-plus中QueryWrapper的使用技巧_第2张图片


12、service的getOne方法

getOne()的默认用法在返回多条记录时会报错,mybatis-plus的service提供了解决方法。
getOne()的第二个参数传false就可以解决,如果有多条记录取第一条返回。

@Test
public void method1() {
    // 等同于
    // QueryWrapper qw = new QueryWrapper();
    // qw.eq("position_no", "P003");

    User user = userService.getOne(Wrappers.<User>lambdaQuery().eq(User::getPositionNo, "P003"),false);
    System.out.println(user);
    
}

13、基于lambda的相关操作

/**
 * lambda查询
 */
@Test
public void lambdaQuery(){
    List<User> list = userService.lambdaQuery().eq(User::getAge, 18).list();
    list.forEach(System.out::println);
}

/**
 * lambda修改
 */
@Test
public void lambdaUpdate(){
    boolean update = userService.lambdaUpdate().eq(User::getAge, 18).set(User::getAge, 31).update();
    System.out.println(update);
}

/**
 * lambda删除
 */
@Test
public void lambdaRemove(){
    boolean remove = userService.lambdaUpdate().eq(User::getAge, 18).remove();
    System.out.println(remove);
}

14、乐观锁的使用

参考地址:https://jiannan.blog.csdn.net/article/details/101025174


15、字段为空update修改不生效的问题

Mybatis-plus默认的字段策略是:

mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  global-config:
    db-config:
      #字段策略,默认非NULL判断
      field-strategy: not_null

此时,调用Mybatis-plus的update()或者updateById()时,如果对象的某个字段为NULL,就不会更新这个字段。
解决方式:
1、修改全局配置,如上面的yaml配置;
2、修改该字段的配置策略,如:

@TableField(updateStrategy = FieldStrategy.IGNORED)
private LocalDateTime operateTime;

@TableField(updateStrategy = FieldStrategy.IGNORED)
private String operateUserId;

这样就能忽略了为NULL的判断,此时operateTime、operateUserId为NULL,调用修改操作后对应数据库的列就是null了。


16、Wrappers.query()使简单查询简化

需求:根据userNo查询userStatus

传统写法:

QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("user_status");
qw.eq("user_no", userNo);
User entity = baseMapper.selectOne(qw);

传统链式写法:

User entity = baseMapper.selectOne(new QueryWrapper<User>().select("job_status").eq("user_no", userNo));

使用Wrappers.query()简化后的写法:

User entity = baseMapper.selectOne(Wrappers.<User>query().select("user_status").eq("user_no", userNo));


后面还会慢慢补充

你可能感兴趣的:(Mybatis,mybatis)