mybatis-select与update多字段判断拼接-xml写法

本文总结了使用MyBatis进行动态SQL操作,包括在select和update语句中如何根据对象属性进行多字段的条件判断拼接。在查询时,利用`if`标签和`include`实现条件复用,而在更新操作中,使用`case when`进行字段判断更新。这些技巧提高了代码的复用性和可维护性。

最近用mybatis进行select和update操作,因为一般只传入一个对象,却只对其中某些属性进行查询或更新,这时要写通用的sql语句,就必然涉及到多字段判断拼接。在这里做一个总结:

1. select多字段判断拼接

只传一个ArticleQuery对象,返回Article对象,常用

    <!--文章查询的sql片断,建议是以单表为单位定义查询条件,建议将常用的查询条件都写出来-->
    <sql id="query_items_where">
        <if test="name!=null and name!=''">
            and name like '%${name}%'
        </if>
        <if test="title!=null and title!=''">
            and title like '%${title}%'
        </if>
        <if test="articleId!=null">
            and article_id = #{articleId}
        </if>
    </sql>


    <select id="queryByCondition" parameterType="ArticleQuery" resultType="Article">
            <!-- 注意ORDER BY后面用的是$而不是# -->
            select * from articles
            <where>
                <include refid="query_items_where" />
            </where>
            <!-- 排序字段判断 -->
            <if test="sort!=null and sort!=''">
                ORDER BY ${sort}
            </if>
           	<! -- 排序方向判断 -->
            <if test="direction!=null and direction!=''">
                ${direction}
            </if>

    </select>

主要通过“if”对字段是否为空进行判断,其中用到了“include”把“where”中的字段判断集中起来,这样其他sql语句可以复用

