Mybatis获取已经赋值,没有问号的Sql

背景:

维护公司内部系统的一个叫获取sql的接口时做的一些改进,该接口的功能是获取报表查询列表的的sql,可以用于直接在数据库里面查询出跟页面一样的数据。
公司原本的代码是每个获取sql接口都是各种StringBuilder拼接硬编码,再配上mybatis里面使用了动态的sql的话,也用java代码各种判断各种判断代替。这些代码的缺点:

  1. 代码十分臃肿,一个接口方法几百上千行的拼接代码。
  2. 可读性十分差,刚开始我开发新页面时,也按照这个方法拼接,过一段时间连自己写的代码都看不太懂逻辑。
  3. 很难维护,如果sql发生变化的话,就要改写该接口的代码,而臃肿加可读性差导致修改也比较困难(这就是我花时间改进的动力,修改太难了)。
  4. 刚好写了两三个接口之后把mybatis源码看了个大概,就尝试着修改一下,但是也可能有更好的办法,不过我改进后的方法也挺好用的了。就记录一下。不过我只敢改自己写的接口,历史的还是让他留着吧。
  5. 也可以用于查询拦截前面作日志输出。

修改:


private static final Field additionalParametersField = getAdditionalParametersField();

    private static Field getAdditionalParametersField() {
        try {
            Field additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
            additionalParametersField.setAccessible(true);
            return additionalParametersField;
        } catch (NoSuchFieldException var1) {
            return null;
        }
    }

/**
     * @param methodId  这个参数是mapper接口方法的全限定名
     * @param paramName  这个是参数名的集合   跟mapper.xml文件中sql语句使用的参数名相同
     * @param paramValue 这个是参数值得集合 必须与上面的参数名位置一一对应
     * @return
     */
protected String baseGetSql(String methodId,String[] paramName,Object[] paramValue) {

        //将参数转换为Map,因为要获取BoundSql需要一个Map的参数。
        Map<String, Object> paramMap = convertParamToMap(paramName, paramValue);
        //获取boundsql
        BoundSql boundSql = getBoundSql(methodId, paramMap);
        //获取参数映射
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Map additionalParameters = null;
        try {
            additionalParameters = (Map)additionalParametersField.get(boundSql);
        } catch (IllegalAccessException e) {
            ;
        }

        StringBuilder stringBuilder = new StringBuilder(boundSql.getSql());
        int paramMappingSize = parameterMappings.size();
        
        //这段是取自MybatisDefaultParameterHandler类的setParameters方法的一部分,用于参数的绑定,我这里是预编译?号替换。
        if (parameterMappings != null) {
            //获取Mapper接口的参数
            Object parameterObject = boundSql.getParameterObject();
            //获取管理类型转换的容器
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            for (int i = 0; i < paramMappingSize; ++i) {
                ParameterMapping parameterMapping = (ParameterMapping) parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        //如果是动态参数近这里
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        //如果参数为空value就为空
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        //如果参数有专门的类型转换就进入这里
                        value = parameterObject;
                    } else {
                        //普通赋值,MetaObject是参数的元信息,里面有参数值和参数名对应
                        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                        if (value == null && MapUtils.isNotEmpty(additionalParameters)) {
                            value = additionalParameters.get(propertyName);
                        }
                    }
                    String stringValue = getParameterValue(value);
                    //获取第一个?号的偏移量,然后用于替换,因为替换了的话,这个问号就消失了,所以可以一直获取第一个问号的偏移量
                    int index = stringBuilder.indexOf("?");
                    if (index > -1)
                        stringBuilder.replace(index,index+1,stringValue);
                }

            }
        }
        return stringBuilder.toString();
    }

/**
     * @description 将多个传到mybatis的参数转换成一个map,paramName与 paramValue 的顺序必须一一对应  并且参数名与mybatis的参数名必须一样 缺一不可
     * @param paramName 参数名数组
     * @param paramValue 参数值数组
     * @return
     */
    private Map<String,Object> convertParamToMap(String[] paramName,Object[] paramValue){
        Map<String,Object> paramMap = new HashMap<>();
        if (CommonsUtils.isEmptyArray(paramName) || CommonsUtils.isEmptyArray(paramValue))
            return  paramMap;
        if (paramName.length != paramValue.length)
            throw new RuntimeException("参数名与参数值的数量必须相等并且要一一对应");
        int len = paramName.length;
        for (int i = 0;i<len;i++){
            paramMap.put(paramName[i],paramValue[i]);
        }
        return paramMap;
    }

/**
     * @description 获取boundsql
     * @param id
     * @param paramMap
     * @return
     */
    private BoundSql getBoundSql(String id,Map<String,Object> paramMap){
        return configuration.getMappedStatement(id).getBoundSql(paramMap);
    }

/**
     * 对参数进行转换成String类型
     * @param obj
     * @return
     */
    private  String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            //如果参数是String类型,则直接添加单引号在两边
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            //如果参数是日期,则转换再加单引号。
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }

        }
        return value;
    }

public void test(){
		//这个是mapper接口参数名,与mybatis的@param里面的值一致。
		String[] paramNameArray = {"params1","params2"};
		//这个是参数名对应的值,一定要一一对应。并且跟mybatis接口一样便可。
        Object[] paramValueArray = {params,params};
        return baseGetSql("mapper接口全限定名",paramNameArray,paramValueArray);
      //这样不用各种拼接,只要传参与mapper接口的一致便可准确地获得sql。方便了不少。
}
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值