mybatis-plus中QueryWrapper的使用技巧

MyBatis-Plus QueryWrapper字段控制技巧与优化 在数据库查询优化中,字段选择是提升性能的关键技术之一。通过精确控制返回字段,可以减少数据传输量、提高查询效率,同时保护敏感数据安全。MyBatis-Plus框架的QueryWrapper组件提供了select()方法,支持包含/排除字段、动态字段选择等灵活配置方式。该技术特别适用于分页查询、敏感数据保护等场景,配合索引优化可显著提升系统性能。在实际开发中,合理使用字段控制功能既能满足DTO转换需求,又能避免常见的字段映射问题,是Java后端开发必备的SQL优化手段。 阅读详情
AI摘要

本文介绍MyBatis-Plus的高级应用场景,包括分页查询多表数据、自定义分页查询接口、使用${ew.customSqlSegment}

前言: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



演示表、数据和相关类

#职位表
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, 'boss@baomidou.com', 'P001', NULL, NOW()),
(1088248166370832385, '陶子', 25, 'taozi@baomidou.com', 'P002', 1087982257332887553, NOW()),
(1088250446457389058, '李艺伟', 28, NULL,  'P003',1088248166370832385, NOW()),
(1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 'P003', 1088248166370832385, NOW()),
(1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 'P003', 1088248166370832385, NOW());

在这里插入图片描述

在这里插入图片描述

/**
 *  实体类
 */
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": "boss@baomidou.com",
        "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=zjq@baomidou.com, 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("boss@baomidou.com");

    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='boss@baomidou.com' 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批量操作方法

在这里插入图片描述


12、service的getOne方法

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

