001/* 002 * Copyright (c) 2022-2024, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.spring.boot; 017 018import com.mybatisflex.core.datasource.DataSourceBuilder; 019import com.mybatisflex.core.datasource.DataSourceDecipher; 020import com.mybatisflex.core.datasource.DataSourceManager; 021import com.mybatisflex.core.datasource.FlexDataSource; 022import com.mybatisflex.core.dialect.DbType; 023import com.mybatisflex.core.dialect.DbTypeUtil; 024import com.mybatisflex.core.exception.FlexExceptions; 025import com.mybatisflex.core.util.MapUtil; 026import com.mybatisflex.spring.boot.MybatisFlexProperties.SeataConfig; 027import com.mybatisflex.spring.datasource.DataSourceAdvice; 028import io.seata.rm.datasource.DataSourceProxy; 029import io.seata.rm.datasource.xa.DataSourceProxyXA; 030import org.apache.ibatis.session.SqlSessionFactory; 031import org.mybatis.spring.SqlSessionFactoryBean; 032import org.springframework.beans.factory.ObjectProvider; 033import org.springframework.beans.factory.config.BeanDefinition; 034import org.springframework.boot.autoconfigure.AutoConfigureBefore; 035import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 036import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 037import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 038import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; 039import org.springframework.boot.context.properties.EnableConfigurationProperties; 040import org.springframework.context.annotation.Bean; 041import org.springframework.context.annotation.Configuration; 042import org.springframework.context.annotation.Role; 043 044import javax.sql.DataSource; 045import java.util.Map; 046import java.util.Optional; 047 048/** 049 * MyBatis-Flex 多数据源的配置支持。 050 * 051 * @author michael 052 * @author 王帅 053 */ 054@ConditionalOnMybatisFlexDatasource() 055@Configuration(proxyBeanMethods = false) 056@EnableConfigurationProperties(MybatisFlexProperties.class) 057@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) 058@AutoConfigureBefore(value = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class} 059 , name = {"com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure", 060 "com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure"}) 061public class MultiDataSourceAutoConfiguration { 062 063 private final String master; 064 065 private final Map<String, Map<String, String>> dataSourceProperties; 066 067 private final SeataConfig seataConfig; 068 069 // 数据源解密器 070 protected final DataSourceDecipher dataSourceDecipher; 071 072 073 public MultiDataSourceAutoConfiguration(MybatisFlexProperties properties 074 , ObjectProvider<DataSourceDecipher> dataSourceDecipherProvider 075 ) { 076 dataSourceProperties = properties.getDatasource(); 077 dataSourceDecipher = dataSourceDecipherProvider.getIfAvailable(); 078 seataConfig = properties.getSeataConfig(); 079 master = properties.getDefaultDatasourceKey(); 080 } 081 082 @Bean 083 @ConditionalOnMissingBean 084 public DataSource dataSource() { 085 086 FlexDataSource flexDataSource = null; 087 088 if (dataSourceProperties != null && !dataSourceProperties.isEmpty()) { 089 090 if (dataSourceDecipher != null) { 091 DataSourceManager.setDecipher(dataSourceDecipher); 092 } 093 094 if (master != null) { 095 Map<String, String> map = dataSourceProperties.remove(master); 096 if (map != null) { 097 // 这里创建master时,flexDataSource一定是null 098 flexDataSource = addDataSource(MapUtil.entry(master, map), null); 099 } else { 100 throw FlexExceptions.wrap("没有找到默认数据源 \"%s\" 对应的配置,请检查您的多数据源配置。", master); 101 } 102 } 103 104 for (Map.Entry<String, Map<String, String>> entry : dataSourceProperties.entrySet()) { 105 flexDataSource = addDataSource(entry, flexDataSource); 106 } 107 } 108 109 return flexDataSource; 110 } 111 112 private FlexDataSource addDataSource(Map.Entry<String, Map<String, String>> entry, FlexDataSource flexDataSource) { 113 DataSource dataSource = new DataSourceBuilder(entry.getValue()).build(); 114 DataSourceManager.decryptDataSource(dataSource); 115 116 // 数据库类型 117 DbType dbType = null; 118 if (seataConfig != null && seataConfig.isEnable()) { 119 if (seataConfig.getSeataMode() == MybatisFlexProperties.SeataMode.XA) { 120 DataSourceProxyXA sourceProxyXa = new DataSourceProxyXA(dataSource); 121 dbType = DbType.findByName(sourceProxyXa.getDbType()); 122 dataSource = sourceProxyXa; 123 } else { 124 DataSourceProxy dataSourceProxy = new DataSourceProxy(dataSource); 125 dbType = DbType.findByName(dataSourceProxy.getDbType()); 126 dataSource = dataSourceProxy; 127 } 128 } 129 130 // 如果没有构建成功dbType,需要自解析 131 final DataSource lambdaInnerDataSource = dataSource; 132 dbType = Optional.ofNullable(dbType).orElseGet(() -> DbTypeUtil.getDbType(lambdaInnerDataSource)); 133 if (flexDataSource == null) { 134 flexDataSource = new FlexDataSource(entry.getKey(), dataSource, dbType, false); 135 } else { 136 flexDataSource.addDataSource(entry.getKey(), dataSource, dbType, false); 137 } 138 return flexDataSource; 139 } 140 141 142 /** 143 * {@link com.mybatisflex.annotation.UseDataSource} 注解切换数据源切面。 144 */ 145 @Bean 146 @ConditionalOnMissingBean 147 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 148 public DataSourceAdvice dataSourceAdvice() { 149 return new DataSourceAdvice(); 150 } 151 152}