基于OGNL实现MyBatis-Plus的动态表名拦截

原有动态表插件功能基于拦截器采取的是提取字符串解析表名替换操作,可能会出现表名提取错误操作,不需要替换的表名方法需要加上注解才能忽略。我想设计基于OGNL表达式实现提供一个虚拟表名在运行时动态处理并替换表名。大致实现思路如下:

1. 定义虚拟表名

在 SQL 语句中使用特定格式的虚拟表名占位符。例如,使用 #tableName# 表示虚拟表名。

2. 解析 SQL 语句

在 MyBatis 拦截器中解析 SQL 语句,并检测虚拟表名占位符。

3. 使用 OGNL 表达式计算真实表名

定义一个接口和实现类,用于根据运行时上下文信息动态计算真实表名。

4. 替换虚拟表名

将解析得到的真实表名替换 SQL 语句中的虚拟表名。

5. 集成到拦截器

将上述逻辑集成到 MyBatis 的拦截器中,在 SQL 语句执行前进行处理。

具体实现步骤

1. 定义接口 DynamicTableNameHandler
 public interface DynamicTableNameHandler { 
     String dynamicTableName(Object parameter, String tableName); 
 } 
2. 实现接口 OgnlDynamicTableNameHandler
 import ognl.Ognl;
 import ognl.OgnlException;
 ​
 public class OgnlDynamicTableNameHandler implements DynamicTableNameHandler {
     private String expression;
 ​
     public OgnlDynamicTableNameHandler(String expression) {
         this.expression = expression;
     }
 ​
     @Override
     public String dynamicTableName(Object parameter, String tableName) {
         try {
             Object realTableName = Ognl.getValue(expression, parameter);
             return realTableName != null ? realTableName.toString() : tableName;
         } catch (OgnlException e) {
             throw new RuntimeException("Failed to evaluate OGNL expression", e);
         }
     }
 }
 ​
3. 修改拦截器 DynamicTableNameInnerInterceptor
 import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
 import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
 import com.baomidou.mybatisplus.core.toolkit.TableNameParser;
 import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 import org.apache.ibatis.executor.Executor;
 import org.apache.ibatis.executor.statement.StatementHandler;
 import org.apache.ibatis.mapping.BoundSql;
 import org.apache.ibatis.mapping.MappedStatement;
 import org.apache.ibatis.mapping.SqlCommandType;
 import org.apache.ibatis.session.ResultHandler;
 import org.apache.ibatis.session.RowBounds;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 ​
 @Getter
 @Setter
 @NoArgsConstructor
 @SuppressWarnings({"rawtypes"})
 public class DynamicTableNameInnerInterceptor implements InnerInterceptor {
     private DynamicTableNameHandler tableNameHandler;
 ​
     public DynamicTableNameInnerInterceptor(DynamicTableNameHandler tableNameHandler) {
         this.tableNameHandler = tableNameHandler;
     }
 ​
     @Override
     public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
         if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) return;
         PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
         mpBs.sql(this.changeTable(mpBs.sql(), parameter));
     }
 ​
     @Override
     public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
         PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
         MappedStatement ms = mpSh.mappedStatement();
         SqlCommandType sct = ms.getSqlCommandType();
         if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
             if (InterceptorIgnoreHelper.willIgnoreDynamicTableName(ms.getId())) {
                 return;
             }
             PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
             mpBs.sql(this.changeTable(mpBs.sql(), mpSh.boundSql().getParameterObject()));
         }
     }
 ​
     public String changeTable(String sql, Object parameter) {
         ExceptionUtils.throwMpe(null == tableNameHandler, "Please implement DynamicTableNameHandler processing logic");
         TableNameParser parser = new TableNameParser(sql);
         List<TableNameParser.SqlToken> names = new ArrayList<>();
         parser.accept(names::add);
         StringBuilder builder = new StringBuilder();
         int last = 0;
         for (TableNameParser.SqlToken name : names) {
             int start = name.getStart();
             if (start != last) {
                 builder.append(sql, last, start);
                 builder.append(tableNameHandler.dynamicTableName(parameter, name.getValue()));
             }
             last = name.getEnd();
         }
         if (last != sql.length()) {
             builder.append(sql.substring(last));
         }
         return builder.toString();
     }
 }
 ​
4. 配置拦截器
 DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor(
     new OgnlDynamicTableNameHandler("user.role + '_table'")
 );
 ​
 // 将拦截器添加到 MyBatis 配置中
 mybatisConfiguration.addInterceptor(interceptor);
 ​

 更多精彩请关注我的GithubUtopia007 (Qiao Guanhao) · GitHub

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值