# 第14天:完善MyBatis插件 ## 📚 今日目标 1. 完善DynamicMybatisPlugin插件 2. 优化SQL替换逻辑 3. 处理表别名和JOIN语句 4. 处理各种SQL场景 --- ## 🛠️ 实践任务:完善DynamicMybatisPlugin ### 完整实现代码 在 `src/main/java/cn/bugstack/middleware/db/router/dynamic/` 目录下完善 `DynamicMybatisPlugin.java`: ```java package cn.bugstack.middleware.db.router.dynamic; import cn.bugstack.middleware.db.router.DBContextHolder; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import java.lang.reflect.Field; import java.sql.Connection; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * MyBatis动态表名插件 * * @author 小傅哥 */ @Intercepts({ @Signature( type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class} ) }) public class DynamicMybatisPlugin implements Interceptor { private Pattern pattern = Pattern.compile("(from|into|update|FROM|INTO|UPDATE)\\s+(\\w+)\\s+", Pattern.CASE_INSENSITIVE); @Override public Object intercept(Invocation invocation) throws Throwable { // 获取StatementHandler StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); // 获取BoundSql BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); // 获取表索引 String tbKey = DBContextHolder.getTBKey(); if (null != tbKey && !tbKey.isEmpty()) { // 修改SQL中的表名 sql = sql.replaceAll("(from|into|update|FROM|INTO|UPDATE)\\s+(\\w+)\\s+", "$1 $2_" + tbKey + " "); } // 使用反射修改BoundSql的sql字段 MetaObject metaObject = SystemMetaObject.forObject(boundSql); metaObject.setValue("sql", sql); return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 可以读取配置 } } ``` ### 优化:处理表别名 ```java // 更精确的正则表达式 private Pattern pattern = Pattern.compile( "(from|into|update|FROM|INTO|UPDATE)\\s+(\\w+)(\\s+\\w+)?\\s+", Pattern.CASE_INSENSITIVE ); // 替换逻辑 Matcher matcher = pattern.matcher(sql); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String tableName = matcher.group(2); String alias = matcher.group(3); matcher.appendReplacement(sb, matcher.group(1) + " " + tableName + "_" + tbKey + (alias != null ? alias : "") + " "); } matcher.appendTail(sb); sql = sb.toString(); ``` ### 优化:处理JOIN语句 ```java // 处理JOIN语句中的表名 sql = sql.replaceAll("(join|JOIN)\\s+(\\w+)\\s+", "$1 $2_" + tbKey + " "); ``` --- ## 🎓 知识点拓展 ### 拓展1:SQL替换的精确性 **问题场景**: ```sql -- 原始SQL SELECT * FROM user WHERE name = 'from table' -- 简单replace会误替换 SELECT * FROM user_01 WHERE name = 'from_01 table' -- 错误! ``` **解决方案**:使用正则表达式精确匹配 ### 拓展2:处理各种SQL场景 **SELECT语句**: ```sql SELECT * FROM user WHERE id = ? -- 替换为 SELECT * FROM user_01 WHERE id = ? ``` **INSERT语句**: ```sql INSERT INTO user (name) VALUES (?) -- 替换为 INSERT INTO user_01 (name) VALUES (?) ``` **UPDATE语句**: ```sql UPDATE user SET name = ? WHERE id = ? -- 替换为 UPDATE user_01 SET name = ? WHERE id = ? ``` **DELETE语句**: ```sql DELETE FROM user WHERE id = ? -- 替换为 DELETE FROM user_01 WHERE id = ? ``` ### 拓展3:性能优化 **缓存Pattern对象**: ```java private static final Pattern PATTERN = Pattern.compile("..."); ``` **避免重复编译正则表达式** --- ## ✅ 今日检查清单 - [ ] 完善了DynamicMybatisPlugin插件 - [ ] 优化了SQL替换逻辑 - [ ] 处理了表别名和JOIN语句 - [ ] 处理了各种SQL场景 - [ ] 完成了拓展阅读 --- ## 🎯 明日预告 明天我们将完善自动配置类: - 完善DataSourceAutoConfig - 处理数据源创建 - 处理配置读取 --- ## 💡 思考题 1. 如何精确匹配SQL中的表名? 2. 如何处理表别名? 3. 如何优化SQL替换的性能? --- ## 📚 参考资源 - [MyBatis插件开发](https://mybatis.org/mybatis-3/zh/configuration.html#plugins) - [正则表达式教程](https://www.runoob.com/regexp/regexp-tutorial.html) - [SQL解析](https://github.com/alibaba/druid/wiki/SQL-Parser)