MyBatis ParameterHandler 是如何将 Java 对象转换为 SQL 语句中的参数的?

在 Mybatis中ParameterHandler 负责 将 Java 对象转换为 SQL 语句中 JDBC Statement 对象所需的参数。 你可以将 ParameterHandler 理解为 Java 对象和 JDBC 参数之间的转换器参数绑定器

ParameterHandler 的核心作用概括:

ParameterHandler 的主要作用是 从传入的 Java 参数对象中提取参数值,并使用合适的 TypeHandler 将这些 Java 值转换为 JDBC 类型的值,然后将转换后的 JDBC 值设置到 JDBC Statement 对象 (通常是 PreparedStatement) 的参数占位符 (?) 位置上。 它封装了参数绑定的复杂性,使得 MyBatis 的其他组件 (例如 StatementHandler) 无需关心具体的参数绑定细节。

ParameterHandler 将 Java 对象转换为 SQL 参数的详细步骤和机制:

  1. ParameterHandler 的创建:

    • ParameterHandler 实例由 StatementHandler 创建。 StatementHandler 会根据 MappedStatement 对象和传入的参数对象,创建 ParameterHandler 实例 (通常是 DefaultParameterHandler 实例)。
    • ParameterHandler 的创建通常发生在 StatementHandler.parameterize() 方法内部,在真正设置 SQL 参数之前。
  2. ParameterHandler.setParameters() 方法:设置 SQL 参数的核心方法

    • ParameterHandler 的核心方法是 setParameter(PreparedStatement ps)。 这个方法负责 将 Java 参数对象的值设置到 PreparedStatement 对象的参数占位符 (?) 位置上。
    • 输入参数:
      • PreparedStatement ps:
<think> 嗯,用户这次聚焦在MyBatis插件修改SQL的具体实现上,看来ta正在开发需要动态改写SQL的功能。从ta特意引用[3][4]来看,可能遇到了分库分表或数据权限控制的场景,需要运行时调整SQL语句。 用户之前已经了解了插件的基本原理,现在更关注技术细节。我注意到引用[3]提供了XML配置插件的代码片段,而引用[4]展示了通过注释占位的技巧,这些都需要融入回答。不过要提醒用户,注释占位符方案虽然巧妙但存在SQL注入风险。 关于实现方式,需要强调三个关键点:首先必须拦截StatementHandler.prepare方法,这是唯一能获取BoundSQL对象的时机;其次要注意线程安全问题,尤其是企业级应用;最后演示代码要包含完整的参数处理逻辑,比如引用[2]提到的ParameterHandler交互。 突然想到用户可能是想实现多租户架构。这种情况下除了修改SQL,还需要考虑在插件里解析租户ID,但要注意避免N+1查询问题。或许可以建议ta配合ThreadLocal传递租户信息,这样比每次解析SQL更高效。 对于高级场景,应该补充说明SQL解析的复杂性。比如修改UPDATE语句时,如果用户使用了join操作,直接替换表名可能导致语法错误。这时候可能需要引入JSqlParser这样的工具,但又要提醒依赖风险。 </think> 是的,**MyBatis插件可以动态修改运行时生成的SQL语句**。这是其核心能力之一,常用于分页、数据权限控制、逻辑删除、SQL优化等场景。以下是实现原理和方法: --- ### 一、修改SQL的核心原理 1. **拦截点选择** 需拦截`StatementHandler.prepare(Connection connection, Integer transactionTimeout)`方法(引用[^2])。该方法在SQL预编译前执行,此时可获取到完整的SQL语句对象。 2. **关键对象 `BoundSql`** 通过`StatementHandler`获取`BoundSql`对象,它包含: - `getSql()`:获取待执行的原始SQL字符串 - `setSql(String sql)`:动态替换SQL(**关键方法**) --- ### 二、实现步骤(含完整代码) #### 1. 实现`Interceptor`接口 ```java @Intercepts({ @Signature( type = StatementHandler.class, // 拦截StatementHandler method = "prepare", // 拦截prepare方法 args = {Connection.class, Integer.class} ) }) public class SqlModifyPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 1. 获取StatementHandler对象 StatementHandler handler = (StatementHandler) invocation.getTarget(); // 2. 获取BoundSql(包含SQL语句参数) BoundSql boundSql = handler.getBoundSql(); // 3. 获取原始SQL String originalSql = boundSql.getSql(); // 4. 修改SQL(示例:在所有SELECT语句后追加条件) String modifiedSql = originalSql; if (originalSql.trim().toLowerCase().startsWith("select")) { modifiedSql = originalSql + " WHERE tenant_id = 123"; // 动态追加条件 } // 5. 通过反射修改BoundSql中的SQL Field sqlField = BoundSql.class.getDeclaredField("sql"); sqlField.setAccessible(true); sqlField.set(boundSql, modifiedSql); // 替换为修改后的SQL // 6. 继续执行原流程 return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } } ``` #### 2. 注册插件(XML配置) ```xml <configuration> <plugins> <plugin interceptor="com.example.SqlModifyPlugin" /> <!-- 引用[^3] --> </plugins> </configuration> ``` #### 3. 进阶技巧:通过注释定位修改点(引用[^4]) 在Mapper XML中使用特殊注释作为标记: ```xml <select id="selectUser"> SELECT * FROM user /* WHERE condition_marker */ </select> ``` 在插件中替换注释: ```java String modifiedSql = originalSql.replace( "/* WHERE condition_marker */", "WHERE department_id = 456 AND is_active = 1" ); ``` --- ### 三、注意事项 1. **SQL注入风险** 直接拼接字符串存在风险,应避免使用外部参数修改SQL。如需参数化,可从`BoundSql`获取参数对象: ```java Object paramObj = boundSql.getParameterObject(); // 解析参数并安全拼接 ``` 2. **性能影响** SQL解析/修改操作应轻量,复杂场景建议使用SQL解析器(如JSqlParser)。 3. **拦截范围** 精确指定`@Signature`避免拦截不必要的方法(如仅拦截`SELECT`操作)。 4. **多插件顺序** 多个插件修改SQL时,执行顺序由配置顺序决定(后注册的先执行)。 --- ### 四、典型应用场景 | **场景** | **实现方式** | |------------------|------------------------------------------| | 多租户数据隔离 | 在所有查询中自动追加`tenant_id = ?` | | 逻辑删除 | 将`DELETE`转换为`UPDATE ... SET deleted=1` | | 分页优化 | 替换`LIMIT`为数据库方言的分页语句 | | SQL性能监控 | 注入`/* PERFORMANCE MONITOR */`注释 | --- ### 相关问题 1. 如何安全地在MyBatis插件中动态添加SQL条件? 2. MyBatis插件修改SQL与直接改写Mapper XML有何优劣? 3. 如何实现基于注解的SQL动态修改?(避免硬编码) 4. 在分页插件中,如何兼容不同数据库的SQL方言? 5. 如何通过MyBatis插件实现字段自动加解密? [^1]: MyBatis插件机制与拦截原理 [^2]: 可拦截的四大对象及方法 [^3]: XML配置插件方式 [^4]: 注释占位符实现SQL灵活修改
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰糖心书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值