mybatis传入表名为参数/拼接SQL

Context

现在有8个表,都有相同的两列。
需求就是选择 表&id,修改对应行的列值
8个表诶,两个操作就要写16个sql,还要判断枚举值选择不同的Dao,返回不同的实体…
啊,真的是太麻烦了。。要写这么多重复的代码,那一定会有更好的方法

show time~

  • select
    查操作要简单很多,参数是表名和id
    我们可以用${}传入表名,statementType="STATEMENT"在预编译前把SQL拼接好
<select id="selectTaskTable" resultMap="rasSqlAssistMap" statementType="STATEMENT">
       select id, columnName1, columnName2 from ${tableName} where id = ${id}
   </select>

踩坑1一开始我写的是"where id = #{id}",但编译出来是"where id = "
如果设置了非预编译,就不能再使用#{}了,都要统一用${}
因为#{}的变量替换是在预编译阶段,在SQL解析阶段是显示不出来的

  • update
    更新操作就麻烦很多了,因为更新的列不确定(两个列不一定都更新)
    踩坑2
    我本来想传入Map然后用判空,还是上面的坑,这些动态sql在解析阶段是解析不出来的,编译完set中间一片空白。。
<update id="updateByTableName" parameterType="String" statementType="STATEMENT">
		update ${tableName}
        <set>
            <if test="columnName1 != null and columnName1 != ''">
                columnName1 = ${property1},
            </if>
            <if test="columnName2 != null and columnName2 != ''">
                columnName2 = ${property2},
            </if>
        </set>
        where id = ${id}
        -->
    </update>

但是不要放弃,懒还是要偷的
那我们在代码里拼接sql吧

		StringBuilder sql = new StringBuilder();
        sql.append("update "+ tableName +" ");
        if (columnName1 != null && columnName2 != null){
            sql.append("set columnName1 = \"");
            sql.append(columnValue1);
            sql.append("\"");
            sql.append(", columnName2 = \"");
            sql.append(columnValue2);
            sql.append("\"");
            sql.append(" where id = \"");
            sql.append(id);
            sql.append("\"");
         }

mapper:

	<update id="updateByTableName" parameterType="String">
        ${value}
    </update>

把sql.toString()传进去就好啦~
拼接完是这样的:
"update tableName set columnName1 = “19”, columnName2 = “1” where id = “133"”
完美解决问题~

#与$的区别

比如

select id, columnName1, columnName2 from tableName where id = #{id}
//编译完
select id, columnName1, columnName2 from tableName where id =
  1. #{}
    被解析为一个参数占位符?
    预编译阶段向占位符中注入值
    将传入的参数当成字符串,会给参数加上"",预编译阶段自动进行Java类型和jdbc类型转换
select id, columnName1, columnName2 from tableName where id = ${id}
//在预编译前完成
select id, columnName1, columnName2 from tableName where id = 133
  1. ${}
    会在预编译前拼接好sql
    不会加上"",所以有sql注入的危险,使用要慎重

不听话的实习僧日常:
懒是促进发展的源泉

<think>好的,我现在需要帮助用户解决在MyBatis中动态传递时如何避免SQL注入的问题。首先,我得回忆一下MyBatis中#{}和${}的区别。根据用户提供的引用内容,特别是引用[1]和[3],知道#{}是预编译处理,能有效防止SQL注入,而${}是直接拼接字符串,存在注入风险。但动态这种情况,用户可能无法使用#{},因为不能作为预编译参数,这时候必须用${},但需要找到安全的方法。 接下来,我需要考虑用户提到的动态使用场景。比如,可能需要根据不同的业务逻辑切换不同的,这时候作为变量传入。但直接使用${tableName}会有风险,因为如果tableName来自用户输入且未经验证,攻击者可能注入恶意代码。 根据引用[4],用户需要在使用${}时确保安全性。可能的解决方案包括白单验证、参数校验和结合其他安全措施。比如,在传入前,先检查该是否存在于数据库的合法中,或者是否符合特定的命规则,比如只允许字母、数字和下划线,并且长度限制。 然后,我需要整合这些思路。首先,验证是否合法,可以通过正则达式检查输入是否符合预期的模式,例如只包含特定字符。其次,使用白单机制,确保传入只能是预定义的几个选项,这样即使使用${},也只能选择允许的,从而避免注入。 此外,可能还需要在业务层进行额外的校验,比如查询数据库的元数据,确认该确实存在,避免使用不存在的导致错误。引用[2]提到动态传入和字段时需要注意动态SQL的处理,而引用[4]则强调在这种情况下需要修复漏洞,所以结合这两点,确保在必须使用${}时,通过严格校验来保证安全。 另外,用户可能想知道最佳实践是什么。最佳实践应该包括:尽可能使用#{},在必须用${}时进行严格的输入验证和白单控制,同时结合其他安全措施如日志记录和权限最小化。 最后,总结这些步骤,给出一个结构清晰的回答,涵盖问题分析、解决方案、示例代码以及注意事项。同时,根据用户要求生成相关问题,比如如何验证、其他安全措施等。</think>在MyBatis中动态传递时,需通过严格的安全控制避免SQL注入风险。以下是具体实践方案: --- ### 一、问题分析 1. **${}的风险**:直接使用`${tableName}`会导致SQL注入,因为MyBatis会原样拼接字符串,如`SELECT * FROM ${table}`若`table`值为`user; DROP TABLE user`会引发灾难[^4]。 2. **#{}的局限**:预编译占位符`#{}`无法用于、字段SQL结构部分[^2]。 --- ### 二、解决方案 #### 1. 白单验证(核心措施) - **实现逻辑**:仅允许预定义的合法 - **代码示例**: ```java public String getSafeTableName(String input) { Set<String> allowedTables = new HashSet<>(Arrays.asList("user", "order", "product")); if (!allowedTables.contains(input)) { throw new IllegalArgumentException("非法"); } return input; } ``` ```xml <select id="selectData" resultType="map"> SELECT * FROM ${getSafeTableName(tableName)} </select> ``` #### 2. 正则达式过滤 - **强制命规范**:例如只允许字母、数字和下划线 ```java if (!tableName.matches("^[a-zA-Z0-9_]+$")) { throw new SQLException("非法格式"); } ``` #### 3. 数据库元数据校验 - **动态查询系统**:验证是否存在 ```xml <select id="validateTableExists" resultType="int"> SELECT COUNT(*) FROM information_schema.tables WHERE table_name = #{tableName} </select> ``` #### 4. 参数校验 - **加密哈希验证**:通过HMAC验证参数完整性 ```java String signature = HmacUtils.hmacSha256Hex(secretKey, tableName); if (!signature.equals(requestSignature)) { throw new SecurityException("参数被篡改"); } ``` --- ### 三、完整实践示例 ```java // Service层 public List<Map> queryDynamicTable(String tableName, String sign) { validateTableName(tableName); // 白单+正则校验 verifySignature(tableName, sign); // HMAC验证 return mapper.selectDynamicTable(tableName); } // XML映射 <select id="selectDynamicTable" resultType="map"> SELECT * FROM ${validatedTableName} </select> ``` --- ### 四、防御增强措施 1. **权限隔离**:数据库账号配置最小权限,禁止DDL操作 2. **日志监控**:记录所有动态操作日志 3. **输入规范化**:统一转为小写再校验(防止大小写绕过) 4. **防御纵深**:结合WAF进行请求过滤 --- ### 五、性能优化建议 1. **缓存白单**:将合法缓存至Redis,定期更新 2. **预编译语句池**:对高频使用的动态创建预编译缓存 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值