@Test
public void method1() {
    // 等同于
    // QueryWrapper<User> 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.youkuaiyun.com/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-Plus QueryWrapper的apply和last方法实战:IP地址比较与分页优化技巧 本文深入探讨MyBatis-Plus中QueryWrapper的apply和last方法实战应用,重点解析IP地址比较与分页优化技巧。通过实际代码示例展示如何利用apply处理复杂SQL条件(如IP地址转换、地理空间查询等),以及使用last方法实现高效分页和SQL尾部追加。同时强调安全使用原则,避免SQL注入风险,为开发者提供MyBatis-Plus复杂查询的最佳实践方案。 阅读详情

相关推荐

点云入门笔记(一):点云数据及获取

点云概念: 点云是某个坐标系下的点的数据集。点包含了丰富的信息,包括三维坐标 X,Y,Z、颜色、分类值、强度值、时间等等。

wc19862274581的博客 2万+

MybatisPlus--QueryWrapper

QueryWrapper wrapper介绍 Wrapper : 条件构造抽象类,最顶端父类 AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件 QueryWrapper : Entity 对象封装操作类,不是用lambda语法 UpdateWrapper : Update 条件封装,用于Entity对象更新操作 AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。 Lambda

weixin_45822542的博客 5851

Stm32L0 STM32CUBE中UART和使用LPUART1以及串口不进中断问题解决

/* 开启串口1中断 */ LL_USART_EnableIT_RXNE(LPUART1); /* 开启串口2中断 */ LL_USART_EnableIT_RXNE(USART2);

シ❤゛甜虾的个人博客 7686

Mybatis-plus 动态条件查询QueryWrapper的使用

Mybatis-plus 动态条件查询QueryWrapper的使用

wounler的博客 1万+

数据中心光模块升级实录:从200G QSFP56到400G QSFP-DD的踩坑指南

本文记录了数据中心从200G QSFP56升级至400G QSFP-DD光模块的完整实战经验。文章深入剖析了升级过程中面临的兼容性、链路预算、布线、散热与功耗管理等核心挑战,并提供了具体的解决方案与排坑指南,为高速网络升级提供了宝贵的实践参考。

butter的博客 479

全面分析总结解决Could not transfer artifact org.apache.maven.plugins等此类问题的方法

全面分析总结解决Could not transfer artifact org.apache.maven.plugins,Could not transfer artifact xxx from/to xxx等此类问题的方法

ccjjjjdff的博客 1万+

MyBatisPlus的基本使用之QueryWrapper

使用来设置查询条件,而不是使用Lambda表达式。

qq_43513957的博客 6138

【训练】Qwen2.5VL 多机多卡 Grounding Box定位

之前的相关文章:【深度学习】LLaMA-Factory微调sft Qwen2-VL进行印章识别使用LLaMA-Factory微调sft Qwen2-VL-7B-Instruct构建最新的LLaMA-Factory镜像。

q742971636的博客 2583

Mybatis-plus及QueryWrapper使用

mybatis-plus

哈哈哈哈 5286

基于人脸识别的智能门禁系统设计

在当前的技术背景下,本文精心打造了一款前沿的视频识别门禁控制系统。在进行调试的时候使用的软件是Keil,编写完代码,生成文件后用下载器下载到单片机上,就能够进行程序的调试了,本次设计实现了预先设定的目标,达到了预期的效果。本次设计的人脸识别的门禁控制系统其硬件部分是以STM32F103C8T6为核心的单片机,采用0V2640摄像头对人员的人脸识别,采用矩阵按键对系统的操作以及密码的输入,使用蜂鸣器来作为报警模块,开门装置是电子锁控制,显示设备采用OLED液晶显示,无线模块在一番思索后选择了蓝牙模块传输。

2401_86147581的博客 1296

51单片机入门指南-基于STC89C52(持续更新)

一、环境介绍 编程IDE: keil5 单片机型号: STC89C51/52 开发板: 普中科技的开发板 完整PDF资源下载地址:https://download.youkuaiyun.com/download/xiaolong1126626497/19785856 二、51单片机开发手册介绍 2.1 前言 51单片机是对所有兼容Intel 8031指令系统的单片机的统称。 该系列单片机的始祖是Intel的8004单片机,后来随着Flash rom技术的发展,8004单片机取得了长足的进展,成为应用最.

8797

Mybatis-plus中QueryWrapper的使用

QueryWrapper就是在使用Mybatis-plus中真实用到的一种技术,能简化sql操作,QueryWrapper其实可以理解成一个放查询条件的盒子,我们把查询条件放在里面,他就会自动按照对应的条件进行查询数据。

whr 1万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

mybatis-plus QueryWrapper json 查询技巧

mybatis-plus QueryWrapper json 查询

qq_38632639的博客 4392

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

MyBatis-Plus高效开发指南:从BaseMapper到QueryWrapper实战技巧

苞米豆团队开发MyBatis-Plus 已连续 5 年()获得“OSC 年度最受欢迎中国开源软件”学习背景:拥有 Java 开发环境以及相应 IDE熟悉 Spring Boot熟悉 Maven 或 Gradle功能:快速生成 Mapper 、 Model 、 Service 、 Controller 层代码#BaseMapper使用#自动主键生成:内含分布式唯一 ID 生成器 - Sequence#批量插入#条件构造器:少量配置即可实现单表大部分 CRUD 操作#逻辑删除实现。

weixin_53538280的博客 775

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

MyBatis-Plus条件构造器进阶:QueryWrapper的5个隐藏技巧让你轻松应对复杂SQL

本文深入探讨了MyBatis-Plus中QueryWrapper的高级应用,揭示了5个隐藏技巧以应对复杂SQL场景。重点介绍了如何使用apply方法实现任意SQL片段注入、灵活构建动态条件、优雅集成子查询以及处理聚合分组查询,帮助开发者摆脱XML,在保持类型安全的同时高效处理复杂业务逻辑。

gg901的博客 595

Mybatis-Plus中LambdaQueryWrapper与QueryWrapper的使用

在开发过程中,几乎每个需求都需要写一些最基础的crud操作,这在mapper和xml文件中会产生大量重复的代码,引入mybatis-plus可以解决最简单的crud以有效缓解,但大部分人在一些比较复杂的查询操作以及考虑sql优化的场景下依旧选择手写xml文件,但是mybatis-plus并不是无法解决这个问题。关于连表查询,我能想到的就是sql中使用数据量小的表来连接数据量大的表,以减少索引扫描循环的次数,以及想使用or连接条件时,需要前后字段都存在索引;两个参数中前者是表中的列名,后者是查询的条件。

lin_silvery的博客 7136

开发避坑指南(44):Mybatis-plus QueryWrapper and()拼接嵌套复杂条件的技巧

Mybatis-plus 的查询条件构造器QueryWrapper要构造and条件子句里面有or条件子句或者嵌套其他复杂条件,该怎么实现?目标语句如下:AND (push_status = ‘N’ OR push_status IS NULL)

真战 467

【Mybatis-Plus篇】Mybatis-Plus基本使用

简单快速写出业务sql

优快云站内信: https://i.youkuaiyun.com/#/msg/chat/qyj19920704 1万+

MyBatis-Plus条件构造器QueryWrapper深度指南:告别手写SQL的烦恼

在日常Java开发中,SQL编写一直是开发者面临的痛点之一。传统的MyBatis虽然提供了强大的SQL映射能力,但仍然需要手动编写大量SQL语句,这不仅容易出错,还难以维护。MyBatis-Plus的QueryWrapper条件构造器正是为了解决这一痛点而生,它通过链式API让开发者能够以面向对象的方式构建复杂的查询条件,彻底告别手写SQL的烦恼。 通过本文,你将掌握: - QueryWrapp...

gitblog_00204的博客 1098

SpringBoot使用Mybatis-Plus

MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,它在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

培根芝士的专栏 3872

mybatis-plus 查询 QueryWrapper 带嵌套子查询

QueryWrapper<Source> queryWrapper = new QueryWrapper(); if (StrUtil.isNotEmpty(filter)) { queryWrapper.or(i->i.like("name", filter).or().like("description", filter)); } if (StrUtil.isNotEmpty(catalog)) { queryWrapper.eq("catalog_id", .

HouYing 3万+

【MyBatis-Plus系列】QueryWrapper中or的使用

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨。

优快云站内信: https://i.youkuaiyun.com/#/msg/chat/qyj19920704 1万+

mybatis-plus条件构造器QueryWrapper实例

我们再搞几个实例来深入理解下条件构造器QueryWrapper的用法: 案例一: 查询姓李的,并且出生日期范围是1993-02-09到1994-04-09的员工 sql实现: SELECT * FROM t_employee WHERE DATE_FORMAT(birthday,'%Y-%m-%d')>='1993-02-09' AND DATE_FORMAT(birthday,'%Y-%m-%d')<='1994-04-09' AND NAME LIKE '李%' mp实现: /** *

java1234的博客 1694

mybatis plus 关联查询_这些Mybatis-Plus的使用小技巧,你应该知道的

推荐学习真香警告!Alibaba珍藏版mybatis手写文档,刷起来 重识SSM,“超高频面试点+源码解析+实战PDF”,一次性干掉全拿走 最近在用 Mybatis-Plus,嗯,真香!!!今天就来说说 Mybatis-Plus 的那些使用技巧1、条件查询(QueryWrapper)如果说,我们需要查询的 SQL 语句如下:SELECT * FROM user_info WHERE age = 2...

weixin_39624332的博客 1140

mybatis-plus的条件查询

gt 大于> ge 大于等于>= lt 小于< le 小于等于<= QueryWrapper<A> queryWrapper = new QueryWrapper<A>(); if (StringUtils.isNotBlank(mediaSource)) { queryWrapper.eq("m ", m ); } if (StringUtils.isNotBlank(t )) { quer..

java技术专家全栈成长之路 899
上一篇: 手动同步Chrome(解决Chrome不能自动同步的问题)
下一篇: Spring事务的7种传播属性
pbxs
博客等级 码龄14年 94粉丝 62原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值