共计 19740 个字符,预计需要花费 50 分钟才能阅读完成。
SAAS 简介
【简介引自】Spring Boot 实现 SAAS 平台的基本思路
一、SAAS是 什么
SaaS 是 Software-as-a-service(软件即服务)它是一种通过 Internet 提供软件的模式,厂商将应用软件对立部署在本人的服务器
上,客户能够依据本人理论需要,通过互联网向厂商定购所需的应用软件服务,按定购的服务多少和工夫长短向厂商领取费用,
并通过互联网取得厂商提供的服务。用户不必再购买软件,而改用向提供商租用基于 Web 的软件,来治理企业经营流动,且无需
对软件进行保护,服务提供商会全权治理和保护软件。
二、SAAS 模式有哪些角色
①服务商:服务商次要是治理租户信息,依照不同的平台需要可能还须要统合整个平台的数据,作为大数据的根底。服务商在 SAAS
模式中是提供服务的厂商。
②租户:租户就是购买 / 租用服务商提供服务的用户,租户购买服务后能够享受相应的产品服务。当初很多 SAAS 化的产品都会划分
零碎版本,不同的版本凋谢不同的性能,还有基于性能免费之类的,不同的租户购买不同版本的零碎后享受的服务也不一样。
三、SAAS 模式有哪些特点
①独立性:每个租户的零碎互相独立。
②平台性:所有租户归平台对立治理。
③隔离性:每个租户的数据互相隔离。
在以上三个个性外面,SAAS 零碎中最重要的一个标记就是数据隔离性,租户间的数据齐全独立隔离。
四、数据隔离有哪些计划
①独立数据库
即一个租户一个数据库,这种计划的用户数据隔离级别最高,安全性最好,但老本较高。
长处:
为不同的租户提供独立的数据库,有助于简化数据模型的扩大设计,满足不同租户的独特需要,如果呈现故障,复原数据比较简单。
毛病:
增多了数据库的装置数量,随之带来保护老本和购买老本的减少。如果定价较低,产品走高价路线,这种计划个别对运营商来说是无奈接受的。
②共享数据库,隔离数据架构
即多个或所有租户共享数据库,然而每个租户一个 Schema。
长处:
为安全性要求较高的租户提供了肯定水平的逻辑数据隔离,并不是齐全隔离,每个数据库可反对更多的租户数量。
毛病:
如果呈现故障,数据恢复比拟艰难,因为复原数据库将牵涉到其余租户的数据 如果须要跨租户统计数据,存在肯定艰难。
③共享数据库,共享数据架构
即租户共享同一个数据库、同一个 Schema,但在表中减少 TenantID 多租户的数据字段。这是共享水平最高、隔离级别最低的模式。
长处:
三种计划比拟,第三种计划的保护和购买老本最低,容许每个数据库反对的租户数量最多。
毛病:
隔离级别最低,安全性最低,须要在设计开发时加大对平安的开发量,数据备份和复原最艰难,须要逐表逐条备份和还原。
如果心愿以起码的服务器为最多的租户提供服务,并且租户承受就义隔离级别换取降低成本,这种计划最适宜。
基于 spring boot、Mybatis-Plus 实现 动静切换数据源实现多租户 SaaS 零碎
注:该模块基于此我的项目 搭建 Springboot 我的项目并集成 Mybatis-Plus 的环境根底上,进行搭建部署。
创立数据库表
创立 people_config
表构造如下:
其对应的数据库 Schema 脚本如下:
DROP TABLE IF EXISTS `people_config`;
CREATE TABLE `people_config` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键 Id',
`empl_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '职员 Id',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '姓名',
`area_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '区域 Id',
`area_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '区域名称',
`dept_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门 Id',
`dept_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '部门名称',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '提交工夫',
`create_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人 Id',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '批改工夫',
`update_user` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '批改人',
`update_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '批改人_id',
`is_delete` int NULL DEFAULT NULL COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
其对应的数据库 Data 脚本如下:
DELETE FROM people_config;
INSERT INTO `mybatis_plus`.`tbl_people_config`(`id`, `empl_id`, `phone`, `name`, `area_id`, `area_name`, `dept_id`, `dept_name`, `create_time`, `create_user`, `create_id`, `update_time`, `update_user`, `update_id`, `is_delete`) VALUES (1, '职员 Id 1', '17343759359', 'isWulongbo', '波波区域', '1 区块链', '1', '开发部', '2020-12-05 15:12:00', '波波', '1', NULL, NULL, NULL, 0);
INSERT INTO `mybatis_plus`.`tbl_people_config`(`id`, `empl_id`, `phone`, `name`, `area_id`, `area_name`, `dept_id`, `dept_name`, `create_time`, `create_user`, `create_id`, `update_time`, `update_user`, `update_id`, `is_delete`) VALUES (2, '职员 Id 2', '13549553864', 'isWulongtao', '涛涛区域', '2 区块链', '2', '军区部', '2020-12-05 16:07:59', '涛涛', '2', NULL, NULL, NULL, 0);
创立 tenant_info
表构造如下:
其对应的数据库 Schema 脚本如下:
DROP TABLE IF EXISTS `tenant_info`;
CREATE TABLE `tenant_info` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键 id',
`tenant_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户 id',
`tenant_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租户名称',
`datasource_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据源 url',
`datasource_username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据源用户名',
`datasource_password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据源明码',
`datasource_driver` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '数据源驱动',
`system_account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '零碎账号',
`system_password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '账号密码',
`system_project` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '零碎 PROJECT',
`status` int NULL DEFAULT NULL COMMENT '是否启用(1 是 0 否)',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创立工夫',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新工夫',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
其对应的数据库 Data 脚本如下:
DELETE FROM tenant_info;
INSERT INTO `tenant_info` VALUES (1, '1', 'test1', 'jdbc:mysql://127.0.0.1:3306/mybatis_plus?serverTimezone=Asia/Shanghai&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull', 'root', 'root', 'com.mysql.cj.jdbc.Driver', 'baba', '123456', '治理', 1, '2020-12-05 14:50:50', NULL);
INSERT INTO `tenant_info` VALUES (2, '2', 'test2', 'jdbc:mysql:// 云服务器 ip:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai', '云服务器数据库连贯账号', '云服务器数据库连贯明码', 'com.mysql.cj.jdbc.Driver', 'baba2', '123456', 'admin', 1, '2020-12-05 15:06:38', NULL);
INSERT INTO `tenant_info` VALUES (3, '3', 'test3', 'jdbc:mysql://127.0.0.1:3306/mybatis_plus_one?serverTimezone=Asia/Shanghai&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull', 'root', 'root', 'com.mysql.cj.jdbc.Driver', 'baba3', '123456', 'root', 1, '2020-12-05 16:16:21', NULL);
生成 mybatisplus 的相干代码
执行 GeneratorCodeConfig
并输出表名:people_config
tenant_info
回车后,目录构造如下:留神 TenantInfo 表:
数据库 datetime
类型的会映射为 LocalDateTime
咱们将其批改为 Date
类型
外围代码
在该我的项目中 新建一个 package
命名为:datasource
创立 动静数据源 DynamicDataSource
package com.mybatis.plus.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 动静数据源
*
* @author 吴龙波
* @version 1.0
* @Description 动静数据源
* @date 2020/12/5 23:26
*/public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 如果不心愿数据源在启动配置时就加载好,能够定制这个办法,从任何你心愿的中央读取并返回数据源
* 比方从数据库、文件、内部接口等读取数据源信息,并最终返回一个 DataSource 实现类对象即可
* @return
*/
@Override
protected DataSource determineTargetDataSource() {return super.determineTargetDataSource();
}
/**
* 如果心愿所有数据源在启动配置时就加载好,这里通过设置数据源 Key 值来切换数据,定制这个办法
* @return
*/
@Override
protected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceKey();
}
/**
* 设置默认数据源
* @param defaultDataSource
*/
public void setDefaultDataSource(Object defaultDataSource) {super.setDefaultTargetDataSource(defaultDataSource);
}
public void setDataSources(Map<Object, Object> dataSources) {super.setTargetDataSources(dataSources);
// TODO 将数据源的 key 放到数据源上下文的 key 汇合中,用于切换时判断数据源是否无效
DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
}
}
创立动静数据源切面拦挡 DynamicDataSourceAspect
package com.mybatis.plus.datasource;
import com.mybatis.plus.base.BaseResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 动静数据源
*
* @author 吴龙波
* @version 1.0
* @Description 动静数据源切面拦挡
* @date 2020/12/5 23:26
*/@Slf4j
@Aspect
@Component
@Order(1) // 请留神:这里 order 肯定要小于 tx:annotation-driven 的 order,即先执行 DynamicDataSourceAspectAdvice 切面,再执行事务切面,能力获取到最终的数据源
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DynamicDataSourceAspect {@Around("execution(* com.mybatis.plus.controller.*.*(..)) || execution(* com.mybatis.plus.*.*(..))")
public Object doAround(ProceedingJoinPoint jp) throws Throwable {ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Object result = null;
try {HttpServletRequest request = sra.getRequest();
HttpSession session = sra.getRequest().getSession(true);
String tenantId = (String)session.getAttribute("tenantId");
if (StringUtils.isEmpty(tenantId)) {tenantId = request.getParameter("tenantId");
}
log.info("以后租户 Id:{}", tenantId);
if (!StringUtils.isEmpty(tenantId)) {DynamicDataSourceContextHolder.setDataSourceKey(tenantId);
result = jp.proceed();} else {result = "查问失败,以后租户信息未取到,请分割技术专家!";}
} catch (Exception ex) {ex.printStackTrace();
result = "零碎异样,请分割技术专家!";
} finally {DynamicDataSourceContextHolder.clearDataSourceKey();
}
return result;
}
}
创立动静数据源上下文 DynamicDataSourceContextHolder
package com.mybatis.plus.datasource;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 动静数据源
*
* @author 吴龙波
* @version 1.0
* @Description 动静数据源上下文
* @date 2020/12/5 23:26
*/public class DynamicDataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 master 数据源的 key 作为默认数据源的 key
*/ @Override
protected String initialValue() {return "master";}
};
/**
* 数据源的 key 汇合,用于切换时判断数据源是否存在
*/
public static List<Object> dataSourceKeys = new ArrayList<>();
/**
* 切换数据源
* @param key 数据源
*/
public static void setDataSourceKey(String key) {if (!StringUtils.isEmpty(key)) {contextHolder.set(key);
}
}
/**
* 获取数据源
* @return
*/
public static String getDataSourceKey() {return contextHolder.get();
}
/**
* 重置数据源
*/
public static void clearDataSourceKey() {contextHolder.remove();
}
/**
* 判断是否蕴含数据源
* @param key 数据源
* @return
*/
public static boolean containDataSourceKey(String key) {return dataSourceKeys.contains(key);
}
/**
* 增加数据源 Keys
* @param keys
* @return
*/
public static boolean addDataSourceKeys(Collection<? extends Object> keys) {return dataSourceKeys.addAll(keys);
}
}
创立动静数据源初始化DynamicDataSourceInit
package com.mybatis.plus.datasource;
import com.mybatis.plus.entity.TenantInfo;
import com.mybatis.plus.service.ITenantInfoService;
import com.mybatis.plus.util.SpringContextUtils;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 动静数据源
*
* @author 吴龙波
* @version 1.0
* @Description 动静数据源初始化
* @date 2020/12/5 23:26
*/@Slf4j
@Configuration
public class DynamicDataSourceInit {
@Autowired
private ITenantInfoService tenantInfoService;
@Bean
public void initDataSource() {log.info("====== 初始化动静数据源 =====");
DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringContextUtils.getBean("dynamicDataSource");
HikariDataSource master = (HikariDataSource) SpringContextUtils.getBean("master");
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", master);
List<TenantInfo> tenantList = tenantInfoService.list();
for (TenantInfo tenantInfo : tenantList) {log.info(tenantInfo.toString());
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(tenantInfo.getDatasourceDriver());
dataSource.setJdbcUrl(tenantInfo.getDatasourceUrl());
dataSource.setUsername(tenantInfo.getDatasourceUsername());
dataSource.setPassword(tenantInfo.getDatasourcePassword());
dataSource.setDataSourceProperties(master.getDataSourceProperties());
dataSourceMap.put(tenantInfo.getTenantId(), dataSource);
}
// 设置数据源
dynamicDataSource.setDataSources(dataSourceMap);
/**
* 必须执行此操作,才会从新初始化 AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动静切换才会起效
*/
dynamicDataSource.afterPropertiesSet();}
}
创立 MyBatisPlus 配置MybatisPlusConfig
package com.mybatis.plus.datasource;
import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 动静数据源
*
* @author 吴龙波
* @version 1.0
* @Description MyBatisPlus 配置
* @date 2020/12/5 23:26
*/@EnableTransactionManagement
@Configuration
@MapperScan({"com.mybatis.plus.mapper","com.mybatis.plus.*.*.mapper"})
public class MybatisPlusConfig {@Bean("master")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource master() {return DataSourceBuilder.create().build();}
@Bean("dynamicDataSource")
public DataSource dynamicDataSource() {DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", master());
// 将 master 数据源作为默认指定的数据源
dynamicDataSource.setDefaultDataSource(master());
// 将 master 和 slave 数据源作为指定的数据源
dynamicDataSource.setDataSources(dataSourceMap);
return dynamicDataSource;
}
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
/**
* 重点,使分页插件失效
*/
Interceptor[] plugins = new Interceptor[1];
plugins[0] = paginationInterceptor();
sessionFactory.setPlugins(plugins);
// 配置数据源,此处配置为要害配置,如果没有将 dynamicDataSource 作为数据源则不能实现切换
sessionFactory.setDataSource(dynamicDataSource());
// 扫描 Model
sessionFactory.setTypeAliasesPackage("com.mybatis.plus.*.*.entity,com.mybatis.plus.entity");
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// 扫描映射文件
sessionFactory.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
return sessionFactory;
}
@Bean
public PlatformTransactionManager transactionManager() {
// 配置事务管理, 应用事务时在办法头部增加 @Transactional 注解即可
return new DataSourceTransactionManager(dynamicDataSource());
}
@Bean
public PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
// 攻打 SQL 阻断解析器、退出解析链
sqlParserList.add(new BlockAttackSqlParser());
paginationInterceptor.setSqlParserList(sqlParserList);
return paginationInterceptor;
}
}
并在 application.properties
文件中配置默认数据源配置
spring.datasource.hikari.username=root
spring.datasource.hikari.password=root
spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?serverTimezone=Asia/Shanghai&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.connection-test-query = SELECT 1
spring.datasource.hikari.idle-timeout = 30000
spring.datasource.hikari.max-lifetime = 1880000
spring.datasource.hikari.connection-timeout = 30000
spring.datasource.hikari.minimum-idle = 5
spring.datasource.hikari.validation-timeout = 60000
创立好后的目录构造如下:
因为咱们在 MybatisPlusConfig
回去扫 mapper
包 以及读取 application.properties
配置,所以切记咱们在启动类 MybatisPlusApplication
中别再反复扫描 DAO,并且要排除SpringBootApplication
的数据源的主动配置
综上所述:MybatisPlusApplication
批改如下:
package com.mybatis.plus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//@MapperScan(basePackages = {"com.mybatis.plus"}) // 扫描 DAO
public class MybatisPlusApplication {public static void main(String[] args) {SpringApplication.run(MybatisPlusApplication.class, args);
}
}
管制页面简略测试代码如下:
PeopleConfigController
package com.mybatis.plus.controller;
import com.alibaba.fastjson.JSONObject;
import com.mybatis.plus.base.BaseApiService;
import com.mybatis.plus.base.BaseResponse;
import com.mybatis.plus.entity.PeopleConfig;
import com.mybatis.plus.service.IPeopleConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* <p>
* 前端控制器
* </p>
*
* @author wulongbo
* @since 2020-12-05
*/@RestController
@RequestMapping("/peopleConfig")
public class PeopleConfigController extends BaseApiService {
@Autowired
private IPeopleConfigService peopleConfigService;
@GetMapping("/info")
public BaseResponse<List<PeopleConfig>> getInfo() {List<PeopleConfig> list = peopleConfigService.list();
return setResultSuccess(list);
}
}
TenantInfoController
package com.mybatis.plus.controller;
import com.mybatis.plus.base.BaseApiService;
import com.mybatis.plus.base.BaseResponse;
import com.mybatis.plus.datasource.DynamicDataSource;
import com.mybatis.plus.entity.TenantInfo;
import com.mybatis.plus.service.ITenantInfoService;
import com.mybatis.plus.util.SpringContextUtils;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 前端控制器
* </p>
*
* @author wulongbo
* @since 2020-12-05
*/@Slf4j
@RestController
@RequestMapping("/tenantInfo")
public class TenantInfoController extends BaseApiService {
@Autowired
private ITenantInfoService tenantInfoService;
@GetMapping("/info")
public BaseResponse<TenantInfo> getInfo() {List<TenantInfo> list = tenantInfoService.list();
return setResultSuccess(list);
}
@GetMapping("/save")
public BaseResponse<?> saveInfo() {TenantInfo tenantInfo = new TenantInfo();
tenantInfo.setTenantName("动静新增");
tenantInfo.setDatasourceUrl("jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=Asia/Shanghai&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull");
tenantInfo.setDatasourceUsername("root");
tenantInfo.setDatasourcePassword("root");
tenantInfo.setDatasourceDriver("com.mysql.cj.jdbc.Driver");
tenantInfo.setStatus(1);
tenantInfo.setCreateTime(new Date(System.currentTimeMillis()));
tenantInfo.setUpdateTime(new Date(System.currentTimeMillis()));
boolean b = tenantInfoService.save(tenantInfo);
if (b) {log.info("====== 初始化动静数据源 =====");
DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringContextUtils.getBean("dynamicDataSource");
HikariDataSource master = (HikariDataSource) SpringContextUtils.getBean("master");
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", master);
List<TenantInfo> tenantList = tenantInfoService.list();
for (TenantInfo tenantInfos : tenantList) {log.info(tenantInfos.getTenantId() + " " + tenantInfos.getTenantName());
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(tenantInfos.getDatasourceDriver());
dataSource.setJdbcUrl(tenantInfos.getDatasourceUrl());
dataSource.setUsername(tenantInfos.getDatasourceUsername());
dataSource.setPassword(tenantInfos.getDatasourcePassword());
dataSource.setDataSourceProperties(master.getDataSourceProperties());
dataSourceMap.put(tenantInfos.getTenantId(), dataSource);
}
// 设置数据源
dynamicDataSource.setDataSources(dataSourceMap);
/**
* 必须执行此操作,才会从新初始化 AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动静切换才会起效
*/
dynamicDataSource.afterPropertiesSet();}
return b ? setResultSuccess("初始化动静数据源胜利!") : setResultError("初始化动静数据源失败!");
}
}
启动 Springboot 我的项目
启动 MybatisPlusApplication
应用 postman
拜访 localhost:8080/peopleConfig/info 须要咱们在 Body
上带人 tenantId
咱们切换 tenantId
的值 为'1'
,'2'
,'3'
查看返回值
因为工夫紧迫,后续再做具体阐明,明天到此为止。