网站首页 > 精选教程 正文
关于多数据源解决方案
目前在SpringBoot框架基础上多数据源的解决方案大多手动创建多个DataSource,后续方案有三:
- 继承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,使用AOP切面注入相应的数据源 ,但是这种做法仅仅适用单Service方法使用一个数据源可行,如果单Service方法有多个数据源执行会造成误读。
- 通过DataSource配置 JdbcTemplateBean,直接使用 JdbcTemplate操控数据源。
- 分别通过DataSource创建SqlSessionFactory并扫描相应的Mapper文件和Mapper接口。
MybatisPlus的多数据源
我通过阅读源码,发现MybatisPlus的多数据源解决方案正是AOP,继承了org.springframework.jdbc.datasource.AbstractDataSource,有自己对ThreadLocal的处理。通过注解切换数据源。也就是说,MybatisPlus只支持在单Service方法内操作一个数据源,毕竟官网都指明——“强烈建议只注解在service实现上”。
而后,注意看com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder,也就是MybatisPlus是如何切换数据源的。
重点看:
/**
* 为什么要用链表存储(准确的是栈)
* <pre>
* 为了支持嵌套切换,如ABC三个service都是不同的数据源
* 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。
* 传统的只设置当前线程的方式不能满足此业务需求,必须模拟栈,后进先出。
* </pre>
*/
private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new ThreadLocal() {
@Override
protected Object initialValue() {
return new ArrayDeque();
}
};
这段话翻译为大家都能懂得的意思就是“可以同时操控多个数据源”。那么,在MYSQL中,有语法为schemaName+.+tableName,如此一来就不会误走数据源了。
我继续看MybatisPlus是如何利用mybatis本身的ORM机制将实体类自动映射以及生成SQL语句的(这里插一句,MybatisPlus的源码易读懂,写的很不错)。无意看到了注解com.baomidou.mybatisplus.annotation.TableName中的schema,如果在类上加schema,在生成SQL语句时就会生成schemaName+. +tableName格式。
MybatisPlus多数据源事务(JTA)
简单说明一下JTA
JTA包括事务管理器(Transaction Manager)和一个或多个支持 XA 协议的资源管理器 ( Resource Manager ) 两部分, 可以将资源管理器看做任意类型的持久化数据存储;事务管理器则承担着所有事务参与单元的协调与控制。
JTA只是提供了一个接口,并没有提供具体的实现。
不过Atomikos对其进行了实现,而后SpringBoot将其进行了整合,对其进行了托管,很方便开发者拿来即用。
其中事务管理器的主要部分为UserTransaction 接口,开发人员通过此接口在信息系统中实现分布式事务;而资源管理器则用来规范提供商(如数据库连接提供商)所提供的事务服务,它约定了事务的资源管理功能,使得 JTA 可以在异构事务资源之间执行协同沟通。
通常接入JTA步骤(目的就是让JTA的UserTransaction接管驱动为分布式的数据源,通常为AtomikosDataSourceBean):
- 配置好AtomikosDataSourceBean。
- 把AtomikosDataSourceBean交给SqlSessionFactory。
- 配置UserTransaction事务管理。
但是我们用的是MybatisPlus,我们需要做的是接管MybatisPlus每一个数据源的配置,然后再把数据源依次交给MybatisPlus进行管理。
看看MybatisPlus是怎么进行多数据源配置的,源码里有这几个地方需要重点看一下:
- com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider,这个就是MybatisPlus多数据源配置的方式,利用HashMap来装载。
- com.baomidou.dynamic.datasource.DynamicDataSourceCreator,这个是每个数据源的配置方式。
其中com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider实现了接口com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider,是该接口的默认的实现。也就是说我们只需要实现该接口,自己配置多数据源以及每个数据源的驱动,成为该接口的默认实现就OK。
- 实现该接口,配置多数据源:
package xxx.xxx.xxx.config;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @author : zuoyu
* @description : 接管MybatisPlus多数据源至Atomikos管理
* @date : 2020-06-01 16:36
**/
@Service
@Primary
public class DynamicDataSourceProviderImpl implements DynamicDataSourceProvider {
/**
* 配置文件数据的松散绑定
*/
private final DynamicDataSourceProperties properties;
/**
* Atomikos驱动数据源创建
*/
private final AtomikosDataSourceCreator atomikosDataSourceCreator;
public DynamicDataSourceProviderImpl(DynamicDataSourceProperties properties, AtomikosDataSourceCreator atomikosDataSourceCreator) {
this.properties = properties;
this.atomikosDataSourceCreator = atomikosDataSourceCreator;
}
@Override
public Map<String, DataSource> loadDataSources() {
Map<String, DataSourceProperty> dataSourcePropertiesMap = properties.getDatasource();
Map<String, DataSource> dataSourceMap = new HashMap<>(dataSourcePropertiesMap.size() * 2);
for (Map.Entry<String, DataSourceProperty> item : dataSourcePropertiesMap.entrySet()) {
String pollName = item.getKey();
DataSourceProperty dataSourceProperty = item.getValue();
dataSourceProperty.setPollName(pollName);
dataSourceMap.put(pollName, atomikosDataSourceCreator.createDataSource(dataSourceProperty));
}
return dataSourceMap;
}
}
- Atomikos驱动数据源创建:
package xxx.xxx.xxx.config;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
/**
* @author : zuoyu
* @description : 事务数据源
* @date : 2020-06-01 17:30
**/
@Component
public class AtomikosDataSourceCreator {
/**
* 创建数据源
*
* @param dataSourceProperty 数据源信息
* @return 数据源
*/
public DataSource createDataSource(DataSourceProperty dataSourceProperty) {
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(dataSourceProperty.getUrl());
mysqlXaDataSource.setPassword(dataSourceProperty.getPassword());
mysqlXaDataSource.setUser(dataSourceProperty.getUsername());
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setMinPoolSize(5);
xaDataSource.setBorrowConnectionTimeout(60);
xaDataSource.setMaxPoolSize(20);
xaDataSource.setXaDataSourceClassName(dataSourceProperty.getDriverClassName());
xaDataSource.setTestQuery("SELECT 1 FROM DUAL");
xaDataSource.setUniqueResourceName(dataSourceProperty.getPollName());
return xaDataSource;
}
}
- 配置JTA事务管理器:
package xxx.xxx.xxx.config;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
/**
* @author : zuoyu
* @description : 分布式事务配置
* @date : 2020-06-01 17:55
**/
@Configuration
@EnableTransactionManagement
public class TransactionManagerConfig {
@Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(10000);
return userTransactionImp;
}
@Bean(name = "atomikosTransactionManager")
public TransactionManager atomikosTransactionManager() throws Throwable {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
@Bean(name = "transactionManager")
@DependsOn({"userTransaction", "atomikosTransactionManager"})
public PlatformTransactionManager transactionManager() throws Throwable {
return new JtaTransactionManager(userTransaction(), atomikosTransactionManager());
}
}
如此,即可
这样一来便可解决MybatisPlus多数据源的误走,且支持多数据源下的事务问题。
看完三件事??
?如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注头条号 『 JAVA后端架构 』,不定期分享原创知识。
- 同时可以期待后续文章ing
- 关注作者后台私信【888】有惊喜相送
猜你喜欢
- 2024-12-03 SpringBoot 动态多数据源及事务解决方案
- 2024-12-03 如何在Spring Boot中整合Druid数据源配置?
- 2024-12-03 连微服务Nacos配置中心都不了解,那你可能注定就是个码农
- 2024-12-03 Spring Boot 2.x基础教程:默认数据源Hikari的配置详解
- 2024-12-03 Spring Boot 2.5 终于对数据源动刀了
- 2024-12-03 mysql和neo4j集成多数据源和事务
- 2024-12-03 MyBatis整合Springboot多数据源实现
- 2024-12-03 ShardingSphere-Proxy分库分表以及多租户安装使用
- 2024-12-03 手把手教你玩多数据源动态切换
- 2024-12-03 若依开发框架解析笔记(5)-多数据源的应用
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)