最近一些场景下需要对SQL进行处理,搜寻了一通发现Druid中提供的SQL处理能力可以满足需求。

Druid本身的功能特性并不止于SQL处理,但如果项目中在数据库层已经有提供类似功能的框架,一般就不太会去变化,不过Druid的SQL处理部分没有那么重的依赖,可以单纯使用SQL这部分相关功能。

SQL处理的一些典型场景有,

SQL监控统计

例如统一不同SQL语句的平均耗时、最大耗时等等统计类的需求。

在进行统计之前需要对SQL进行归一化处理,将一些变量统一进行替换,如此才能对SQL进行归类。

Druid提供了一个快捷的方法,

public class ParameterizedOutputVisitorUtils {
    public static String parameterize(String sql, String dbType);
}

// 测试执行
ParameterizedOutputVisitorUtils.parameterize("select * from foobar where id > 1", JdbcConstants.MYSQL);

// 输出
SELECT *
FROM foobar
WHERE id > ?

SQL动态拦截

拦截具体SQL

这个场景与上面类似,同样也许要将SQL归一化后处理。而后可以与预设的拦截规则进行匹配判断,将制定的SQL语句进行拦截。

拦截表操作

在某些情况下,可能需要紧急禁止对表的操作访问,也可以通过Druid进行处理。这种情况下需要获取SQL语句中的表名,

public static List<String> getRelatedTable(String sql) {
    SQLStatement statement = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL).get(0);
    if (statement instanceof SQLInsertStatement) {
        return Arrays.asList(((SQLInsertStatement) statement).getTableName().getSimpleName());
    } else if (statement instanceof SQLUpdateStatement) {
        return Arrays.asList(((SQLUpdateStatement) statement).getTableName().getSimpleName());
    } else if (statement instanceof SQLDeleteStatement) {
        return Arrays.asList(((SQLDeleteStatement) statement).getTableName().getSimpleName());
    } else {
        MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
        statement.accept(visitor);
        Map<TableStat.Name, TableStat> info = visitor.getTables();
        return info.keySet().stream().map(TableStat.Name::getName).collect(Collectors.toList());
    }
}

表映射修改(影子表)

在处理数据表隔离时可能会使用影子表方案,根据环境不同将SQL路由到不同的表中,对此Druid提供了快捷操作接口,

public class SQLUtils {
    public static String refactor(String sql, String dbType, Map<String, String> tableMapping);
}

例如,

Map<String, String> mapping = new HashMap<>();
mapping.put("foobar", "foobar_shadow");
System.out.println(SQLUtils.refactor("select * from foobar where id > 1";, JdbcConstants.MYSQL, mapping));

// 输出

SELECT *
FROM foobar_shadow
WHERE id > 1

条件变更

在一些情况下对SQL的查询条件可能需要统一进行增加条件处理,对此也有直接可用的接口,

public class SQLUtils {
    public static String addCondition(String sql, String condition, String dbType);
}

System.out.println(SQLUtils.addCondition("select * from foobar", "id > 1", JdbcConstants.MYSQL));

// 输出

SELECT *
FROM foobar
WHERE id > 1