mybatis插件的使用-- AutoSql

234 阅读2分钟

使用场景(问题)

  • 帮公司内部管理做了一个小应用,需求一直在变,关键是没有太多时间去天天调整代码,于是想到为什么不能配置就自动生成相关的dao层或是相关的服务呢?
    由于内部系统的使用,大量的需使用sql语句,现在sql太多的散落在各个xml文件中,不方便管理.
  • 他们需求一调整,就需要改代码而且每次都需要发包,能不能支持在线编辑呢,将sql放到数据库管理,通数据库配置来生成他们所要的服务。
  • 这里可以加权限,只是我懒得去加了,反正是内部使用,如果有人兴趣的话,建议可以放到service增加
    # 原理
    这里使用到了mybatis的插件特性,就是当mybatis拿到用户的参数后,在要执行sql前,我们就主动去拿出我们的sql去替换要执行的sql。 主要实现的类:AutoSqlIntercepter,相关的说明可以参考: ``` ``` package com.taing;

    import com.taing.autosql.sqlconfig.IAutoSqlDao; import com.taing.autosqlutils.ServiceConfig; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.Configuration;

    import java.sql.Connection; import java.util.List; import java.util.Map; import java.util.Properties;

    /**

    • @Author tianyi

    • @Description mybatis的拦截类

    • @Date 2023 - 01 - 30 **/ @Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})) public class AutoSqlIntercepter implements Interceptor {

      @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler target = (StatementHandler) invocation.getTarget(); String fieldDelegate = "delegate"; Object delegate = FieldUtils.readField(target, fieldDelegate, true); MappedStatement mappedStatement = (MappedStatement) FieldUtils.readField(delegate, "mappedStatement", true); String sqlId = mappedStatement.getId(); String packageName = "com.taing.autosql.sqlconfig.IAutosqlDao"; if (sqlId.contains(packageName)) { // 取出sql BoundSql boundSql = target.getBoundSql(); // 获得用户的参数 Map<String, Object> paramObject = (Map<String, Object>) boundSql.getParameterObject(); // get the method from the paramter, if you can't find it ,the show error String serviceConfigMethod = "methodName"; String method = (String) paramObject.get(serviceConfigMethod); if (null == method || method.trim().length() == 0) { throw new IllegalArgumentException("the paramter can't find the" + serviceConfigMethod);

           }
           Configuration configuration = new Configuration();
           ServiceConfig serviceConfig = new ServiceConfig(configuration, IAutoSqlDao.class);
           serviceConfig.parse(method);
           MappedStatement ms = configuration.getMappedStatement(sqlId);
           BoundSql newBoundSql = ms.getBoundSql(boundSql.getParameterObject());
           List<ParameterMapping> parameterMetaDataList = newBoundSql.getParameterMappings();
           String sql = newBoundSql.getSql();
           FieldUtils.writeField(boundSql, "sql", sql, true);
           String fieldName = "additionalParameters";
           Object parameter = FieldUtils.readField(newBoundSql,
                   fieldName, true);
           Map<String, Object> additionalParamters = parameter instanceof Map<?, ?> ? (Map<String, Object>) parameter : null;
           if (additionalParamters != null) {
               for (Map.Entry<String, Object> paramItem : additionalParamters.entrySet()) {
                   if (paramItem != null) {
                       boundSql.setAdditionalParameter(paramItem.getKey(), paramItem.getValue());
                       MapUtils.putAll(paramObject, new Object[]{paramItem});
                   }
               }
           }
           FieldUtils.writeField(boundSql, "parameterMappings", parameterMetaDataList, true);
       }
       return invocation.proceed();
      

      }

      @Override public Object plugin(Object target) { return Plugin.wrap(target, this); }

      @Override public void setProperties(Properties properties) {

      } }

    使用说明

    主要的接口如下,这里输入在数据库的定义的参数名称findAllUserInfo
    

    image.png

    image.png

    测试的结果

    image.png