mybatis 转义(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言

在数据库操作中,MyBatis 是 Java 开发者广泛使用的 ORM 框架。然而,许多开发者在使用 MyBatis 时,会遇到 SQL 语句中的特殊字符处理问题,例如单引号、百分号(%)或反斜杠(\)等。这些问题往往会导致 SQL 语法错误或安全漏洞。本文将围绕 mybatis 转义这一核心主题,通过循序渐进的方式,结合实际案例和代码示例,帮助读者理解转义的原理、常见场景及解决方案。


MyBatis 转义的核心概念

什么是转义?

转义(Escape)在计算机领域指通过特定符号或方法,将原本具有特殊含义的字符“翻译”为普通字符的过程。例如,在 SQL 语句中,单引号(')用于表示字符串的开始和结束,若直接在字符串中插入单引号,会导致语法错误。此时需要通过转义符号(如反斜杠 \)将其转换为普通字符。

比喻:可以将转义想象成“翻译官”,当遇到需要特殊处理的字符时,它会将其转换为数据库能理解的“安全形态”,从而避免语法错误或逻辑漏洞。


MyBatis 转义的两种方式

MyBatis 提供了两种转义方式:

  1. 预编译参数(#{})
    这是 MyBatis 推荐的写法,通过预编译参数将用户输入的值与 SQL 语句分离,框架会自动处理特殊字符的转义。例如:

    <select id="selectUserByName" resultType="User">  
      SELECT * FROM users WHERE name = #{name}  
    </select>  
    

    name 参数值为 'O'Reilly' 时,MyBatis 会自动将其转换为 'O''Reilly',确保 SQL 语句的完整性。

  2. 字符串拼接(${})
    此方式直接将参数值拼接到 SQL 语句中,不会进行自动转义。因此,若参数值包含特殊字符,需手动处理。例如:

    <select id="selectUserByName" resultType="User">  
      SELECT * FROM users WHERE name = '${name}'  
    </select>  
    

    name 参数值为 'O'Reilly',则拼接后的 SQL 语句会是:

    SELECT * FROM users WHERE name = 'O'Reilly'  
    

    这将导致语法错误,因为单引号未被转义。


常见场景与解决方案

场景 1:字符串中的单引号

问题:用户输入包含单引号的姓名,如 'O'Reilly'
解决方案:使用 #{}预编译参数,MyBatis 会自动转义单引号。

示例代码

public interface UserMapper {  
  @Select("SELECT * FROM users WHERE name = #{name}")  
  User findByName(@Param("name") String name);  
}  

当调用 findByName("O'Reilly") 时,生成的 SQL 语句为:

SELECT * FROM users WHERE name = 'O''Reilly'  

场景 2:通配符(% 或 _)的转义

在 LIKE 子句中,%_ 是通配符。若用户输入中需要匹配这些字符本身,则需进行转义。

示例
用户搜索包含 % 的字符串,如 "100% 完成"

<select id="searchText" resultType="String">  
  SELECT text FROM articles WHERE content LIKE CONCAT('%', #{keyword, jdbcType=VARCHAR}, '%')  
</select>  

此时,若 keyword100%,MyBatis 会自动将 % 转义为 %%,生成:

SELECT text FROM articles WHERE content LIKE '%100%%%'  

场景 3:特殊符号的直接拼接

在动态 SQL 或复杂查询中,若必须使用 ${} 拼接字符串,需手动转义特殊字符。

案例:动态生成排序字段和方向:

<select id="queryWithSort" resultType="User">  
  SELECT * FROM users  
  ORDER BY ${sortField} ${sortDirection}  
</select>  

sortField"name DESC",则需手动转义空格和关键字:

Map<String, String> params = new HashMap<>();  
params.put("sortField", "name");  
params.put("sortDirection", "DESC");  

进阶技巧:自定义转义规则

1. 使用 EscapeStrategy 接口

MyBatis 允许开发者自定义转义策略。例如,若需将反斜杠(\)作为转义符,可实现 EscapeStrategy 接口:

public class CustomEscapeStrategy implements EscapeStrategy {  
  @Override  
  public String escapeString(String s) {  
    return s.replace("\\", "\\\\").replace("'", "''");  
  }  
}  

然后在 MyBatis 配置文件中指定该策略:

<configuration>  
  <settings>  
    <setting name="escapeStringHandler" value="com.example.CustomEscapeStrategy"/>  
  </settings>  
</configuration>  

2. 动态 SQL 中的条件转义

<if> 标签中处理特殊字符:

<select id="searchUsers" resultType="User">  
  SELECT * FROM users  
  <where>  
    <if test="name != null">  
      AND name LIKE CONCAT('%', #{name}, '%')  
    </if>  
    <if test="email != null">  
      AND email = #{email}  
    </if>  
  </where>  
</select>  

此示例中,name 参数会自动转义,而 email 参数因使用 #{} 也无需额外处理。


实战案例:处理用户输入的复杂查询

案例背景

用户希望搜索包含特定符号(如 #$)的文本,并按自定义排序。

错误示例

<!-- 错误写法:直接拼接导致 SQL 注入风险 -->  
<select id="searchWithCustomSort" resultType="Article">  
  SELECT * FROM articles  
  WHERE content LIKE '%${keyword}%'  
  ORDER BY ${sortField} ${sortDirection}  
</select>  

keyword'%' OR 1=1--,则 SQL 可能被篡改成:

SELECT * FROM articles WHERE content LIKE '%' OR 1=1--%'  

这会导致全表数据泄露。

正确实现

<!-- 正确写法:使用预编译参数并限制排序字段 -->  
<select id="searchWithCustomSort" resultType="Article">  
  SELECT * FROM articles  
  WHERE content LIKE CONCAT('%', #{keyword}, '%')  
  ORDER BY  
    <choose>  
      <when test="sortField == 'title'">title</when>  
      <when test="sortField == 'date'">date</when>  
      <otherwise>id</otherwise>  
    </choose>  
    ${sortDirection}  
</select>  

通过枚举限制 sortField 的合法值,并结合 #{} 转义 keyword,确保安全性。


总结与最佳实践

关键知识点回顾

  1. 预编译参数(#{})是 MyBatis 转义的首选方式,能自动处理特殊字符并防止 SQL 注入。
  2. 字符串拼接(${})需谨慎使用,必须手动转义或限制输入内容。
  3. 动态 SQL 中的条件逻辑需结合 <if><choose> 标签,避免直接拼接用户输入。
  4. 自定义转义策略可通过 EscapeStrategy 接口扩展,满足复杂场景需求。

最佳实践建议

  • 始终优先使用 #{}:除非绝对必要(如动态表名),否则避免使用 ${}
  • 对用户输入进行白名单校验:例如,限制排序字段为预设值,防止非法操作。
  • 结合日志和测试:通过 MyBatis 的日志功能(如 log4j)记录生成的 SQL,验证转义效果。

通过本文的讲解,开发者应能掌握 mybatis 转义的核心方法,并在实际项目中避免因字符处理不当引发的错误或安全漏洞。掌握这些技巧后,不仅能提升代码的健壮性,还能为团队协作和系统维护打下坚实的基础。

最新发布