Mycat 多租户方案
import org.springframework.beans.factory.annotation.Autowired;
import persistent.prestige.modules.edu.service.UserSchemeService;
public class TenantControlInteceper implements MethodInterceptor {
@Autowired
private UserSchemeService userScemeService;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
if("login".equals(invocation.getMethod().getName())) {
return invocation.proceed();
}
System.out.println("控制器层面,,计算 tenant。。。");
Object[] args = invocation.getArguments();
String tenant = "";
if( args != null && args.length > 0) {
tenant = (String)args[0];
}
TenantContextHolder.setTenant(tenant);
return invocation.proceed();
}finally {
TenantContextHolder.remove();
System.out.println("控制器层面,,移除 tenant。。。");
}
}
}
统一处理 Tenant 的设置为移除;此处与代码中的有点差别,是因为,,根据用户登录名获取 tenant 的逻辑放在了上面登录接口中。
只要遵循这样一种编码规范,action 方法的第一个参数的值为 tenant 就好。配置一下拦截器【基于 Spring AOP】
[](()2.2.3、业务承载方法
业务方法无需改变;但是要利用 Mybatis 拦截器改写 SQL。代码和配置如下:
1、工具类
package persistent.prestige.platform.mybatis.Interceptor;
import java.lang.reflect.Field;
import org.apache.commons.lang.reflect.FieldUtils;
public class ReflectHelper {
public static Object getFieldValue(Object obj , String fieldName ){
if(obj == null){
return null ;
}
Field targetField = getTargetField(obj.getClass(), fieldName);
try {
return FieldUtils.readField(targetField, obj, true ) ;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null ;
}
public static Field getTargetField(Class<?> targetClass, String fieldName) {
Field field = null;
try {
if (targetClass == null) {
return field;
}
if (Object.class.equals(targetClass)) {
return field;
}
field = FieldUtils.getDeclaredField(targetClass, fieldName, true);
if (field == null) {
field = getTargetField(targetClass.getSuperclass(), fieldName);
}
} catch (Exception e) {
}
return field;
}
public static void setFieldValue(Object obj , String fieldName , Object value ){
if(null == obj){return;}
Field targetField = getTargetField(obj.getClass(), fieldName);
try {
FieldUtils.writeField(targetField, obj, value) ;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
SQL 拦截类
package persistent.prestige.platform.mybatis.Interceptor;
import java.sql.Connection;
import jav 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 a.util.Properties;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.kahadb.page.Page;
import org.springframework.beans.factory.annotation.Autowired;
import persistent.prestige.modules.common.tenant.TenantContextHolder;
import persistent.prestige.modules.edu.dao.TeacherUserDao;
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class TenantInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
String tenant = TenantContextHolder.getTenant();
if(tenant == null || tenant == "") {
System.out.println("tenant 为空,不需要改写 sql 语句");
return invocation.proceed();
}
if (invocation.getTarget() instanceof RoutingStatementHandler) {
System.out.println("aaaaaaa");
RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation
.getTarget();
StatementHandler delegate = (StatementHandler) R Java 开源项目【ali1024.coding.net/public/P7/Java/git】 eflectHelper
.getFieldValue(statementHandler, "delegate");
BoundSql boundSql = delegate.getBoundSql();
Object obj = boundSql.getParameterObject();
// 通过反射获取 delegate 父类 BaseStatementHandler 的 mappedStatement 属性
MappedStatement mappedStatement = (MappedStatement) ReflectHelper
.getFieldValue(delegate, "mappedStatement");
// 拦截到的 prepare 方法参数是一个 Connection 对象
Connection connection = (Connection) invocation.getArgs()[0];
// 获取当前要执行的 Sql 语句,也就是我们直接在 Mapper 映射语句中写的 Sql 语句
String sql = boundSql.getSql();
// 给当前的 page 参数对象设置总记录数
System.out.println("处理之前" + sql);
//对 sql 增加 mycat 注解
sql = "/*!mycat:schema=" + tenant + " */" + sql;
System.out.println("加入处理后:" + sql);
ReflectHelper.setFieldValue(boundSql, "sql", sql);
}
return invocation.proceed();
}
最后
ActiveMQ 消息中间件面试专题
什么是 ActiveMQ?
ActiveMQ 服务器宕机怎么办?
丢消息怎么办?
持久化消息非常慢怎么办?
消息的不均匀消费怎么办?
死信队列怎么办?
ActiveMQ 中的消息重发时间间隔和重发次数吗?
ActiveMQ 消息中间件面试专题解析拓展:
redis 面试专题及答案
支持一致性哈希的客户端有哪些?
Redis 与其他 key-value 存储有什么不同?
Redis 的内存占用情况怎么样?
都有哪些办法可以降低 Redis 的内存使用情况呢?
查看 Redis 使用情况及状态信息用什么命令?
Redis 的内存用完了会发生什么?
Redis 是单线程的,如何提高多核 CPU 的利用率?
Spring 面试专题及答案
谈谈你对 Spring 的理解
Spring 有哪些优点?
Spring 中的设计模式
怎样开启注解装配以及常用注解
简单介绍下 Spring bean 的生命周期
Spring 面试答案解析拓展
高并发多线程面试专题
现在有线程 T1、T2 和 T3。你如何确保 T2 线程在 T1 之后执行,并且 T3 线程在 T2 之后执行?
Java 中新的 Lock 接口相对于同步代码块(synchronized block)有什么优势?如果让你实现一个高性能缓存,支持并发读取和单一写入,你如何保证数据完整性。
Java 中 wait 和 sleep 方法有什么区别?
如何在 Java 中实现一个阻塞队列?
如何在 Java 中编写代码解决生产者消费者问题?
写一段死锁代码。你在 Java 中如何解决死锁?
高并发多线程面试解析与拓展
jvm 面试专题与解析
JVM 由哪些部分组成?
JVM 内存划分?
Java 的内存模型?
引用的分类?
GC 什么时候开始?
JVM 面试专题解析与拓展!
评论