构建一个SAAS应用,需要解决多租户数据隔离的问题,一般来说有3种方案:独立数据库、共享数据库独立SCHEMA、共享数据库共享SCHEMA,以下内容都是基于共享数据库共享SCHEMA这种方案进行讨论。
共享数据库共享SCHEMA这种方式,一般是为每一张表增加一个租户ID(TENANT_ID)的字段来实现数据隔离,那么在写业务逻辑的时候需要时刻关注租户数据的隔离,业务逻辑和数据隔离逻辑耦合在一起,增加了业务逻辑的复杂度,不利于业务逻辑的扩展。
那么比较好的实现方案是:在数据库操作层,用AOP方式拦截所有数据库操作,统一对数据库的增、删、改、查语句进行租户ID字段的增强逻辑,实现租户数据隔离。下面会使用BeetSql这款数据库工具来实现该方案。有关BeetSql详细信息请访问官网http://ibeetl.com。
BeetSql提供Interceptor接口,用来对执行的sql语句做各种扩展。before方法会在执行sql语句前被调用,after方法会在执行sql语句后被调用,exception方法会在执行sql语句抛出异常的时候被调用。那么我们要做的就是在before方法中去改写sql语句,并设置相关参数,来看一下具体代码:
public class TenantInterceptor implements Interceptor {
public static final String PROCESS = "PROCESS";
@Override
public void before(InterceptorContext ctx) {
// 如果当前线程变量设置了不处理,不进行租户过滤,默认不设置进行处理 ThreadLocalContext.put(TenantInterceptor.PROCESS,false);
Object processObj = null;
try {
processObj = ThreadLocalContext.get(PROCESS);
if (processObj == null || (Boolean) processObj) {
TenantHandler handler = TenantHandlerFactory.createHandler(ctx);
handler.handle();
}
} finally {
if (processObj != null) {
ThreadLocalContext.remove(PROCESS);
}
}
}
@Override
public void after(InterceptorContext ctx) {
}
@Override
public void exception(InterceptorContext ctx, Exception ex) {
}
}
TenantInterceptor代码逻辑比较简单,首先会读取线程本地变量是否需要对当前执行的方法进行租户数据隔离增强,如果设置了false,就不执行增强操作,否则会调用TenantHandlerFactory工厂类生成TenantHandler,并调用handle方法,那么来看看TenantHandler和TenantHandlerFactory的代码;
原文地址在: https://my.oschina.net/u/2542402/blog/2046075