4月更文d19n19-sql拦截器之动态指定schema

451 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情

sql拦截器之动态指定schema

前文

本文内容为对于项目应用中动态选择schema的整理。主要采用的方式为使用mybatis拦截器,对于schema的内容进行动态的替换。文章中难免存在不足之处,还请见谅。

解决方案

首先说一下需求,根据人物的信息不同可能归属于不同的组织及分类中,也就导致了每个用户的数据可能需要存储在不同的表或schema中。而本文的内容也就应用而生,如何通过拦截器,简化sql语句中对于数据库表的指定,从而实现简单的schema动态指定。项目中采用了mybatis框架进行数据库操作,因此sql语句会写在xml文件当中。而要实现该需求,则选择通过mybatis自带的拦截器。

具体来看一下代码的操作,代码在下一部分中会贴出来。由于指定数据库的schema是在sql语句处理的准备阶段,因此采用数据库拦截器,拦截阶段选择prepare方法。因此当数据库操作语句执行时,会在准备阶段进行语句拼接,将指定的schema占位符,在拦截器中替换为我们实际上需要采用的schema的名字即可。因此在后续的实际语句执行语句时,实际操作的则已经是具体的schema,而不是之前指定的占位符。按照这种方式,也就达到了动态分库的目的。

以此方式利用拦截器也可以进行引申,我们拦截的位置也可以不仅仅是准备阶段,也可以是后续的执行阶段,甚至包括专门对于某类操作进行拦截,例如查询操作。对于查询操作时,通过拦截器进行一些特殊处理,例如在查询时通过拦截器增加处理条件等等。

解决代码

@Intercepts({@Signature(
    type = StatementHandler.class,
    method = "prepare",
    args = {Connection.class, Integer.class}
)})
public class SqlInterceptor implements Interceptor {
    public SqlReplaceInterceptor() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        if (sql.indexOf("@{schemaName}") != -1) {
            String schemaName = ...;
            sql = sql.replace("@{schemaName}", schemaName);
            Field field = boundSql.getClass().getDeclaredField("sql");
            field.setAccessible(true);
            field.set(boundSql, sql);
        }

        return invocation.proceed();
    }

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

    public void setProperties(Properties properties) {
    }
}

后记

  • 千古兴亡多少事?悠悠。不尽长江滚滚流。