MyBatis加载表统计

1.引入

统计当前应用, MyBatis所有使用到的表.

2.原理

  1. 从Spring容器中, 依次获取 SqlSessionFactory, Configuration, MappedStatement
  2. 获取SQL (mappedStatement.getBoundSql(params).getSql())
  3. 解析SQL (Druid SQL-Parser)

3.源码

package com.clx.variablemanage;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.util.JdbcConstants;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

/**
 * MyBatis SQL 解析器
 *
 * @author liuxianqiang
 * @since 2021/11/19
 */
@Slf4j
public class MyBatisSqlAnalyzer {

    private static Map<TableStat.Name, TableStat> tableMap = new HashMap<>();

    public static void process(ApplicationContext context) {
        // 参数(mock)
        Map<String, Object> params = getMockParams();

        // 从Spring容器中获取 MappedStatement
        SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class);
        Assert.notNull(sqlSessionFactory, "mybatis: sqlSessionFactory is null");

        Configuration configuration = sqlSessionFactory.getConfiguration();
        Assert.notNull(configuration, "mybatis: configuration is null");

        Collection<MappedStatement> mappedStatements = configuration.getMappedStatements();
        Assert.notEmpty(mappedStatements, "mybatis: mappedStatements is empty");

        // Statement去重 (sql会用namespace.funcId, funcId作为id, 注册两次)
        mappedStatements = mappedStatements.stream().distinct().collect(Collectors.toList());

        // 遍历MappedStatement, 解析SQL
        for (Object mappedStatement : mappedStatements) {
            if (!(mappedStatement instanceof MappedStatement)) {
                // ignore, 方法名ID的SQL重复
                continue;
            }
            parseStmt((MappedStatement)mappedStatement, params);
        }

        // 打印解析的信息
        log.info("---------------- table info ----------------");
        tableMap.forEach((k, v) -> {
            log.info("{}: {}, {}, {}, {}, {}",
                    k.getName(), v.getInsertCount(), v.getDeleteCount(), v.getUpdateCount(), v.getSelectCount(), v.getDropCount());
        });
    }

    private static Map<String, Object> getMockParams() {
        // 重写get方法
        Map<String, Object> params = new HashMap() {
            @Override
            public Object get(Object key) {
                Object v = super.get(key);
                return Objects.isNull(v) ? "1" : v;
            }
        };

        /**
         * 兼容mybatis-plus
         * @see  com.baomidou.mybatisplus.core.mapper.BaseMapper
         */
        params.put("coll", ImmutableList.of(1));
        params.put("et", new HashMap() {
            @Override
            public Object get(Object key) {
                return "1";
            }
        });
        params.put("cm", ImmutableMap.of("id", 1));
        params.put("ew", new QueryWrapper().eq("id", 1));
        params.put("MP_OPTLOCK_VERSION_COLUMN", "v");

        /**
         * 兼容项目
         */
        params.put("sessionIdList", ImmutableList.of(1));

        return params;
    }

    private static void parseStmt(MappedStatement mappedStatement, Map<String, Object> params) {
        String id = null;
        String sql = null;
        try {
            id = mappedStatement.getId();
            sql = mappedStatement.getBoundSql(params).getSql();

            List<SQLStatement> stmts = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
            if (CollectionUtils.isNotEmpty(stmts)) {
                MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
                stmts.forEach(stmt -> {
                    stmt.accept(visitor);
                    appendTableState(visitor.getTables());
                });
            }
        } catch (Exception e) {
            log.error("parse sql exception, id:{}, sql:{}", id, sql, e);
        }
    }

    private static void appendTableState(Map<TableStat.Name, TableStat> tables) {
        if (MapUtils.isNotEmpty(tables)) {
            tables.forEach((k, v) -> {
                TableStat stat = tableMap.get(k);
                if (Objects.nonNull(stat)) {
                    v.setInsertCount(v.getInsertCount() + stat.getInsertCount());
                    v.setDeleteCount(v.getDeleteCount() + stat.getDeleteCount());
                    v.setUpdateCount(v.getUpdateCount() + stat.getUpdateCount());
                    v.setSelectCount(v.getSelectCount() + stat.getSelectCount());
                    v.setDropCount(v.getDropCount() + stat.getDropCount());
                }
                tableMap.put(k, v);
            });
        }
    }
}

4. 参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值