MyBatis-plus 自动生成 条件构造器 QueryWrapper

背景

在使用Wrapper构建条件时,经常因为需要构建的条件过多需要写半个多小时,还容易粗心写错字段,所以就想搞个可以直接自动构建QueryWrapper的工具类。

思路

我们需要一个方法,能够根据条件参数对象的属性值来设置QueryWrapper的条件,同时需要返回一个我们所需要对应范型的QueryWrapper。

使用范型来确定返回的QueryWrapper的范型,使用反射来获取参数类中的属性值,同时需要区分字段的类型来确定选择该字段使用哪种查询方式。

实现

再获取字段名的时候有可能参数类的字段名不能直接根数据库表的字段名直接对应上,所以我直接复用了@TableField 注解,若是字段上有该注解就直接使用其对应的value属性值,若是没有该注解则就使用字段名,将字段名的驼峰命名法转换成下划线分割的格式。

​​​​​​​

    /**     * 创建对应的wrapper     * @param param 参数对象     * @param <T> 范型     * @return 创建好的wrapper     */    public static <T> QueryWrapper<T> createWrapper(Object param) {        QueryWrapper<T> wrapper = new QueryWrapper<>();        Class<?> dataClass = param.getClass();        try {            for (Field field : dataClass.getDeclaredFields()) {                field.setAccessible(true);                //获取字段类型和字段值                Class<?> type = field.getType();                Object value = field.get(param);                if (value == null){                    continue;                }                //将字段名转为数据库中的字段名                String fieldName;                TableField tableFieldAnnotation = field.getAnnotation(TableField.class);                if (tableFieldAnnotation != null) {                    fieldName = tableFieldAnnotation.value();                }else{                    fieldName = convertToSnakeCase(field.getName());                }                //根据字段的类型来选择不同的查询方式                if (type == String.class) {                    wrapper.like(fieldName, value);                }else if (type == Long.class){                    wrapper.eq(fieldName, value);                }else if (type == List.class){                    wrapper.in(fieldName, (List<?>)value);                }else if (type == Between.class){                    Between val= (Between)value;                    wrapper.between(fieldName, val.getStart(), val.getEnd());                }else{                    wrapper.eq(fieldName, value);                }            }        } catch (IllegalAccessException e) {            e.printStackTrace();        }        return wrapper;    }    /**     * 将驼峰命名法的字段名转为下划线隔开的形式     * @param input 驼峰命名法的字段名     * @return 下划线隔开的数据库字段名     */    private static String convertToSnakeCase(String input) {        StringBuilder output = new StringBuilder();        for (int i = 0; i < input.length(); i++) {            char ch = input.charAt(i);            if (Character.isUpperCase(ch)) {                if (i > 0) {                    output.append(StrPool.C_UNDERLINE);                }                output.append(Character.toLowerCase(ch));            } else {                output.append(ch);            }        }        return output.toString();    }

测试

可以看到dto参数对象的属性值都已经填充到了QueryWrapper中

图片

图片

<think>嗯,用户想详细了解MyBatis-Plus条件构造器QueryWrapper的用法。让我先回顾一下之前的对话和提供的引用内容。用户之前的问题是关于mapper-locations配置的,现在转到了条件构造器,特别是QueryWrapper。我需要确保回答的结构清晰,涵盖基本用法、链式调用、条件组合、排序分页、Lambda表达式对比,以及常见问题。首先,引用[1]、[2]、[3]都提到了Wrapper的三种类型,其中QueryWrapper用于查询条件。引用[4]和[5]给出了具体的代码示例,尤其是QueryWrapper和LambdaQueryWrapper的区别。用户可能需要知道如何构建各种查询条件,比如eq、like、between等,以及如何组合条件。接下来,我需要分步骤解释QueryWrapper的用法。从创建实例开始,然后逐个添加条件方法,比如eq、likein等。链式调用是关键点,需要强调其可读性和维护性。同时,要提到条件优先级,比如and和or的使用,引用[4]中的例子展示了嵌套的and方法,需要详细说明。 然后,排序和分页也是常用功能,需要包括orderBy和last方法的应用。不过要注意last方法可能带来的SQL注入风险,提醒用户谨慎使用。比较QueryWrapper和LambdaQueryWrapper的区别也很重要,引用[5]提到列名的不同处理方式,需要指出Lambda的类型安全优势,但QueryWrapper在动态场景下的灵活性。 可能还需要提到多条件组合时的逻辑,比如and和or的嵌套,以及如何处理复杂的查询结构。例如,用户可能需要同时满足多个条件,或者其中一个条件满足即可,这时候需要正确使用括号来组合条件。 另外,用户可能在实际使用中遇到条件不生效的问题,这时候需要检查是否错误地使用了链式调用,或者条件字段名是否正确。例如,数据库字段名是否与实体类属性对应,尤其是在使用下划线命名的情况下。最后,生成相关问题,比如如何解决条件优先级问题,或者动态条件构建的方法,帮助用户进一步学习。确保引用标记正确,比如链式调用的部分引用[2],条件构造器的类型引用[3],Lambda的对比引用[5]等。总结下来,回答的结构应该包括基本用法、常用方法、链式调用、条件组合、排序分页、与Lambda的对比、注意事项,以及相关问题。需要确保每个部分都有代码示例,并正确引用提供的资料。</think>### MyBatis-Plus 条件构造器 QueryWrapper 详解 #### 1. **基本概念与核心作用** QueryWrapperMyBatis-Plus 提供的查询条件封装类,用于**动态构建 SQL 查询条件**,避免手动编写 XML 或注解中的复杂条件逻辑[^1]。其核心优势在于: - **链式调用**:通过方法链式组合条件,提升代码可读性[^2]; - **类型安全**:支持 Lambda 表达式(LambdaQueryWrapper),减少字段名硬编码错误; - **防 SQL 注入**:内部自动处理参数化查询,提升安全性[^2]。 #### 2. **核心方法与使用场景** ##### 2.1 基础条件构建 ```java QueryWrapper<User> wrapper = new QueryWrapper<>(); // 等于条件 wrapper.eq("age", 25); // WHERE age = 25 // 模糊查询 wrapper.like("name", "张"); // WHERE name LIKE '%张%' // 范围查询 wrapper.between("create_time", startDate, endDate); // WHERE create_time BETWEEN ? AND ? // IN 查询 wrapper.in("status", Arrays.asList(1, 3, 5)); // WHERE status IN (1,3,5) ``` [^4] ##### 2.2 链式调用与条件组合 ```java // 组合 AND/OR 条件 wrapper.gt("age", 18) .and(w -> w.like("email", "@example.com").or().isNotNull("phone")) .orderByDesc("create_time"); // 对应 SQL: // WHERE age > 18 AND (email LIKE '%@example.com%' OR phone IS NOT NULL) ORDER BY create_time DESC ``` [^4] ##### 2.3 动态条件(空值过滤) ```java User user = new User(); user.setName("李四"); user.setStatus(null); // 状态为 null 时自动忽略该条件 QueryWrapper<User> wrapper = new QueryWrapper<>(user) .eq(StringUtils.isNotBlank(user.getName()), "name", user.getName()) .eq(user.getStatus() != null, "status", user.getStatus()); // 若 user.name 非空且 status 为 null,生成 SQL: WHERE name = '李四' ``` [^5] #### 3. **复杂条件与嵌套逻辑** ##### 3.1 嵌套 AND/OR 条件 ```java wrapper.likeRight("name", "王") .and(w -> w.eq("gender", 1).or().gt("age", 30)); // SQL: WHERE name LIKE '王%' AND (gender = 1 OR age > 30) ``` [^4] ##### 3.2 子查询支持 ```java wrapper.inSql("department_id", "SELECT id FROM department WHERE name = '研发部'"); // SQL: WHERE department_id IN (SELECT id FROM department WHERE name = '研发部') ``` #### 4. **排序与分页** ```java // 排序 wrapper.orderByAsc("age", "create_time"); // ORDER BY age ASC, create_time ASC // 分页(需配合 Page 对象) Page<User> page = new Page<>(1, 10); userMapper.selectPage(page, wrapper); ``` [^4] #### 5. **QueryWrapper 与 LambdaQueryWrapper 对比** | **特性** | **QueryWrapper** | **LambdaQueryWrapper** | |----------------|---------------------------|----------------------------------| | 字段引用方式 | 字符串(易错) | 方法引用(类型安全) | | 动态条件 | 灵活但需手动判空 | 支持链式动态过滤 | | 适用场景 | 简单查询或动态字段名场景 | 复杂条件且需类型安全的场景 | ```java // LambdaQueryWrapper 示例 LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>(); lambdaWrapper.eq(User::getAge, 25).like(User::getName, "张"); ``` [^5] #### 6. **常见问题与解决** - **条件未生效**:检查字段名是否与数据库列名一致(区分大小写和下划线规则)[^5]; - **动态条件失效**:确保使用 `.eq(condition, column, value)` 格式,且 `condition` 为布尔值; - **性能问题**:避免在循环中频繁创建 QueryWrapper 对象。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值