SpringBoot配置多数据源
一.分包实现
-
在application.properties中配置两个数据库:
## test1 database spring.datasource.test1.url=jdbc:mysql://localhost:3307/multipledatasource1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.test1.username=root spring.datasource.test1.password=root spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver ## test2 database spring.datasource.test2.url=jdbc:mysql://localhost:3307/multipledatasource2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.test2.username=root spring.datasource.test2.password=root spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver
-
建立2个数据源的配置文件:
第一个配置文件:
//表示这个类为一个配置类 @Configuration // 配置mybatis的接口类放的地方 @MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory") public class DataSourceConfig1 { // 将这个对象放入Spring容器中 @Bean(name = "test1DataSource") // 表示这个数据源是默认数据源 @Primary // 读取application.properties中的配置参数映射成为一个对象 // prefix表示参数的前缀 @ConfigurationProperties(prefix = "spring.datasource.test1") public DataSource getDateSource1() { return DataSourceBuilder.create().build(); } @Bean(name = "test1SqlSessionFactory") // 表示这个数据源是默认数据源 @Primary // @Qualifier表示查找Spring容器中名字为test1DataSource的对象 public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(datasource); bean.setMapperLocations( // 设置mybatis的xml所在位置 new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml")); return bean.getObject(); } @Bean("test1SqlSessionTemplate") // 表示这个数据源是默认数据源 @Primary public SqlSessionTemplate test1sqlsessiontemplate( @Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) { return new SqlSessionTemplate(sessionfactory); } }
第二个配置文件:
@Configuration @MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory") public class DataSourceConfig2 { @Bean(name = "test2DataSource") @ConfigurationProperties(prefix = "spring.datasource.test2") public DataSource getDateSource2() { return DataSourceBuilder.create().build(); } @Bean(name = "test2SqlSessionFactory") public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(datasource); bean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml")); return bean.getObject(); } @Bean("test2SqlSessionTemplate") public SqlSessionTemplate test2sqlsessiontemplate( @Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) { return new SqlSessionTemplate(sessionfactory); } }
注意:
- @Primary这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源)
- mapper的接口、xml形式以及dao层都需要两个分开。
- bean.setMapperLocations(newPathMatchingResourcePatternResolver().getResources(“XXXX”));mapper的xml形式文件位置必须要配置,不然将报错
- 在service层中根据不同的业务注入不同的dao层。
- 如果是主从复制- -读写分离:比如test01中负责增删改,test02中负责查询。但是需要注意的是负责增删改的数据库必须是主库
- 如果是分布式结构的话,不同模块操作各自的数据库就好。test01包下全是test01业务,test02全是test02业务,但是如果test01中掺杂着test02的编辑操作,这时候将会产生事务问题:
二.AOP实现
-
简介:
- AbstractRoutingDataSource:这个类是实现多数据源的关键,他的作用就是动态切换数据源,实质:有多少个数据源就存多少个数据源在targetDataSources(是AbstractRoutingDataSource的一个map类型的属性,其中value为每个数据源,key表示每个数据源的名字)这个属性中,然后根据determineCurrentLookupKey()这个方法获取当前数据源在map中的key值,然后determineTargetDataSource()方法中动态获取当前数据源,如果当前数据源不存并且默认数据源也不存在就抛出异常。
- AOP:面向切面编程,这里理解为在选择数据源的时候进行拦截,然后切换对应的数据源。
-
具体实现:
-
定义一个动态数据源:继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType(); return dataBaseType; } }
-
创建一个切换数据源类型的类: ThreadLocal为了线程的安全性,每个线程之间不会相互影响。
public class DataSourceType { public enum DataBaseType { TEST01, TEST02 } // 使用ThreadLocal保证线程安全 private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>(); // 往当前线程里设置数据源类型 public static void setDataBaseType(DataBaseType dataBaseType) { if (dataBaseType == null) { throw new NullPointerException(); } System.err.println("[将当前数据源改为]:" + dataBaseType); TYPE.set(dataBaseType); } // 获取数据源类型 public static DataBaseType getDataBaseType() { DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.TEST01 : TYPE.get(); System.err.println("[获取当前数据源的类型为]:" + dataBaseType); return dataBaseType; } // 清空数据类型 public static void clearDataBaseType() { TYPE.remove(); } }
-
定义多个数据源:怎么定义就不多说了,和方法一是一样的,主要是将定义好的多个数据源放在动态数据源中。
@Configuration @MapperScan(basePackages = "com.mzd.multipledatasources.mapper", sqlSessionFactoryRef = "SqlSessionFactory") public class DataSourceConfig { @Primary @Bean(name = "test1DataSource") @ConfigurationProperties(prefix = "spring.datasource.test1") public DataSource getDateSource1() { return DataSourceBuilder.create().build(); } @Bean(name = "test2DataSource") @ConfigurationProperties(prefix = "spring.datasource.test2") public DataSource getDateSource2() { return DataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") public DynamicDataSource DataSource(@Qualifier("test1DataSource") DataSource test1DataSource, @Qualifier("test2DataSource") DataSource test2DataSource) { Map<Object, Object> targetDataSource = new HashMap<>(); targetDataSource.put(DataSourceType.DataBaseType.TEST01, test1DataSource); targetDataSource.put(DataSourceType.DataBaseType.TEST02, test2DataSource); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSource); dataSource.setDefaultTargetDataSource(test1DataSource); return dataSource; } @Bean(name = "SqlSessionFactory") public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dynamicDataSource); bean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml")); return bean.getObject(); } }
-
**定义AOP:**就是不同业务切换不同数据库的入口。如果觉得execution太长不愿意写,就可以定义一个注解来实现。
@Aspect @Component public class DataSourceAop { @Before("execution(* com.mzd.multipledatasources.service..*.test01*(..))") public void setDataSource2test01() { System.err.println("test01业务"); DataSourceType.setDataBaseType(DataBaseType.TEST01); } @Before("execution(* com.mzd.multipledatasources.service..*.test02*(..))") public void setDataSource2test02() { System.err.println("test02业务"); DataSourceType.setDataBaseType(DataBaseType.TEST02); } }
-
来源:CSDN
作者:Arkcy
链接:https://blog.csdn.net/arkitocy/article/details/103654740