2. update多字段判断拼接

    <update id="updateArticle" parameterType="Article">
        UPDATE articles SET
         view_num =(case when #{viewNum}=null then view_num else #{viewNum} end),
         comment_num=(case when #{commentNum}=null then comment_num else #{commentNum} end),
         category_id=(case when #{categoryId}=null then category_id else #{categoryId} end),
         like_num=(case when #{likeNum}=null then like_num else #{likeNum} end)

        WHERE article_id=#{articleId}

    </update>

主要就是用“case when”进行字段判断拼接。

<think> 我们正在讨论MyBatis-Plus中XML映射文件同时使用`${}`和`#{}`的方法。在MyBatis中,`#{}`是预编译处理(参数占位符),而`${}`是字符串替换(直接拼接SQL)。两者有不同的使用场景和安全性考虑。 ### 1. 使用场景区别 - **`#{}`(安全)**: 将传入的数据作为参数处理,生成预编译的SQL语句(如`?`占位符),能有效防止SQL注入。适用于大多数情况,尤其是参数值传递。 - **`${}`(谨慎使用)**: 直接替换为变量的值(不转义),常用于动态表名、列名等无法使用预编译的场景。但直接拼接SQL可能引发SQL注入风险。 ### 2. 示例:XML中同时使用`${}`和`#{}` 以下示例展示在同一个SQL语句中混合使用两种占位符: ```xml <select id="selectByDynamicTable" resultType="User"> SELECT * FROM ${tableName} WHERE name = #{name} AND age > ${age} <!-- 注意:此处${age}有注入风险,除非确保age是数字且来源可信 --> </select> ``` - **说明**: - `${tableName}`:动态表名(如根据业务切换表)。 - `#{name}`:安全传递参数值(预编译)。 - `${age}`:直接拼接数字(若`age`来自用户输入且未校验,可能被注入恶意代码)。 ### 3. 安全使用`${}`的建议 1. **严格校验输入**: 确保`${}`中的值来自可信源(如内部生成而非用户输入),或进行严格白名单校验(如只允许特定字符串)。 2. **数字类型优先用`#{}`**: 即使对于数字,若来源不可控,应优先使用`#{}`(MyBatis会自动转换类型)。例如: ```xml <!-- 安全写法:强制使用#{},避免注入 --> WHERE age > #{age} ``` 3. **动态表名/列名的替代方案**: - **方案1(推荐)**:在Java代码中校验表名合法性,再通过`${}`传递。 - **方案2**:使用MyBatis的`<bind>`标签预处理动态值(但无法完全避免拼接)。 ### 4. 完整示例:动态表名 + 条件查询 - **Mapper接口**: ```java List<User> selectByDynamicTable(@Param("tableName") String tableName, @Param("name") String name, @Param("age") int age); ``` - **XML映射**: ```xml <select id="selectByDynamicTable" resultType="User"> <!-- 动态表名(需确保tableName经过校验) --> SELECT * FROM ${tableName} WHERE 1=1 <!-- 安全参数 --> <if test="name != null"> AND name = #{name} </if> <!-- 注意:此处age使用#{}更安全 --> <if test="age > 0"> AND age > #{age} </if> </select> ``` ### 5. 注意事项 - **禁止用户输入直接用于`${}`**: 任何用户可控输入(如URL参数、表单字段)都不应直接填入`${}`,否则可能导致SQL注入。 - **MyBatis-Plus的特殊支持**: 对于动态表名,MyBatis-Plus提供`IDynamicTableName`接口实现更安全的处理[^1],避免手动拼接。 §§相关问题§§ 1. 如何防止MyBatis中的SQL注入? 2. MyBatis-Plus有哪些安全处理动态表名的方法? 3. `<if>`标签和`${}`在动态SQL中的区别是什么? </think> 在 MyBatis-Plus 的 XML 映射文件中同时使用 `${}` 和 `#{}` 是常见的需求,两者有不同的作用和适用场景: ### ▶ 核心区别 | **特性** | `#{}` (预编译占位符) | `${}` (字符串替换) | |----------------|----------------------------------|----------------------------------| | **处理方式** | 生成 `?` 占位符,预编译防止 SQL 注入 | 直接替换为参数值 | | **安全性** | ✅ 安全 | ❌ 有 SQL 注入风险 | | **适用场景** | 值参数(WHERE 条件值、INSERT 值) | 动态 SQL 片段(表名、列名、关键字)| | **数据类型转换**| 自动类型转换 | 直接字符串替换 | ### ▶ 同时使用的典型场景 #### 1. 动态表名 + 安全参数值 ```xml <select id="selectByDynamicTable" resultType="User"> SELECT * FROM ${tableName} WHERE department_id = #{deptId} AND create_time > #{startDate} </select> ``` - `${tableName}`:动态表名(需确保来源安全) - `#{deptId}`/`#{startDate}`:安全的值参数 #### 2. 动态排序字段 + 固定条件 ```xml <select id="selectWithOrder" resultType="Employee"> SELECT * FROM employee WHERE status = #{status} ORDER BY ${orderField} ${orderDirection} </select> ``` - `${orderField}`:动态排序字段(如 "salary") - `${orderDirection}`:排序方向(ASC/DESC) - `#{status}`:安全的查询条件值 ### ▶ 安全使用 `${}` 的建议 1. **白名单校验**(关键防御) ```java // Mapper 接口中校验动态参数 List<User> selectByDynamicTable( @Param("tableName") String tableName, // 需校验 @Param("deptId") Long deptId ); // Service 层实现校验 private static final Set<String> ALLOWED_TABLES = Set.of("user_2023", "user_2024"); public List<User> safeQuery(String tableName, Long deptId) { if (!ALLOWED_TABLES.contains(tableName)) { throw new IllegalArgumentException("Invalid table name"); } return mapper.selectByDynamicTable(tableName, deptId); } ``` 2. **避免用户输入直接拼接** ```xml <!-- 危险示例:用户输入直接用于表名 --> SELECT * FROM ${userInput} <!-- 可能被注入 DROP TABLE --> ``` 3. **复杂场景结合 OGNL 表达式** ```xml <select id="search" resultType="Product"> SELECT * FROM products WHERE 1=1 <if test="dynamicColumn != null and allowedColumns.contains(dynamicColumn)"> ORDER BY ${dynamicColumn} <!-- 动态列名 --> </if> LIMIT #{limit} </select> ``` ### ▶ 混合使用最佳实践 ```xml <update id="updateDynamicColumn"> UPDATE ${tableName} SET <!-- 动态列名 + 安全值 --> ${columnName} = #{newValue}, update_time = NOW() WHERE id = #{id} AND tenant_id = #{tenantId} <!-- 多租户过滤 --> </update> ``` 1. `${tableName}`/${columnName}:运维可控的动态元素 2. `#{}`:所有值类型参数 3. **防御措施**: - 表名/列名通过正则校验:`^[a-z_][a-z0-9_]{0,63}$` - 租户ID强制使用 `#{}` 防止越权 > ⚠️ **重要安全提示**:当必须使用 `${}` 时,应 Spring 的 `@PreAuthorize` 或自定义拦截器结合,实现权限校验[^1]。MyBatis-Plus 的[安全审计插件](https://baomidou.com/pages/2976a3/)可自动填充安全字段[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值