博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring-Boot快速集成MyBatis动态数据源(AbstractRoutingDataSource)
阅读量:3922 次
发布时间:2019-05-23

本文共 6483 字,大约阅读时间需要 21 分钟。

Spring-Boot快速集成MyBatis动态数据源(AbstractRoutingDataSource)

在一些应用场景下,我们会使用到多个数据源,可能存在不同类型数据库,mysql和sql server之类的同时使用,还有就是主从数据库,读写分离也可能会使用多数据源来解决问题。使用的场景一般有:1,主从数据库切换;2,多租户间数据逻辑隔离

依赖引入

org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
mysql
mysql-connector-java
runtime

配置动态数据源

/** * 动态数据源配置 */public class DynamicDataSource extends AbstractRoutingDataSource {
/** * 数据源存储用map */ private final Map
dataSourceMap = new ConcurrentHashMap<>(); /** * 当前线程数据源key */ private final ThreadLocal
contextHolder = new ThreadLocal
(); @Override protected Object determineCurrentLookupKey() {
//弹出当前数据源key return contextHolder.get(); } /** * 动态数据源构造方法 * @param defaultDataSource */ public DynamicDataSource(DataSource defaultDataSource) {
//存入主数据源 dataSourceMap.put("master",defaultDataSource); //设定目标数据源map setTargetDataSources(dataSourceMap); //设定默认数据源 setDefaultTargetDataSource(defaultDataSource); DynamicDataSourceUtil.dynamicDataSource =this; }}

代码中的dataSourceMap 是用来保存加入动态数据源中数据源,contextHolder 使用ThreadLocal 来定义,保证只为当前线程所持有。定义动态数据源一定要继承AbstractRoutingDataSource 类,并且重写determineCurrentLookupKey方法,determineCurrentLookupKey方法的返回值表示的是当前数据源的key,通过key去找已经加入map中的数据源。

注入数据源

/** * 数据源设定 * * @author DavidLei */@Configuration@MapperScan(basePackages = {
"club.dlblog.datasource.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")public class DataSourceConfig {
/** * 数据源设定 */ @Bean(name = "dataSource") @ConfigurationProperties("spring.datasource") public DataSource masterDataSource() {
return DataSourceBuilder.create().build(); } /** * session工厂 */ @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory( @Qualifier("dynamicDataSource") DynamicDataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver(). getResources("classpath:mapper/**/*.xml")); return sessionFactoryBean.getObject(); } /** * session模板 * @param sqlSessionFactory * @return */ @Bean(name = "sqlSessionTemplate") public SqlSessionTemplate ComSqlSessionTemplate( @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory); } /** * 动态数据源 * @param dataSource * @return */ @Bean(name = "dynamicDataSource") public DynamicDataSource dynamicDataSource(@Qualifier("dataSource") DataSource dataSource){
return new DynamicDataSource(dataSource); }}

在配置类中初始化动态数据源,并将将动态数据源绑定到session工厂上。

数据源管理工具类

/** * 数据源切换工具 */public class DynamicDataSourceUtil {
/** * 动态数据源 */ public static DynamicDataSource dynamicDataSource = null; /** * 判断当前数据源是否存在 * * @param tenantId * @return */ public static boolean isExistDataSource(String tenantId) {
if (dynamicDataSource != null) {
return dynamicDataSource.getDataSourceMap().containsKey(tenantId); } else {
return false; } } /** * 切换数据源 * * @param key */ public static void setDataSourceKey(String key) {
if (dynamicDataSource != null) {
dynamicDataSource.getContextHolder().set(key); dynamicDataSource.afterPropertiesSet(); } } /** * 获取当前数据源 * * @return */ public static String getDataSourceKey() {
if (dynamicDataSource != null) {
return dynamicDataSource.getContextHolder().get(); } else {
return null; } } /** * 切换到默认数据源 */ public static void clearDataSourceKey() {
if (dynamicDataSource != null) {
dynamicDataSource.getContextHolder().remove(); } } /** * 根据租户ID设置数据源 * * @param datatBase * @return */ public static void addDataSource(String datatBase, DataSource dataSource) {
if (dynamicDataSource == null) {
return; } // 没有数据源时添加数据源,有数据源直接使用 if (!isExistDataSource(datatBase)) {
// 新增数据源 dynamicDataSource.getDataSourceMap().put(datatBase, dataSource); } // 切换数据源 checkoutDataSource(datatBase); } /** * 切换数据源 * * @param tenantId */ public static void checkoutDataSource(String tenantId) {
if (dynamicDataSource != null) {
// 切换数据源 setDataSourceKey(tenantId); dynamicDataSource.afterPropertiesSet(); } } /** * 创建数据源 * @param driverClassName * @param url * @param userName * @param password * @return */ public static DataSource createDataSource(String driverClassName,String url, String userName,String password){
return DataSourceBuilder.create().driverClassName(driverClassName) .url(url).username(userName).password(password).build(); }}

在工具类中就是对动态数据源中的存储数据源的map进行put和remove操作,指定数据源时只需将contextHolder重新设定。

在进行具体CRUD时,数据源就会根据contextHolder进行切换。createDataSource方法随时可以创建新的数据源,放入动态数据源中进行管理。

拓展用法

定义过滤器,根据请求头来变更数据源

@Order(1)@WebFilter(urlPatterns = "/*")public class DynamicDataSourceFilter extends   GenericFilter {
private final static String TARGET_ID = "XXX-TARGET-ID"; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest; String targetId = request.getHeader(TARGET_ID); //数据源切换 DynamicDataSourceUtil.checkoutDataSource(targetId); filterChain.doFilter(servletRequest,servletResponse); }}

git地址 https://github.com/DavidLei08/BlogDataSource.git

转载地址:http://xhugn.baihongyu.com/

你可能感兴趣的文章
Volatile-2.不保证原子性
查看>>
剑指 Offer 25. 合并两个排序的链表
查看>>
剑指 Offer 26. 树的子结构
查看>>
剑指 Offer 27. 二叉树的镜像
查看>>
剑指 Offer 29. 顺时针打印矩阵
查看>>
剑指 Offer 31. 栈的压入、弹出序列
查看>>
剑指 Offer 32 - III. 从上到下打印二叉树 III
查看>>
剑指 Offer 33. 二叉搜索树的后序遍历序列
查看>>
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
查看>>
剑指 Offer 68 - II. 二叉树的最近公共祖先
查看>>
剑指 Offer 18. 删除链表的节点
查看>>
剑指 Offer 32 - II. 从上到下打印二叉树 II
查看>>
杭电oj-2011 多项式求和 C++
查看>>
杭电oj-2014 青年歌手大奖赛_评委会打分 C++
查看>>
杭电oj-2015 偶数求和 C++
查看>>
杭电oj-2016 数据的交换输出 C++
查看>>
杭电oj-2017 字符串统计 C++
查看>>
杭电oj-2018 母牛的故事 C++
查看>>
Educational Codeforces Round 87 (Rated for Div. 2)----题目+题解(A、B)
查看>>
Codeforces Round #647 (Div. 2) - Thanks, Algo Muse!B. Johnny and His Hobbies(异或)---题解
查看>>