当前位置: 首页 > wzjs >正文

贵阳市 网站建设厦门市建设区网站首页

贵阳市 网站建设,厦门市建设区网站首页,建设网站有什么原则,建设银行的网站是什么字体SaaS化多租户实现的两种方法 SaaS系统的定义 SaaS,全称为Software-as-a-Service(软件即服务),是一种基于云计算的软件交付模式。而SaaS系统,即是通过这种模式提供给用户的软件系统。即多租户系统,每个租户…

SaaS化多租户实现的两种方法

SaaS系统的定义 SaaS,全称为Software-as-a-Service(软件即服务),是一种基于云计算的软件交付模式。而SaaS系统,即是通过这种模式提供给用户的软件系统。即多租户系统,每个租户独立,只能看到自己数据。

一、租户id隔离

这种方法比较简单,在每张表里添加一个字段tenant_id,给每个企业(租户)一个唯一tenant_id,那么在SQL的一切增删改查都带上tenant_id,即可实现租户隔离。

如何自动带上租户id,无需每次都在sql上添加tenant_id?
使用mybatis-plugin可以做到

步骤一、写一个拦截器

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class,Integer.class})
})
public class CustomerInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {//todo 拦截逻辑System.out.println("");StatementHandler statementHandler = (StatementHandler) invocation.getTarget();String originalSql = statementHandler.getBoundSql().getSql();//实际开发中从登录用户去获取他的tenant_id String modifiedSql = originalSql + " AND tenant_id = '" + tenant_id + "'";ReflectUtil.setFieldValue(statementHandler.getBoundSql(), "sql", modifiedSql);return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target,this);}@Overridepublic void setProperties(Properties properties) {//设置属性}
}

步骤二、注册插件

@Configuration
public class MybatisConfig {@Beanpublic String myInterceptor(SqlSessionFactory sqlSessionFactory) {sqlSessionFactory.getConfiguration().addInterceptor(new CustomerInterceptor());return "interceptor";}
}

二、动态数据源(重点)

本文重点要讲的是使用动态数据源实现动态切换数据库,来实现多租户自由切换
本文使用的是mybatis-flex

步骤一、注册租户和数据源到数据库

即,把租户的唯一信息和分配给租户的数据源一一对应,存入数据库,例如:

