一起养成写作习惯!这是我参与「掘金日新计划 · 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) {
}
}
后记
- 千古兴亡多少事?悠悠。不尽长江滚滚流。