优化记录(9):string.replaceFirst()的优化

一、前提:

在原有的项目中做迭代,发现一个sql执行效率特别慢,总费事16秒时间,触发了生产环境的报警机制。根据获取日志拿到mybatis的动态sql,在本地创建库表,执行效率不到1秒,执行效率并不慢;所以想着是否时sprinAop机制没有写好,导致执行的时候费时间。

排查了aop机制的代码,发现并不是这个问题,而是在记录打印log日志时,生成的log日志慢,因为前面开发人员是从度娘上找的(从网上可以找到),代码没有任何问题,但是当sql长度超过一定时,会存在用时过长问题,这是开发人员没有考虑到的,这里我将该处做了一些优化。

二、问题排查:

2.1、getSql 获取动态sql:

    public static String getSql(Invocation invocation) {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = "";
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        Configuration configuration = mappedStatement.getConfiguration(); 
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
               // begin : 程序慢的出处
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        sql = sql.replaceFirst("\\?", "");
                    }
                }
              // end 
            }
        }
        return sql;
    }

2.2、getParameterValue 对SQL进行格式化方法

private static String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(obj) + "'";
        } else {
            return obj != null ? obj.toString() : "";
        }
        return value;
    }

2.3、 单元测试

/**
     * 批量插入数据花费时间测试
     */
    @Test
    public void insertBatchUser_test(){
        List<User> userLists = new ArrayList<User>();
        //总数
        int count =600 ;
        Date date = new Date();
        for (int i = 0; i <count ; i++) {
            User  user = new User();
            user.setCompanyId(67L);
            user.setCompanyName("测试公司");
            user.setOrgCode("GWDS");
            user.setOrgName("国网典设");
            user.setUserId(194382L);
            user.setUserName("测试人");
            user.setCode("110200199001012345");
            user.setAge(32);
            // ... 还有十几个就不展示了
            userLists.add(user);
        }

        try {
            System.out.println("开始:==========================================================");
            long start = System.currentTimeMillis();
            userDao.insertBatch(userLists);
            long end = System.currentTimeMillis();
            System.out.println("插入"+count+"条数据,总计花费"+(end-start)+"毫秒");
            System.out.println("结束:==========================================================");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2.4、 测试结果:

开始:==========================================================
插入600条数据,总计花费16675毫秒
结束:==========================================================

三、 优化的代码

使用数组下标赋值的方式替换replaceFirst 方法实现的,实现代码的优化

public static String getSql(Invocation invocation) {
	// ... 代码省略,同2.1
	Object parameterObject = boundSql.getParameterObject();//获取参数
	List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
	String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
	if (parameterMappings.size() > 0 && parameterObject != null) {
		TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
		if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
			...
		} else {
			MetaObject metaObject = configuration.newMetaObject(parameterObject);
			// 改进的部分 
			String[] boundSqlArrays = sql.split("\\?");
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				String propertyName = parameterMapping.getProperty();
				String objStr = null;
				if (metaObject.hasGetter(propertyName)) {
					Object obj = metaObject.getValue(propertyName);
					objStr = Matcher.quoteReplacement(getParameterValue(obj));
				} else if (boundSql.hasAdditionalParameter(propertyName)) {
					//该分支是动态sql
					Object obj = boundSql.getAdditionalParameter(propertyName);
					objStr = Matcher.quoteReplacement(getParameterValue(obj));
				} else {
					objStr = "";
				}
				// 下标元素
				String elementIndex = boundSqlArrays[i];
				String replacement = elementIndex + objStr;
				boundSqlArrays[i] = replacement;
			}
			sql = Arrays.stream(boundSqlArrays).collect(Collectors.joining(""));
		}
	}
	return sql;
}

3.1、 执行结果:

开始:==========================================================
插入600条数据,总计花费875毫秒
结束:==========================================================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值