CREATE TABLE `datasource` (`id` bigint NOT NULL AUTO_INCREMENT,`display_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '显示名称',`db_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '链接默认数据库',`schema_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据库schema',`pool_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '连接池名称必须唯一',`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '描述',`db_host` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据库地址',`db_port` int NOT NULL COMMENT '数据库端口',`db_user` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户',`db_password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',`db_driver` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '驱动',`connect_params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '连接参数',`create_time` timestamp NULL DEFAULT NULL,`update_time` timestamp NULL DEFAULT NULL,`create_by` timestamp NULL DEFAULT NULL,`update_by` timestamp NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `datasource_pool_name_uindex` (`pool_name`) USING BTREE,KEY `database_creator_id_index` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;

示例数据:
在这里插入图片描述

步骤二、程序启动完成把数据连接信息加载到JVM
@Component
@Order(1)
public class InitialDataSource implements CommandLineRunner {public static final String DATASOURCE_MYSQL_COMMON_PARAMS_URL = "jdbc:%s://%s:%s/%s?%s";public static final String DATASOURCE_PGSQL_COMMON_PARAMS_URL = "jdbc:%s://%s:%s/%s?%s&%s";//这是数据源(步骤1提到的)表的mapper接口@Resourceprivate DatasourceMapper datasourceMapper;@Overridepublic void run(String... args) throws Exception {//1.清空内存中的数据源DataSourceKey.clear();//2.把数据库的datasource查询出来List<Datasource> fillSubmittals = datasourceMapper.selectAll();//3.动态添加新的数据源 FlexDataSource来自于mybatis-flexFlexDataSource flexDataSource = FlexGlobalConfig.getDefaultConfig().getDataSource();fillSubmittals.forEach(item -> addDatasourceItem(flexDataSource, item));}/*** 组装-添加数据源** @param flexDataSource* @param item*/public void addDatasourceItem(FlexDataSource flexDataSource, Datasource item) {DruidDataSource druidDataSource = buildDruidDataSource(item);//数据源信息加载到内存addIntoJVMDynamicPool(flexDataSource, item.getPoolName(),druidDataSource);}/*** 数据源信息加载到内存* @param flexDataSource* @param poolName* @param druidDataSource* @return*/public void addIntoJVMDynamicPool(FlexDataSource flexDataSource, String poolName, DruidDataSource druidDataSource){flexDataSource.addDataSource(poolName,druidDataSource);}public DruidDataSource buildDruidDataSource(Datasource item){DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setUrl(getCommonUrl(item));druidDataSource.setDriverClassName(DriverEnum.findByEnumDescription(item.getDbDriver()).getDriverClass());druidDataSource.setUsername(item.getDbUser());druidDataSource.setPassword(item.getDbPassword());druidDataSource.setValidationQuery("select 1");return druidDataSource;}public String getCommonUrl(Datasource datasource){String url = null;if(DriverEnum.MYSQL.getDescription().equals(datasource.getDbDriver())){url = String.format(DATASOURCE_MYSQL_COMMON_PARAMS_URL,datasource.getDbDriver(),datasource.getDbHost(),datasource.getDbPort(),StringUtils.hasText(datasource.getDbName()) ? datasource.getDbName() : "",StringUtils.hasText(datasource.getConnectParams()) ? datasource.getConnectParams() : "");}else if(DriverEnum.POSTGRES.getDescription().equals(datasource.getDbDriver())){url =String.format(DATASOURCE_PGSQL_COMMON_PARAMS_URL,datasource.getDbDriver(),datasource.getDbHost(),datasource.getDbPort(),StringUtils.hasText(datasource.getDbName()) ? datasource.getDbName() : "",StringUtils.hasText(datasource.getSchemaName()) ? "currentSchema="+datasource.getSchemaName() : "",StringUtils.hasText(datasource.getConnectParams()) ? datasource.getConnectParams() : "");}return url;}}

用到的枚举:

@Getter
public enum DriverEnum {/*** pg*/POSTGRES(0, "postgresql", "org.postgresql.Driver", DbType.postgresql, "postgres"),/*** mysql*/MYSQL(1, "mysql", "com.mysql.cj.jdbc.Driver", DbType.mysql,"mysql"),/*** ck*/CLICK_HOUSE(2, "clickhouse", "com.clickhouse.jdbc.ClickHouseDriver", DbType.clickhouse, "clickhouse");private final int index;// bi 记录驱动private final String description;private final String driverClass;// metabase 记录驱动private final String engine;private final DbType analysisType;DriverEnum(int index, String description, String driverClass, DbType analysisType, String engine){this.index = index;this.description = description;this.driverClass = driverClass;this.analysisType = analysisType;this.engine = engine;}private static final Map<String, DriverEnum> DESCRIPTION_ENUMS_MAP = Map.of(DriverEnum.POSTGRES.getDescription(), DriverEnum.POSTGRES,DriverEnum.MYSQL.getDescription(), DriverEnum.MYSQL,DriverEnum.CLICK_HOUSE.getDescription(), DriverEnum.CLICK_HOUSE);private static final Map<String, DbType> ANALYSISTYPE_ENUMS_MAP = Map.of(DriverEnum.POSTGRES.getDescription(), DbType.postgresql,DriverEnum.MYSQL.getDescription(), DbType.mysql,DriverEnum.CLICK_HOUSE.getDescription(), DbType.clickhouse);/*** 判断参数合法性*/public static boolean isValidName(String name) {for (DriverEnum cardStatus : DriverEnum.values()) {if (cardStatus.getDescription().equals(name)) {return true;}}return false;}/*** 根据描述查找枚举* @param description 描述* @return 枚举*/public static DriverEnum findByEnumDescription(String description){return DESCRIPTION_ENUMS_MAP.getOrDefault(description, DriverEnum.POSTGRES);}/*** 根据描述查找SQL解析器* @param description 描述* @return 枚举*/public static DbType findAnalysisTypeByDescription(String description){return ANALYSISTYPE_ENUMS_MAP.getOrDefault(description, DbType.postgresql);}
}
步骤三、业务使用(只列出核心)

@Resourceprivate JdbcTemplate jdbcTemplate;public List<FillSubmittal> queryAll() {String sql1 = "select * from datasource";//设置数据库pool-name   与步骤一的表里的pool_name对应DataSourceKey.use("c-1");executeSql(sql1);String sql2 = "select * from nc_fill_table_24_4lg0aa20f4rw9r";DataSourceKey.use("c-2");executeSql(sql2);String sql3 = "select * from user_info";DataSourceKey.use("c-3");executeSql(sql3);String sql4 = "select * from sys_role";DataSourceKey.use("c-4");executeSql(sql4);return null;}
 public void executeSql(String sql){List<Map<String, Object>> list =jdbcTemplate.queryForList(sql);log.info(JSON.toJSONString(list));}

这样,每个租户注册的时候就分配一个数据源,在使用时,根据租户灯笼裤信息获取到他对应的数据源信息,就可以通过DataSourceKey.use设置当前租户要用的数据源,实现动态切换


如图: 这里只画了程序启动,把数据加载到JVM中,和使用时,根据不用的pool_name(与租户一一对应)切换到对应的数据源。那么执行sql得到的就是对应数据源的数据,注意:这里执行sql使用的是jdbcTemplate了

在这里插入图片描述


文章转载自:

http://y6OLPq7V.xhpnp.cn
http://7PyDU41K.xhpnp.cn
http://cCl8qcrO.xhpnp.cn
http://2QgZigM4.xhpnp.cn
http://hFxT0gwf.xhpnp.cn
http://9KRZ1qT6.xhpnp.cn
http://OAtxSDBV.xhpnp.cn
http://xf4cfPC1.xhpnp.cn
http://8iN80de6.xhpnp.cn
http://vazn3IU8.xhpnp.cn
http://oH9ih7nT.xhpnp.cn
http://BYYLUs00.xhpnp.cn
http://MkMrUQix.xhpnp.cn
http://0iaKchA5.xhpnp.cn
http://kCXt9UHv.xhpnp.cn
http://jPH3mMyp.xhpnp.cn
http://poVwX5vf.xhpnp.cn
http://I2sri6VX.xhpnp.cn
http://tRHbjOFo.xhpnp.cn
http://aDDCgKjt.xhpnp.cn
http://ysH1FXx3.xhpnp.cn
http://LyS02xEj.xhpnp.cn
http://A6JKypQD.xhpnp.cn
http://cSe7tunA.xhpnp.cn
http://Lv5hNUcg.xhpnp.cn
http://JNJX55rQ.xhpnp.cn
http://50QLrrrI.xhpnp.cn
http://5FhAC0dQ.xhpnp.cn
http://ahxsWaXc.xhpnp.cn
http://reVaDxBE.xhpnp.cn
http://www.dtcms.com/wzjs/613623.html

相关文章:

  • 网站推广服务费会计分录怎么做长春火车站什么时候解封
  • 用asp.net做购物网站昆明网站建设公司排名猫咪科技
  • 如何导入旧网站数据库单位网站建设论文
  • 网站建设和维护实训建设网站需要的步骤
  • 网站加速mvc网站开发 案例视频
  • 怎样搭建网站百度百科优化
  • dw做购物网站官方网站app大全
  • 关于网站建设心得体会可口可乐公司的企业网站建设
  • 分模板网站和定制网站百度统计怎么用
  • 营销型企业网站群策略网站建设 中企动力成都
  • 上传文档网站开发关键字搜索网站怎么做
  • 凡科建站提示网站建设中网站设计任务书
  • 一个网站多个域名备案吗大连网站建设怎么样
  • 网站建设与管理电子教程盐城网站建设厂商
  • 网站推广软文公司家政公司怎么注册
  • 开发一个网站需要几个人美橙建站怎么样
  • 湖南竞网做网站好吗北京网站建设天下公司
  • it产品网站建设方案19年做网站
  • 吉他谱网站如何建设重庆建设工程信息网项目经理积分
  • 福州学做网站做图标得英文网站
  • 企业网站必须备案吗网站维护要多久
  • 专业商城网站设计制作做系统网站提醒有风险
  • 哪些网站适合新手编程做项目wordpress 预缓存
  • 旅游景区网站开发的政策可行性织梦快速建站
  • 东台建设网站的公司深圳市市场监督管理局
  • 地宝网 网站建设正规的机械外包加工订单网
  • 如何做盗版小说网站seo软件下载
  • iis网站ip县 两学一做网站
  • 做网站麻烦不网站是否开启gzip
  • 天津网站优化公司推荐哪家网站访问流程设计