前段时间写了完成基于 AbstractRoutingDataSource 接口的办法来完成多数据源的动态切换,参考:链接。
可是此种办法没有确保业务,所以今天来整合 Atomiks 来确保动态多数据源的业务。
引入的 jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
装备
第一个程序注解:AopContext 相关,和包扫描
@Configuration
//表明经过 aop 框架暴露改代理目标,AopContext 能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan(basePackages = "com.practice.thinkindynamicsource.多数据源动态完成.mapper" , sqlSessionTemplateRef = "sqlSessionTemplate")
public class ApplicationConfig {
}
AtomikosConfig
JTA 业务装备类:Java Transaction API。
界说了 PlatformTransactionManager 实际是 JtaTransactionManager。
@Configuration
public class AtomikosConfig {
@Bean(name = "userTransaction")
public UserTransaction userTransaction() throws SystemException {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(10000);
return userTransactionImp;
}
@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
@Bean(name = "transactionManager")
@DependsOn({"userTransaction", "atomikosTransactionManager"})
public PlatformTransactionManager transactionManager() throws SystemException {
UserTransaction userTransaction = userTransaction();
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
DruidConfig
- 首先界说了两个数据源Bean(这里有几个数据源,就界说几个Bean)
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(Environment env)
{
String prefix = "spring.datasource.druid.master.";
return getDataSource(env, prefix, MASTER);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(Environment env)
{
String prefix = "spring.datasource.druid.slave.";
return getDataSource(env, prefix, SLAVE);
}
protected DataSource getDataSource(Environment env, String prefix, String dataSourceName)
{
Properties prop = build(env, prefix);//获取装备信息
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(dataSourceName);
ds.setXaProperties(prop);
return ds;
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(MASTER, masterDataSource);
setDataSource(targetDataSources, SLAVE, "slaveDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}
//下面是结合 AbstractRoutingDataSource 完成多数据源的代码,详细参考源码
/**
* 设置数据源
*
* @param targetDataSources 备选数据源调集
* @param sourceName 数据源称号
* @param beanName bean称号
*/
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
{
try
{
DataSource dataSource = SpringUtils.getBean(beanName);
targetDataSources.put(sourceName, dataSource);
}
catch (Exception e)
{
}
}
MybatisConfig
public SqlSessionFactory createSqlSessionFactory(Environment env, DataSource dataSource) throws Exception
{
// String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
String mapperLocations = env.getProperty("mybatis.mapperLocations");
String configLocation = env.getProperty("mybatis.config-location");
// typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
VFS.addImplClass(SpringBootVFS.class);
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
return sessionFactory.getObject();
}
@Bean(name = "sqlSessionFactoryMaster")
public SqlSessionFactory sqlSessionFactoryMaster(Environment env, @Qualifier("masterDataSource") DataSource dataSource) throws Exception
{
return createSqlSessionFactory(env, dataSource);
}
@Bean(name = "sqlSessionFactorySlave")
public SqlSessionFactory sqlSessionFactorySlave(Environment env, @Qualifier("slaveDataSource") DataSource dataSource) throws Exception
{
return createSqlSessionFactory(env, dataSource);
}
@Bean(name = "sqlSessionTemplate")
public DynamicSqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactoryMaster") SqlSessionFactory factoryMaster,
@Qualifier("sqlSessionFactorySlave") SqlSessionFactory factorySlave) throws Exception
{
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
sqlSessionFactoryMap.put(DruidConfig.MASTER, factoryMaster);
sqlSessionFactoryMap.put(DruidConfig.SLAVE, factorySlave);
DynamicSqlSessionTemplate customSqlSessionTemplate = new DynamicSqlSessionTemplate(factoryMaster);
customSqlSessionTemplate.setTargetSqlSessionFactorys(sqlSessionFactoryMap);
return customSqlSessionTemplate;
}
自界说SqlSessionTemplate
这里初始化会结合 MybatisConfig :有几个数据源就会初始化几个 SqlSessionFactory 详细履行句子的时分就会运用详细的 SqlSessionFactory 去履行。
public class DynamicSqlSessionTemplate extends SqlSessionTemplate {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
private Map<Object, SqlSessionFactory> targetSqlSessionFactorys;
private SqlSessionFactory defaultTargetSqlSessionFactory;
@Override
public SqlSessionFactory getSqlSessionFactory()
{
SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys
.get(DynamicDataSourceContextHolder.getDataSourceType());
if (targetSqlSessionFactory != null)
{
return targetSqlSessionFactory;
}
else if (defaultTargetSqlSessionFactory != null)
{
return defaultTargetSqlSessionFactory;
}
return this.sqlSessionFactory;
}
private class SqlSessionInterceptor implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
final SqlSession sqlSession = getSqlSession(DynamicSqlSessionTemplate.this.getSqlSessionFactory(),
DynamicSqlSessionTemplate.this.executorType, DynamicSqlSessionTemplate.this.exceptionTranslator);
try
{
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory()))
{
sqlSession.commit(true);
}
return result;
}
catch (Throwable t)
{
Throwable unwrapped = unwrapThrowable(t);
if (DynamicSqlSessionTemplate.this.exceptionTranslator != null
&& unwrapped instanceof PersistenceException)
{
Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null)
{
unwrapped = translated;
}
}
throw unwrapped;
}
finally
{
closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
}
}
}
}
多数据源装备
查看源码,或参考上一篇文章:/post/719582…
- DynamicDataSource 完成 AbstractRoutingDataSource# determineCurrentLookupKey();
- DynamicDataSourceContextHolder 动态切换数据源处理
- DataSource、DataSourceAspect
测试
写一个接口,在完成类中完成多数据源切换而且确保业务
@Override
@Transactional
public Map testDataSource() {
SpringUtils.getAopProxy(this).insertA();
SpringUtils.getAopProxy(this).insertB();
int i=10/0;
return new HashMap();
}
@DataSource(value = DataSourceType.slave)
public void insertA(){
dynamicTestMapper.dynamicTestA();
}
@DataSource(value = DataSourceType.master)
public void insertB(){
dynamicTestMapper.dynamicTestB();
}
总结
相似指定完成那样:/post/720220…,履行详细数据源的时分,指定SqlSessionFactory。
- 运用 Atomikos 创立 transactionManager
- 自界说 SqlSessionFactory 来自界说 SqlSessionTemplate ,当切换数据源的时分,SqlSessionTemplate 也随之切换。
- 运用工具类获取当时 AOP代理目标去履行 mapper 办法:AopContext.currentProxy();才会收效。
悉数代码库房:gitee.com/ncharming/k…