SASS平台根据用户机构动态切换数据库连接的datasource

Stella981
• 阅读 304

背景:作为sass平台,有若干机构作为系统的租户存在,用户的创建需要绑定到唯一的机构下面,机构有机构简称,设计为,根据不同的机构下的用户设立独立的数据库,平台系统根据用户所在的机构去连接不同数据库进行业务操作

1.创建注解类

@Target(ElementType.METHOD,ElementType.Type)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Ducumented
public @interface DBChangeAnno{
   String value() default "master";
}

2.在数据库连接配置文件中设置多个数据源,并且数据源的名称根据机构简称设置

xx.jdbc.datasource.names=master,czbk
xx.jdbc.datasource.master.jdbcurl=jdbc:mysql://192.1.1.1:3306/db1
xx.jdbc.datasource.master.username=db1
xx.jdbc.datasource.master.passowrd=123456
xx.jdbc.datasource.master.driverClassName=com.mysql.jdbc.Driver
....
xx.jdbc.datasource.czbk.jdbcurl=jdbc:mysql://192.1.1.2:3306/db2
xx.jdbc.datasource.czbk.username=db2
xx.jdbc.datasource.czbk.passowrd=123456
xx.jdbc.datasource.czbk.driverClassName=com.mysql.jdbc.Driver

3.创建切面类等类,对所有标注了注解的方法执行前进行拦截并且切换数据源

//编写切面类,对注解方法进行前置增强,改变当前线程的数据库连接
@Slf4j
@Aspect
@Order(-1)
@ConditionalOnProperty(value = {xx.jdbc.datasource.names})
@Component
public class DBChangeInterceptor {
   @Autowired
   private XXXmapper mapper;//查询用户所在机构简称mapper
   
   @Before("@annotation(dbChangeAnno)")
   //这里也可加@within(dbChangeAnno)就会在类层面的所有方法进行切面,但是不能写在一起,比如@Before("@annotation(dbChangeAnno)||@within(dbChangeAnno)")这样只会生效后面的
   public void switchDataSource(JoinPoint point,DBCHangeAnno anno){
      String sourceName = anno.value();//默认是连接master数据库的,因为用户及机构信息是在master数据库进行维护
      if(SessionUtils.getSession() != null){
         sourceName = SessionUtils.getSession().getOrgId();//在session中存储当前登录用户的机构代码主键
         OrgInfo orgi = mapper.sekectByPrimaryKey(sourceName);
         sourceName = orgi.getShortName();
      }
      if(XXDataSourceContextHolder.containDataSourceName(sourceName)){
         XXDataSourceContextHolder.setCurrentDataSourceName(sourceName);
      }
   }

   //调用结束后还原线程的连接数据库到默认库中去
   @After("@annotation(dbChangeAnno)")
   public void restoreDataSource(JoinPoint point,DBCHangeAnno anno){
      XXDataSourceContextHolder.clearCurrentDataSourceName();
   }
}

//多数据源上下文类
public final class XXDataSourceContextHolder{
   //数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰。
   private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>();
   private static final Set<String> NAME_SET = new CopyOnWriteArraySet<String>();
   private static String defaultDataSourceName = null;//程序启动时将xx.jdbc.datasource.names的第一个名称作为默认数据库

   private XXDataSourceContextHolder(){
   }

   public static void setCurrentDataSourceName(String name){
      CONTEXT_HOLDER.set(name);
   }

   public static String getCurrentDataSourceName(){
      String name = CONTEXT_HOLDER.get();
      if(name == null){
         name = defaultDataSourceName;
      }
      return name;
   }

   public static void clearCurrentDataSourceName(){
      CONTEXT_HOLDER.remove();
   }

   public static boolean containDataSourceName(String name){
      return NAME_SET.contains(name);
   }

   public static void addDataSourceName(String name){
      NAME_SET.add(name);
   }

   public static void setDefaultDataSourceName(String name){
      XXDataSourceContextHolder.defaultDataSourceName = name;
   }
}

//在数据源的配置时使用该数据源配置
@Slf4j
public class XXRoutingDataSource extends AbstractRoutingDataSource{
   @Override
   protected Object determinCurrentLookupKey(){
      String cuurentDSName = XXDataSourceContextHolder.getCurrentDataSourceName();
      if(currentDSName == null || "".equals(cuurentDSName)){
         cuurentDSName = XXDataSourceContextHolder.getDefaultDataSourceName();
      }
      return cuurentDSName;
   }
}

//在Controller类中需要切换数据库的方法增加注解
@RequestMapping("/a/b/")
public class XXController{
   @RequestMapping(value = "list")
   @DBChangeAnno //增加此注解表示该业务需根据用户所在机构切换不同数据库连接
   public Obj list(){
      return new Object;//具体业务逻辑编写
   }
}

//参考文章:
https://www.jianshu.com/p/6b203f4926d5
https://www.cnblogs.com/haha12/p/10613549.html
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
2年前
Sass
嵌套规则(NestedRules)Sass允许将一套CSS样式嵌套进另一套样式中,内层的样式将它外层的选择器作为父选择器mainp{color:00ff00;width:97%;.redbox{
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这