一 思路
首先我们知道一个数据源可以简单的理解为一个数据库连接 我们程序要配置多数据源 那么就需要 配置多个数据库的连接信息 然后对与事务 一个数据源对应一个事务管理器 在mybatis的初始化中 它需要根据数据源创建sqlSessionFactory工厂 我们要创建多数据源 那么我们应该创建多个sqlSessionFactory与数据源对应 这个时候问题来了 我们怎么可以让不同的mapper调用不同的会话工厂呢得到会话呢 这些就是配置多数据源的步骤与原理 下面我们来依次解决这个问题
环境 springboot 2.0.x
步骤一:
首先需要创建配置文件 来得到不同的数据源配置信息 可以想到这些配置的属性名和行为都是一样的 比如创建数据源 创建session工厂 事务管理处理器 所以我们可以 可以抽取出来 作为一个父类
@Data
@Slf4j
public class BaseSourceConfig {
private String filters;
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int minIdle;
private int maxActive;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
private MybatisConfig mybatisConfig=new MybatisConfig();
private Configuration configuration;
private String dataSourceName;
@Data
public static class MybatisConfig{
private String [] mapperXmlLocation;
private String [] mapperLocation;
}
public DataSource dataSourse() throws SQLException {
DruidDataSource druid = new DruidDataSource();
// 监控统计拦截的filters
druid.setFilters(filters);
// 配置基本属性
druid.setDriverClassName(driverClassName);
druid.setUsername(username);
druid.setPassword(password);
druid.setUrl(url);
druid.setName(dataSourceName);
/* //初始化时建立物理连接的个数
druid.setInitialSize(initialSize);
//最大连接池数量
druid.setMaxActive(maxActive);
//最小连接池数量
druid.setMinIdle(minIdle);
//获取连接时最大等待时间,单位毫秒。
druid.setMaxWait(maxWait);
//间隔多久进行一次检测,检测需要关闭的空闲连接
druid.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
//一个连接在池中最小生存的时间
druid.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
//用来检测连接是否有效的sql
druid.setValidationQuery(validationQuery);
//建议配置为true,不影响性能,并且保证安全性。
druid.setTestWhileIdle(testWhileIdle);
//申请连接时执行validationQuery检测连接是否有效
druid.setTestOnBorrow(testOnBorrow);
druid.setTestOnReturn(testOnReturn);
//是否缓存preparedStatement,也就是PSCache,oracle设为true,mysql设为false。分库分表较多推荐设置为false
druid.setPoolPreparedStatements(poolPreparedStatements);
// 打开PSCache时,指定每个连接上PSCache的大小
druid.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);*/
return druid;
}
// 创建该数据源的事务管理
public DataSourceTransactionManager dataSourseTransactionManager(DataSource dataSourse) throws SQLException {
return new DataSourceTransactionManager(dataSourse);
}
// 创建Mybatis的连接会话工厂实例
public SqlSessionFactory dataSourseSqlSessionFactory( DataSource dataSourse) throws Exception {
DruidDataSource dataSourseR = (DruidDataSource) dataSourse;
String name = dataSourseR.getName();
log.info("配置数据源:{} sqlSessionFactory工厂---------------------------------------",name);
log.info("应用的数据源地址:{}", dataSourseR.getUrl());
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSourse); // 设置数据源bean
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
List<Resource> mapperLocations=new ArrayList<>();
Arrays.stream(mybatisConfig.mapperXmlLocation).forEach(xml->{
try {
Resource[] resources = pathMatchingResourcePatternResolver.getResources(xml);
mapperLocations.addAll(Arrays.asList(resources));
} catch (IOException e) {
log.info("{} 文件不存在",xml);
e.printStackTrace();
}
});
log.info("扫描到数据源:{} 的mapper文件如下:-----------------------",name);
mapperLocations.forEach(p->{
log.info(p.getFilename());
});
Resource [] resources=new Resource[mapperLocations.size()];
sessionFactory.setMapperLocations(mapperLocations.toArray(resources)); // 设置mapper文件路径
if(configuration!=null){
sessionFactory.setConfiguration(configuration);
}
log.info("配置数据源:{} sqlSessionFactory工厂配置结束---------------------------------------",name);
return sessionFactory.getObject();
}
步骤二:不同的数据源继承父类
数据源1
/**
* @author
* @discription;
* @time 2020/9/5 17:50
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource1")
@MapperScan(basePackages = {"com.pps.back.frame.pupansheng.mapper","com.pps.back.frame.pupansheng.security.mapper"},sqlSessionFactoryRef = "dataSourse1SqlSessionFactory")
public class DataSourceConfig1 extends BaseSourceConfig{
@Primary
@Bean(name = "dataSourse1")
public DataSource dataSourse1() throws SQLException {
return super.dataSourse();
}
// 创建该数据源的事务管理
@Primary
@Bean(name = "dataSourse1TransactionManager")
public DataSourceTransactionManager dataSourse1TransactionManager(@Qualifier("dataSourse1") DataSource dataSourse1) throws SQLException {
return super.dataSourseTransactionManager(dataSourse1);
}
// 创建Mybatis的连接会话工厂实例
@Primary
@Bean(name = "dataSourse1SqlSessionFactory")
public SqlSessionFactory dataSourse1SqlSessionFactory(@Qualifier("dataSourse1") DataSource dataSourse1) throws Exception {
return super.dataSourseSqlSessionFactory(dataSourse1);
}
}
数据源2:
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource2")
@MapperScan(basePackages = {"com.pps.back.frame.pupansheng.mapper2","com.pps.back.frame.pupansheng.security.mapper"},sqlSessionFactoryRef = "dataSourse2SqlSessionFactory")
@Slf4j
public class DataSourceConfig2 extends BaseSourceConfig {
@Bean(name = "dataSourse2")
public DataSource dataSourse2() throws SQLException {
return super.dataSourse();
}
// 创建该数据源的事务管理
@Bean(name = "dataSourse2TransactionManager")
public DataSourceTransactionManager dataSourse2TransactionManager(@Qualifier("dataSourse2") DataSource dataSourse) throws SQLException {
return super.dataSourseTransactionManager(dataSourse);
}
// 创建Mybatis的连接会话工厂实例
@Bean(name = "dataSourse2SqlSessionFactory")
public SqlSessionFactory dataSourse2SqlSessionFactory(@Qualifier("dataSourse2") DataSource dataSourse2) throws Exception {
return super.dataSourseSqlSessionFactory(dataSourse2);
}
}
步骤三: 配置文件填写
datasource1:
# 数据源基本配置
username: root
password: pps123
#driver-class-name: com.mysql.jdbc.Driver
#url: jdbc:mysql://xxxx:3306/sajt?characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/webmodel1?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
type: com.alibaba.druid.pool.DruidDataSource
dataSourceName: dataSource1
mybatisConfig:
mapperXmlLocation:
- classpath:mybatis/security/mapper/*.xml #xml位置
mapperLocation:
- com.pps.back.frame.pupansheng.mapper #暂时未用到
datasource2:
# 数据源基本配置
username: root
password: pps123
#driver-class-name: com.mysql.jdbc.Driver
#url: jdbc:mysql://xxxx:3306/sajt?characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/webmodel2?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
type: com.alibaba.druid.pool.DruidDataSource
dataSourceName: dataSource2
mybatisConfig:
mapperXmlLocation:
- classpath:mybatis/security/mapper/*.xml #xml位置
mapperLocation:
- com.pps.back.frame.pupansheng.mapper2 #暂时未用到
好的此时springboot启动的时候 就可以根据不同配置信息创建不同的sessionFactory工厂在容器中
并且我们也在创建容器的过程中指定了不同的数据源 不同的事务管理器 sessionFactory工厂 和mapper文件位置 和dao接口的包名位置
下面分析一下数据源1的配置
其中:
指定不同的数据源 :
@Primary
@Bean(name = "dataSourse1")
public DataSource dataSourse1() throws SQLException {
return super.dataSourse();
}
指定不同事务管理器:
// 创建该数据源1的事务管理
@Primary
@Bean(name = "dataSourse1TransactionManager")
public DataSourceTransactionManager dataSourse1TransactionManager(@Qualifier("dataSourse1") DataSource dataSourse1) throws SQLException {
return super.dataSourseTransactionManager(dataSourse1);
}
指定不同的mybatis会话工厂 请注意 此步骤 很重要 这一步骤制定了dao接口位置和xml位置
// 创建Mybatis的连接会话工厂实例
@Primary
@Bean(name = "dataSourse1SqlSessionFactory")
public SqlSessionFactory dataSourse1SqlSessionFactory(@Qualifier("dataSourse1") DataSource dataSourse1) throws Exception {
return super.dataSourseSqlSessionFactory(dataSourse1);
}
指定接口位置在类的注解上 @MapperScan(当然也有其他方式):
@MapperScan(basePackages = {"com.pps.back.frame.pupansheng.mapper","com.pps.back.frame.pupansheng.security.mapper"},sqlSessionFactoryRef = "dataSourse1SqlSessionFactory")
指定xml位置在数据源父类的方法 dataSourse1SqlSessionFactory():
public SqlSessionFactory dataSourseSqlSessionFactory( DataSource dataSourse) throws Exception {
DruidDataSource dataSourseR = (DruidDataSource) dataSourse;
String name = dataSourseR.getName();
log.info("配置数据源:{} sqlSessionFactory工厂---------------------------------------",name);
log.info("应用的数据源地址:{}", dataSourseR.getUrl());
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSourse); // 设置数据源bean
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
List<Resource> mapperLocations=new ArrayList<>();
Arrays.stream(mybatisConfig.mapperXmlLocation).forEach(xml->{
try {
Resource[] resources = pathMatchingResourcePatternResolver.getResources(xml);
mapperLocations.addAll(Arrays.asList(resources));
} catch (IOException e) {
log.info("{} 文件不存在",xml);
e.printStackTrace();
}
});
log.info("扫描到数据源:{} 的mapper文件如下:-----------------------",name);
mapperLocations.forEach(p->{
log.info(p.getFilename());
});
Resource [] resources=new Resource[mapperLocations.size()];
sessionFactory.setMapperLocations(mapperLocations.toArray(resources)); // 设置mapper文件路径
if(configuration!=null){
sessionFactory.setConfiguration(configuration);
}
log.info("配置数据源:{} sqlSessionFactory工厂配置结束---------------------------------------",name);
return sessionFactory.getObject();
}
好的此时多数据源就配置结束了 -------------------------
根据我们自己配置文件配置的不同数据源的和xml位置 当我们使用dao方法时就会连接对应的数据库 实现多数据源
什么你以为文章结束了!!!! no no no 第一种方式简单明了 但是有个致命的问题 就是不够灵活 我们再添加一个数据库 就必须再添加一个配置类 有没有全自动的方式呢?完全根据配置文件来自动生成dataSource sqlSessionFactory 呢? 答案 :有的 下面我们来用高级的方法来实现多数据源配置
步骤一
改造刚才的基础配置类
BaseSourceConfig
package com.pps.back.frame.pupansheng.core.datasource;
import lombok.extern.slf4j.Slf4j;
/**
* @author
* @discription;
* @time 2020/9/6 12:38
*/
@Slf4j
public class BaseSourceConfig {
private String type;
private MybatisConfig mybatisConfig=new MybatisConfig();
private DataBaseProperty database=new DataBaseProperty();
private TransationProperty transationProperty=new TransationProperty();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public DataBaseProperty getDatabase() {
return database;
}
public void setDatabase(DataBaseProperty database) {
this.database = database;
}
public TransationProperty getTransationProperty() {
return transationProperty;
}
public void setTransationProperty(TransationProperty transationProperty) {
this.transationProperty = transationProperty;
}
public static class TransationProperty{
private int transactionSynchronization = 0;
private int defaultTimeout = -1;
private boolean nestedTransactionAllowed = false;
private boolean validateExistingTransaction = false;
private boolean globalRollbackOnParticipationFailure = true;
private boolean failEarlyOnGlobalRollbackOnly = false;
private boolean rollbackOnCommitFailure = false;
private boolean enforceReadOnly;
public int getTransactionSynchronization() {
return transactionSynchronization;
}
public void setTransactionSynchronization(int transactionSynchronization) {
this.transactionSynchronization = transactionSynchronization;
}
public int getDefaultTimeout() {
return defaultTimeout;
}
public void setDefaultTimeout(int defaultTimeout) {
this.defaultTimeout = defaultTimeout;
}
public boolean isNestedTransactionAllowed() {
return nestedTransactionAllowed;
}
public void setNestedTransactionAllowed(boolean nestedTransactionAllowed) {
this.nestedTransactionAllowed = nestedTransactionAllowed;
}
public boolean isValidateExistingTransaction() {
return validateExistingTransaction;
}
public void setValidateExistingTransaction(boolean validateExistingTransaction) {
this.validateExistingTransaction = validateExistingTransaction;
}
public boolean isGlobalRollbackOnParticipationFailure() {
return globalRollbackOnParticipationFailure;
}
public void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) {
this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure;
}
public boolean isFailEarlyOnGlobalRollbackOnly() {
return failEarlyOnGlobalRollbackOnly;
}
public void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) {
this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly;
}
public boolean isRollbackOnCommitFailure() {
return rollbackOnCommitFailure;
}
public void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) {
this.rollbackOnCommitFailure = rollbackOnCommitFailure;
}
public boolean isEnforceReadOnly() {
return enforceReadOnly;
}
public void setEnforceReadOnly(boolean enforceReadOnly) {
this.enforceReadOnly = enforceReadOnly;
}
}
public static class DataBaseProperty{
private String filters;
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int minIdle;
private int maxActive;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public long getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = maxWait;
}
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public int getMaxPoolPreparedStatementPerConnectionSize() {
return maxPoolPreparedStatementPerConnectionSize;
}
public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
}
public MybatisConfig getMybatisConfig() {
return mybatisConfig;
}
public void setMybatisConfig(MybatisConfig mybatisConfig) {
this.mybatisConfig = mybatisConfig;
}
public static class MybatisConfig{
private String [] mapperXmlLocation;
private String [] mapperLocation;
private String [] typeAliasesPackageList;
public String[] getMapperXmlLocation() {
return mapperXmlLocation;
}
public void setMapperXmlLocation(String[] mapperXmlLocation) {
this.mapperXmlLocation = mapperXmlLocation;
}
public String[] getMapperLocation() {
return mapperLocation;
}
public void setMapperLocation(String[] mapperLocation) {
this.mapperLocation = mapperLocation;
}
public String[] getTypeAliasesPackageList() {
return typeAliasesPackageList;
}
public void setTypeAliasesPackageList(String[] typeAliasesPackageList) {
this.typeAliasesPackageList = typeAliasesPackageList;
}
}
}
分别封装 数据源属性 和 事务管理属性 和 sqlSessionFactory属性
步骤二 改变配置文件写法
spring:
dataSourseList: data-source1 #数据源列表名称 用逗号分割 分别与下面数据源对应 第一个默认为主数据库
data-source1:
type: com.alibaba.druid.pool.DruidDataSource #数据源类型
database: #数据源基本配置
username: root
password: pps123
driver_class_name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/webmodel?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
mybatisConfig:
type-aliases-ackageList: #别名
mapperXmlLocation: #mapper.xml 位置
- classpath:mybatis/security/mapper/*.xml
mapperLocation: #mapper位置
- com.pps.back.frame.pupansheng.core.authority.security.mapper
- com.pps.back.frame.pupansheng.mapper
步骤三 实现
ImportBeanDefinitionRegistrar, EnvironmentAware 两个接口 实现通过读取配置文件信息 动态注入我们希望的注入的组件到spring容器中 这一步非常重要 代码很长 但实现的目的很简单 三步
通过配置信息得到一个数据源列表(用逗号分割) 然后分割 通过前缀获取到具体每一个数据源的属性
对于每一个具体的数据源
首先往容器中添加数据源
然后往容器中添加事务管理器
再然后往容器中添加sqlSessionFactory工厂
结束
其实就是第一种方式自动化了
package com.pps.back.frame.pupansheng.core.datasource;
import com.pps.back.frame.pupansheng.common.util.ValidateUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.type.TypeHandler;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author
* @discription;
* @time 2020/9/11 21:50
*/
@Slf4j
public class MyBatisDataSourceProcessor implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Environment environment;
private final String DATASOURCE_DATA_LIST = "spring.dataSourseList";
private final String DATASOURCE_PREFIX = "spring";
private final String SPILIT = ",";
private final String ADD_CHAR = ".";
private Binder binder;
@SneakyThrows
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
binder = Binder.get(this.getEnvironment());
String dataSourceList = environment.getProperty(DATASOURCE_DATA_LIST);
if (ValidateUtil.isNotEmpty(dataSourceList)) {
log.info("数据源配置开始---------------------------------------------------------------------------------------");
String[] dataSourses = dataSourceList.split(SPILIT);
boolean isFirst=true;
for (String dataSourseName : dataSourses) {
BindResult<BaseSourceConfig> bind = binder.bind(DATASOURCE_PREFIX + ADD_CHAR + dataSourseName, BaseSourceConfig.class);
BaseSourceConfig baseSourceConfig = bind.get();
log.info("数据源:{} 自动配置 开始------------------------------------------------",dataSourseName);
log.info("为数据源:{}配置数据源--------------------------------",dataSourseName);
//配置数据源
{
Map dataSourceProperties= (Map) this.binder.bind(DATASOURCE_PREFIX + ADD_CHAR + dataSourseName+".database", Bindable.of(Map.class)).get();
Class<?> aClass = null;
if(ValidateUtil.isEmpty(baseSourceConfig.getType())||(aClass= Class.forName(baseSourceConfig.getType()))==null){
log.warn("指定的数据源类型为空或类库不存在 !!!应用采用默认的数据库源:{}数据源"," HikariDataSource.class");
aClass= HikariDataSource.class;
}
Map map = validateConfigMap(aClass, dataSourceProperties);
registerBean(beanDefinitionRegistry, dataSourseName,map, aClass,isFirst);
}
log.info("为数据源:{}配置数据源:{} 结束---------------------------",dataSourseName,dataSourseName);
log.info("为数据源:{}配置事务管理器-----------------------------",dataSourseName);
//配置事务管理器
{
Class tr= DataSourceTransactionManager.class;
log.warn("---采用的事务处理管理器为:{}",tr.getName());
registerBean(beanDefinitionRegistry, dataSourseName + "TransactionManager", this.buildTransactionManager(DATASOURCE_PREFIX, dataSourseName,tr), tr,isFirst);
}
log.info("为数据源:{}配置事务管理器 :{} 结束--------------------------",dataSourseName,dataSourseName + "TransactionManager");
log.info("为数据源:{} 配置SqlSessionFactory工厂:{}--------------------------------",dataSourseName,dataSourseName + "SqlSessionFactory");
//配置sessiong工厂
{
registerBean(beanDefinitionRegistry, dataSourseName + "SqlSessionFactory", this.buildSqlSessionFactoryBean(binder, dataSourseName, baseSourceConfig), SqlSessionFactoryBean.class,isFirst);
}
log.info("为数据源:{} 配置SqlSessionFactory工厂:{}---------结束---------------------",dataSourseName,dataSourseName + "SqlSessionFactory");
//注册mapper扫描
log.info("为数据源:{} 配置mapper位置 >>>>>>>>--------------------------------",dataSourseName);
{
registerMapper(beanDefinitionRegistry, baseSourceConfig, dataSourseName,dataSourseName + "SqlSessionFactory");
}
log.info("为数据源:{} 配置mapper位置 结束>>>>>>>>--------------------------------",dataSourseName);
log.info("数据源:{} 自动配置 结束------------------------------------------------",dataSourseName);
if(isFirst) {
isFirst = false;
}
}
} else {
log.warn("未检查到数据源名称 无法启动数据源自动配置");
}
log.info("数据源配置结束---------------------------------------------------------------------------------------");
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Environment getEnvironment() {
return environment;
}
private void registerMapper(BeanDefinitionRegistry beanDefinitionRegistry, BaseSourceConfig baseSourceConfig, String dataSourseName,String sessionFactoryName) {
String[] mappersList = baseSourceConfig.getMybatisConfig().getMapperLocation();
if (ValidateUtil.isEmpty(mappersList)) {
throw new RuntimeException("对于数据源:" + dataSourseName + ",指定的mapper包名 为空");
}
log.info("配置数据源:{} mappper 位置-------------------------------------------------------", dataSourseName);
for (String mapperPackage : mappersList) {
log.info("为数据源:{} 配置dao接口 mapper 位置:{}", dataSourseName, mapperPackage);
ClassPathMapperScanner scanner = new ClassPathMapperScanner(beanDefinitionRegistry);
scanner.setSqlSessionFactoryBeanName(sessionFactoryName);
scanner.registerFilters();
scanner.doScan(mapperPackage);
}
log.info("配置数据源:{} mappper 位置结束-----------------------------------------------------", dataSourseName);
}
private void registerBean(BeanDefinitionRegistry registry, String beanName, Map<String, Object> attributeMap, Class<?> clazz,Boolean primary) {
BeanDefinitionBuilder dbBuilder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
RootBeanDefinition dbBeanDefinition = (RootBeanDefinition) dbBuilder.getBeanDefinition();
dbBeanDefinition.getPropertyValues().addPropertyValues(attributeMap);
dbBeanDefinition.setPrimary(primary);
registry.registerBeanDefinition(beanName, dbBeanDefinition);
}
protected Map<String, Object> buildTransactionManager(String prefix, String beanName,Class tr) {
Map<String, Object> dstmMap = new HashMap(5);
dstmMap.put("dataSource", new RuntimeBeanReference(beanName));
Map<String, Object> map = this.extendAttributes(prefix + "." + beanName + ".transaction");
if(map!=null) {
dstmMap.putAll(validateConfigMap(tr,map));
}
return dstmMap;
}
protected Map<String, Object> extendAttributes(String path) {
Binder binder = Binder.get(this.environment);
try {
return (Map) binder.bind(path, Bindable.of(HashMap.class)).get();
} catch (Exception var4) {
return null;
}
}
private Map<String, Object> buildSqlSessionFactoryBean(Binder binder, String beanName, BaseSourceConfig baseSourceConfig) {
Map<String, Object> sqlMap = new HashMap(5);
class Util {
protected String cutEndChar(List<String> list) {
StringBuilder sb = new StringBuilder();
list.stream().forEach((x) -> {
sb.append(x).append(",");
});
return sb.toString().substring(0, sb.length() - 1);
}
private DatabaseIdProvider buildDatabaseIdProvider() {
DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
Properties properties = new Properties();
Arrays.stream(DriverType.values()).forEach((driverType) -> {
properties.setProperty(driverType.getPattern(), driverType.getType());
});
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
private TypeHandler[] buildTypeHandle() {
TypeHandler[] typeHandlers = new TypeHandler[]{new LvarStringTypeHandler()};
return typeHandlers;
}
}
Util util = new Util();
sqlMap.put("dataSource", new RuntimeBeanReference(beanName));
List<String> typeAliasesPackageList =null;
if (StringUtils.isEmpty(baseSourceConfig.getMybatisConfig().getTypeAliasesPackageList())) {
log.warn("警告:数据源:{}-----------type-aliases-package属性为空",beanName);
}else {
typeAliasesPackageList= Arrays.asList(baseSourceConfig.getMybatisConfig().getTypeAliasesPackageList());
}
if (StringUtils.isEmpty(baseSourceConfig.getMybatisConfig().getMapperXmlLocation())) {
throw new RuntimeException("mapper-xml-location属性为空");
}
List<String> mapperLocationsList = Arrays.asList(baseSourceConfig.getMybatisConfig().getMapperXmlLocation());
if (!ValidateUtil.isEmpty(typeAliasesPackageList)) {
sqlMap.put("typeAliasesPackage", util.cutEndChar(typeAliasesPackageList));
}
sqlMap.put("mapperLocations", mapperLocationsList);
sqlMap.put("databaseIdProvider", util.buildDatabaseIdProvider());
sqlMap.put("typeHandlers", util.buildTypeHandle());
Map<String, Object> extendMap = this.extendAttributes(DATASOURCE_PREFIX + ADD_CHAR + beanName + ".sqlsessionfactory");
if (extendMap != null) {
sqlMap.putAll(extendMap);
}
return sqlMap;
}
protected Map<String, Object> validateConfigMap(Class obj, Map<String, Object> map) {
Map<String, Object> newMap = new HashMap(10);
map.keySet().stream().forEach((key) -> {
String methodName = "get";
//驼峰转换
if(key.contains("_")){
key= Arrays.stream(key.split("_")).map(s->s.substring(0, 1).toUpperCase().concat(s.substring(1))).collect(Collectors.joining());
}
key=key.substring(0, 1).toLowerCase().concat(key.substring(1));
methodName = methodName + key.substring(0, 1).toUpperCase().concat(key.substring(1));
try {
obj.getMethod(methodName);
newMap.put(key, map.get(key));
} catch (Exception var7) {
log.warn("{} 属性: {}不存在 这个配置不合理",obj.getName(), key);
}
});
return newMap;
}
}
补上其他两个辅助类:
public enum DriverType {
MYSQL("MySQL", "MYSQL"),
ORACLE("Oracle", "ORACLE"),
SQLSERVER("SQL Server", "SQLSERVER"),
INFORMIX("Informix", "INFORMIX"),
H2("H2", "H2"),
POSTGRESQL("PostgreSQL", "POSTGRESQL");
private String pattern;
private String type;
private DriverType(String pattern, String type) {
this.pattern = pattern;
this.type = type;
}
public String getPattern() {
return this.pattern;
}
public String getType() {
return this.type;
}
@Override
public String toString() {
return this.type;
}
}
@MappedJdbcTypes({JdbcType.LONGVARCHAR})
@MappedTypes({String.class})
public class LvarStringTypeHandler extends BaseTypeHandler<String> {
public LvarStringTypeHandler() {
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}
步骤四 在启动类加上
@Import(MyBatisDataSourceProcessor.class)
ok 完成
附上 应用启动的日志记录
感谢观看!