|
@@ -0,0 +1,202 @@
|
|
|
+package com.rongwei.zhsw.system.config;
|
|
|
+
|
|
|
+import com.alibaba.druid.pool.DruidDataSource;
|
|
|
+import com.rongwei.commonservice.service.RedisService;
|
|
|
+import com.rongwei.rwadmincommon.system.vo.SysUserVo;
|
|
|
+import com.rongwei.rwcommon.utils.Constants;
|
|
|
+import com.rongwei.rwcommon.utils.SqlAdaptationUtil;
|
|
|
+import com.rongwei.rwcommon.utils.StringUtils;
|
|
|
+import org.apache.ibatis.binding.MapperMethod;
|
|
|
+import org.apache.ibatis.executor.statement.StatementHandler;
|
|
|
+import org.apache.ibatis.mapping.BoundSql;
|
|
|
+import org.apache.ibatis.plugin.*;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.web.context.request.RequestContextHolder;
|
|
|
+import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.sql.Connection;
|
|
|
+import java.util.Properties;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 自定义查询拦截器方法、适配多数据库语法
|
|
|
+ */
|
|
|
+@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
|
|
|
+public class ZHSWQuerySqlAdaptationInterceptor implements Interceptor {
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(ZHSWQuerySqlAdaptationInterceptor.class);
|
|
|
+
|
|
|
+ @Value("${saas.tenantmode:NONE}")
|
|
|
+ private String saasmode;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisService redisService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private DruidDataSource datasource;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object intercept(Invocation invocation) throws Throwable {
|
|
|
+// DruidDataSource datasource = (DruidDataSource)DynamicDataSource.dataSourcesMap.get(DynamicDataSource.getDataSource());
|
|
|
+ // 获取数据库类型
|
|
|
+ String dbType = datasource.getDbType();
|
|
|
+ // 方法一
|
|
|
+ StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
|
|
|
+ //映射工具
|
|
|
+// MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
|
|
|
+// MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
|
|
|
+ //先拦截到RoutingStatementHandler,里面有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandler的成员变量mappedStatement
|
|
|
+// MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
|
|
|
+ //id为执行的mapper方法的全路径名,如com.uv.dao.UserMapper.insertUser
|
|
|
+ BoundSql boundSql = statementHandler.getBoundSql();
|
|
|
+
|
|
|
+ //获取到原始sql语句
|
|
|
+ String sql = boundSql.getSql();
|
|
|
+ SqlAdaptationUtil sqlAdaptation = new SqlAdaptationUtil();
|
|
|
+ String mSql = sqlAdaptation.modifySql(sqlAdaptation.commonRuleAnalysis(sql),dbType);
|
|
|
+ //通过反射修改sql语句
|
|
|
+ Field field = boundSql.getClass().getDeclaredField("sql");
|
|
|
+ field.setAccessible(true);
|
|
|
+ // dao参数
|
|
|
+ Object parObj = statementHandler.getParameterHandler().getParameterObject();
|
|
|
+ // SaaS环境下schema切换
|
|
|
+ if("DATASOURCE".equals(saasmode)){
|
|
|
+ logger.info("saas切换");
|
|
|
+ String schema = null;
|
|
|
+ try{
|
|
|
+ schema = this.getSchema();
|
|
|
+ }catch (Exception e){
|
|
|
+ logger.info("通过上下文对象获取schema失败");
|
|
|
+ }
|
|
|
+ // 判断是否有参数(appointSchema)指定schema
|
|
|
+ if(parObj instanceof MapperMethod.ParamMap){
|
|
|
+ MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap)parObj;
|
|
|
+ if(paramMap.containsKey("appointSchema") && paramMap.get("appointSchema") != null){
|
|
|
+ String appointSchema = paramMap.get("appointSchema").toString();
|
|
|
+ schema = appointSchema;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ logger.info("schema:"+schema);
|
|
|
+ if(StringUtils.isNotBlank(schema)){
|
|
|
+ // 多租户schema切换方案一:通过 USE databaseName; 实现切换
|
|
|
+ mSql = "USE " + schema + ";" + mSql;
|
|
|
+ logger.info("mSql:"+mSql);
|
|
|
+ // 多租户schema切换方案二:直接获取Connection设置schema
|
|
|
+ /*Object[] objs = invocation.getArgs();
|
|
|
+ Connection conn = (Connection)objs[0];
|
|
|
+ conn.setSchema(schema);*/
|
|
|
+ }else{
|
|
|
+// throw new Exception("SaaS模式下查不到相应schema");
|
|
|
+ logger.warn("SaaS模式下查不到相应schema");
|
|
|
+ }
|
|
|
+ /*Connection o = (Connection)invocation.getArgs()[0];
|
|
|
+ o.setSchema(schema);
|
|
|
+ */
|
|
|
+ }
|
|
|
+
|
|
|
+ field.set(boundSql, mSql);
|
|
|
+ //sql语句类型 select、delete、insert、update
|
|
|
+ /*if(SqlCommandType.SELECT == mappedStatement.getSqlCommandType()){
|
|
|
+ }*/
|
|
|
+
|
|
|
+ // 返回
|
|
|
+ return invocation.proceed();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object plugin(Object target) {
|
|
|
+ return Plugin.wrap(target, this);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setProperties(Properties properties) {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SaaS模式下获取当前租户的数据库名
|
|
|
+ * @return
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ private String getSchema() throws Exception{
|
|
|
+ Long startTime = System.currentTimeMillis();
|
|
|
+ String schema = null;
|
|
|
+ schema = ContextHolder.getValue("dsKey");
|
|
|
+ if(StringUtils.isNotBlank(schema)){
|
|
|
+ return schema;
|
|
|
+ }
|
|
|
+
|
|
|
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
+ if(attributes == null){
|
|
|
+ logger.error("ServletRequestAttributes为null");
|
|
|
+ throw new Exception("ServletRequestAttributes为null");
|
|
|
+ }
|
|
|
+ HttpServletRequest request = attributes.getRequest();
|
|
|
+ if(request == null){
|
|
|
+ logger.error("HttpServletRequest为null");
|
|
|
+ throw new Exception("HttpServletRequest为null");
|
|
|
+ }
|
|
|
+
|
|
|
+ schema = request.getHeader("schema");
|
|
|
+ if(StringUtils.isNotBlank(schema)) {
|
|
|
+ return schema;
|
|
|
+ }
|
|
|
+
|
|
|
+ String token = request.getHeader("token");
|
|
|
+ logger.info("token:"+token);
|
|
|
+// if(StringUtils.isBlank(token)){
|
|
|
+// String incontrolToken = request.getHeader("incontrol-token");
|
|
|
+// Object tokenDskey = request.getAttribute(Constants.SAAS_LOGIN_TOKEN);
|
|
|
+// // 登录功能特殊处理获取DsKey
|
|
|
+// if(tokenDskey != null){
|
|
|
+// logger.info("tokenDskey:"+tokenDskey);
|
|
|
+// schema = (String) tokenDskey;
|
|
|
+// }
|
|
|
+// // 某些特殊功能incontrolToken也可以作为token
|
|
|
+// else if(StringUtils.isNotBlank(incontrolToken)){
|
|
|
+// logger.info("incontrolToken:"+incontrolToken);
|
|
|
+// schema = getSchemaByToken(incontrolToken);
|
|
|
+// }
|
|
|
+// // 某些特殊功能token放入线程全局变量中
|
|
|
+// else if(StringUtils.isNotBlank(ThreadLocalResource.token.get())){
|
|
|
+// logger.info("ThreadToken:"+ThreadLocalResource.token.get());
|
|
|
+// token = ThreadLocalResource.token.get();
|
|
|
+// schema = getSchemaByToken(token);
|
|
|
+// }
|
|
|
+// } else{
|
|
|
+// schema = getSchemaByToken(token);
|
|
|
+// System.out.println("Dskey:"+schema);
|
|
|
+// }
|
|
|
+ Object tokenDskey = request.getAttribute(Constants.SAAS_LOGIN_TOKEN);
|
|
|
+ if(tokenDskey != null && StringUtils.isNotBlank(tokenDskey.toString())){
|
|
|
+ schema = (String) tokenDskey;
|
|
|
+ }else {
|
|
|
+ schema = getSchemaByToken(token);
|
|
|
+ }
|
|
|
+ System.out.println("Dskey:"+schema);
|
|
|
+ Long endTime = System.currentTimeMillis();
|
|
|
+ System.out.println("查找schema时间为:"+(endTime-startTime));
|
|
|
+ return schema;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据token获取多租户模式的schema
|
|
|
+ * @param token
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private String getSchemaByToken(String token){
|
|
|
+ SysUserVo currUser = null;
|
|
|
+ if(StringUtils.isNotEmpty(token)){
|
|
|
+ currUser = redisService.getLoginUser(token);
|
|
|
+ }
|
|
|
+ String schema = null;
|
|
|
+ if(currUser != null){
|
|
|
+ schema = currUser.getTenantDo().getDskey();
|
|
|
+ }
|
|
|
+ return schema;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|