Mybatis 动态传入order by 参数排序无效

本文介绍了在MyBatis中使用动态orderby参数进行排序时遇到的问题及解决方案。通过将#{}

                                                               Mybatis 动态传入order by 参数排序无效

一、背景

        2019年4月14日,下午5点半,今天周末,但依旧加点调程序,补补丁。遇到这个坑,耗了我一会,我也很无奈啊,还好当时就解决了,特此记录一下。

        对最近比较热门的 996.ICU 话题表示没有多余的精力去了解,why?因为我还在马不停蹄的干活、搬砖,心疼三秒钟。虽然当前的日子苦了一些,但依旧对以后的生活的充满了希望,对当前的工作充满了激情。始终相信我们程序员也可以按照劳动法正常上下班,我们也有享受生活的权利,我们是一群可爱的人。

二、问题解决

   在相应的映射文件里面,将 #{} 改成 ${}  就行了,其他的不用做任何变动。

   原来是这样的(错误的

<if test="orderByString != null" >
    order by #{orderByString}
</if>

  应该要改成这样的(正确的) 

<if test="orderByString != null" >
    order by ${orderByString}
</if>

三、心得

       周末工作,最主要的是心态要好,由于经过了一周的长时间编程工作,周末有时候难免有些急躁,遇见问题就会有一些烦躁,这还是不可取的,很不利于工作的进行。

      积极工作,认真生活。这世间不仅有充满激情的工作,还有美好的生活。

<think> 我们正在讨论MyBatis动态SQL中如何动态拼接查询条件(特别是列名部分)。参考用户提供的引用内容,尤其是引用[1]中展示了在ORDER BY子句中使用${sort}动态指定排序方式,引用[3][4]展示了<foreach>标签用于动态生成IN条件或批量操作。 用户的问题:如何动态地给查询条件拼接一个引用值? 在MyBatis中,动态拼接查询条件主要有两种方式: 1. 使用`${}`直接替换(字符串替换),适用于非值部分(如表名、列名等),但需注意SQL注入风险。 2. 使用`#{}`预处理参数(安全,防止SQL注入),用于传递值(如条件值)。 但用户特别提到“拼接一个引用值”,这里可能指两种情况: - 情况A:动态指定列名(如之前的例子中${query.feeItem}_weight,即列名的一部分需要动态改变)。 - 情况B:动态构建查询条件的值(比如一个集合,用foreach遍历生成多个条件)。 根据用户上一次的问题(动态列名)和本次问题描述,我们重点讨论动态列名(情况A)和动态值(情况B)的实现方法。 ### 1. 动态列名(表名等非值部分) 使用`${}`语法进行字符串替换。例如,根据传入参数动态选择列: ```xml <select id="selectCheckWeight" resultType="java.lang.Double"> SELECT tiw.${feeItem}_weight AS checkWeight FROM t_iron_waybill tiw WHERE tiw.deleted = &#39;0&#39; </select> ``` 注意:`${feeItem}`会被替换为传入参数值(字符串)。例如,若传入参数`feeItem`的值为"fuel",则生成的SQL为: ```sql SELECT tiw.fuel_weight AS checkWeight FROM t_iron_waybill tiw WHERE tiw.deleted = &#39;0&#39; ``` **风险警告**:直接使用`${}`会有SQL注入风险,因此必须确保传入参数值安全(例如,从预定义的可选值中选取,而不是用户自由输入)。如果参数值来自用户输入且未经严格校验,攻击者可能注入恶意SQL。 ### 2. 动态条件值(安全方式) 对于条件值,通常使用`#{}`。例如,根据传入的id列表查询多个记录(使用`<foreach>`动态生成IN条件): ```xml <select id="selectByIds" resultType="com.example.Entity"> SELECT * FROM table WHERE id IN <foreach collection="idList" item="id" open="(" separator="," close=")"> #{id} </foreach> </select> ``` 这里`#{}`会被预处理为参数占位符(?),然后传入参数值,避免SQL注入。 ### 3. 组合使用(动态列名+动态值) 例如,根据动态列名和动态值进行查询: ```xml <select id="selectByDynamicColumn" resultType="map"> SELECT * FROM t_iron_waybill WHERE ${columnName} = #{value} </select> ``` 注意:`${columnName}`是动态列名(直接替换),`#{value}`是动态值(安全参数)。 ### 4. 使用<if>标签实现条件分支 如果某个条件可能存在也可能不存在,可以用`<if>`标签: ```xml <select id="selectDynamicCondition" resultType="com.example.Entity"> SELECT * FROM t_iron_waybill WHERE deleted = &#39;0&#39; <if test="feeItem != null and feeItem != &#39;&#39;"> AND ${feeItem}_weight > #{minWeight} </if> </select> ``` ### 安全建议 - 对于动态列名、表名等非值部分,无法使用`#{}`(因为`#{}`会添加引号,导致列名无效),所以必须用`${}`。但必须严格控制参数来源,避免用户不可控的输入。 - 可以考虑在应用层进行校验:将传入的列名参数与一个白名单(允许的列名列表)比较,只允许使用预定义的列名。 - 对于条件值,务必使用`#{}`,防止SQL注入。 ### 示例:完整的Mapper接口和XML配置 假设我们有一个方法,根据传入的列名后缀(如"fuel")和最小值查询运单。 **Mapper接口**: ```java public interface IronWaybillMapper { List<Map<String, Object>> selectByFeeItemAndMinWeight( @Param("feeItem") String feeItem, @Param("minWeight") Double minWeight); } ``` **XML映射文件**: ```xml <select id="selectByFeeItemAndMinWeight" resultType="map"> SELECT tiw.${feeItem}_weight AS checkWeight, tiw.* FROM t_iron_waybill tiw WHERE tiw.deleted = &#39;0&#39; <if test="minWeight != null"> AND tiw.${feeItem}_weight > #{minWeight} </if> </select> ``` ### 总结 在MyBatis动态拼接查询条件(包括动态列名和条件值)的方法如下: - 动态列名(非值部分)使用`${param}`(注意SQL注入风险)。 - 条件值使用`#{param}`(安全)。 - 使用动态SQL标签(如`<if>`, `<foreach>`等)构建灵活的条件。 §§相关问题§§ 1. MyBatis中`#{}`和`${}`有什么区别? 2. 如何防止MyBatis动态SQL中的SQL注入攻击? 3. MyBatis有哪些常用的动态SQL标签?请举例说明。 4.MyBatis中,如何实现动态表名查询? </think> ###MyBatis动态拼接查询条件引用值的方法 在 MyBatis动态拼接查询条件引用值,主要通过以下三种方式实现(结合您提供的引用内容): #### 1. **使用 `${}` 直接拼接(慎用)** ```xml <select id="dynamicQuery" resultType="YourResult"> SELECT * FROM table WHERE <!-- 直接拼接列名和值 --> ${columnName} = #{staticValue} OR <!-- 动态列名+动态值 --> ${dynamicColumn} = &#39;${dynamicValue}&#39; </select> ``` - **说明**:`${}` 会直接替换为字符串(引用[1]中的 `ORDER BY balance ${sort}`) - **风险**:存在 SQL 注入漏洞(如引用[4]的 `building_id = #{id}` 更安全) - **适用场景**:动态表名/列名等非用户输入值 #### 2. **使用 `<if>` 标签条件拼接** ```xml <select id="safeDynamicQuery" resultType="YourResult"> SELECT * FROM table WHERE 1=1 <!-- 安全拼接动态条件 --> <if test="param.value != null"> AND ${param.column} = #{param.value} </if> <!-- 引用[3]的遍历逻辑 --> <if test="ids != null"> AND id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} <!-- 安全参数化 --> </foreach> </if> </select> ``` - **优点**:结合 `#{}` 防止 SQL 注入(如引用[2]的 `id = #{id}`) - **动态性**:通过 `test` 属性控制条件是否生效 #### 3. **使用 `<bind>` 预处理复杂值** ```xml <select id="bindExample"> <!-- 预处理动态值 --> <bind name="pattern" value="&#39;%&#39; + keyword + &#39;%&#39;" /> SELECT * FROM table WHERE name LIKE #{pattern} <!-- 安全引用预处理值 --> </select> ``` - **用途**:处理需拼接的LIKE条件、计算表达式等 - **安全**:最终仍通过 `#{}` 传递参数 --- ### ✨ 最佳实践方案(推荐) ```xml <select id="dynamicCondition" resultType="map"> SELECT tiw.* FROM t_iron_waybill tiw WHERE tiw.deleted = &#39;0&#39; <!-- 动态列名 + 动态值(安全组合) --> <if test="query.column != null and query.value != null"> AND ${query.column} = #{query.value} <!-- 列名用${},值用#{} --> </if> <!-- 引用[4]的遍历逻辑处理多值 --> <if test="query.ids != null"> OR id IN <foreach collection="query.ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </if> </select> ``` #### 📝 关键说明: 1. **`${}` vs `#{}`** - 列名/表名等标识符用 `${}`(如 `${feeItem}_weight`) - 条件值必须用 `#{}` 防注入(如引用[2][3]) 2. **动态控制逻辑** - `<if>` 控制条件分支(引用[3]) - `<foreach>` 处理集合值(引用[3][4]) 3. **参数传递** Java 代码传入包含 `column` 和 `value` 的 Map 对象: ```java Map<String, Object> params = new HashMap<>(); params.put("query", Map.of( "column", "fee_weight", "value", 100.50, "ids", Arrays.asList(101, 102, 103) )); sqlSession.selectList("dynamicCondition", params); ``` --- ### ️ 安全提示 1. **禁止用户输入直接作为 `${}` 值** 动态列名应来自预定义白名单(如引用[1]的 `${sort}` 应只允许 "ASC"/"DESC") 2. **复杂场景用 `<bind>`** 如拼接 LIKE 模糊查询: ```xml <bind name="safeKeyword" value="&#39;%&#39; + keyword + &#39;%&#39;"/> WHERE name LIKE #{safeKeyword} ``` 3. **优先选择参数化 `#{}`** 能在 `#{}` 中解决的逻辑不用 `${}`(如引用[2]的 `id = #{id}`) ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值