記錄一下java開發中多數據源的配置過程,
參考博客:https://blog.csdn.net/weinichendian/article/details/72903757,我在這里進行了整理,使用yml;排除了里邊在springboot2.0報錯的內容,以及里邊沒有說太清楚的內容,進行了詳細的說明
1.配置文件application.yml
personnel:#數據源1 driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3307/person?useSSL=false&useUnicode=true&characterEncoding=utf8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&allowMultiQueries=true&allowPublicKeyRetrieval=true username: root password: ******* userauth:#數據源2 driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3307/user?useSSL=false&useUnicode=true&characterEncoding=utf8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&allowMultiQueries=true&allowPublicKeyRetrieval=true username: root password: ******
2.不同數據源枚舉:
public enum DataSourceEnum {master,slaver;}
3.枚舉類工具 set
public class DataSourceContextHolder { private static final ThreadLocal<DataSourceEnum> CONTEXT_HOLDER = new ThreadLocal<DataSourceEnum>() { @Override protected DataSourceEnum initialValue() { return DataSourceEnum.master; } }; public static void setDataSourceType(DataSourceEnum type) { CONTEXT_HOLDER.set(type); } public static DataSourceEnum getDataSourceType() { return CONTEXT_HOLDER.get(); } public static void resetDataSourceType() { CONTEXT_HOLDER.set(DataSourceEnum.master); } }
4.自定義注解
@Retention(RetentionPolicy.RUNTIME) // 在運行時可見 @Target(ElementType.METHOD) // 注解可以用在方法上 public @interface DataSourceTypeAnno { //使用方式在service層方法上添加@DataSourceTypeAnno(DataSourceEnum.數據源枚舉類型)用於指定所使用的數據源
DataSourceEnum value() default DataSourceEnum.master; }
5.aop攔截:這里使用asp的
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Component @Aspect @Order(-100)//為了保證可以攔截到 public class DataSourceAspect { @Pointcut("execution(* com.yzy.*.*..*(..)) " + //這里掃描的切點包是主要是service層,根據service層方法的上邊所說的的自定義注解,去判斷所使用的數據源類型,並動態切換數據源 "&& @annotation(com.yzy.config.DataSourceTypeAnno)") public void dataSourcePointcut() { } @Around("dataSourcePointcut()") public Object doAround(ProceedingJoinPoint pjp) { MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); Method method = methodSignature.getMethod(); DataSourceTypeAnno typeAnno = method.getAnnotation(DataSourceTypeAnno.class); DataSourceEnum sourceEnum = typeAnno.value(); if (sourceEnum == DataSourceEnum.master) { DataSourceContextHolder.setDataSourceType(DataSourceEnum.master); } else if (sourceEnum == DataSourceEnum.slaver) { DataSourceContextHolder.setDataSourceType(DataSourceEnum.slaver); } Object result = null; try { result = pjp.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } finally { DataSourceContextHolder.resetDataSourceType(); } return result; } }
6.繼承 AbstractRoutingDataSource 類,實現對應數據源key的切換
package com.yzy.config; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } }
7.配置mybatisConfig=》數據源信息
import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.jdbc.DataSourceBuilder;//org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;該包在springboot2.0被取代,目前網上的都是這個springboot1.5的包,所以會一直報錯 import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration @MapperScan(basePackages = "com.yzy.*.mapper")//掃描dao層mapper接口 public class MyBatisConfig { /** * @return * @throws Exception * @Primary 必需指定一個且只能有一個主數據源,否則報錯 */ @Primary @Bean("masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.userauth")//根據數據源前綴到application.yml讀取數據源信息//此處改變前綴可以改變默認數據源// public DataSource masterDataSource() throws Exception { return DataSourceBuilder.create().build(); } @Bean("slaverDataSource") @ConfigurationProperties(prefix = "spring.datasource.personnel")//根據數據源前綴到application.yml讀取數據源信息//可以配置更多數據源,到前提是application.yml中存在,而且也需要在枚舉類中添加枚舉類型 public DataSource slaverDataSource() throws Exception { return DataSourceBuilder.create().build(); } /** * @Qualifier 根據名稱進行注入,通常是在具有相同的多個類型的實例的一個注入(例如有多個DataSource類型的實例) * @DataSourceTypeAnno(DataSourceEnum.master)事務方法需要指定數據源 */ @Bean("dynamicDataSource") public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaverDataSource") DataSource slaverDataSource) { Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); targetDataSources.put(DataSourceEnum.master, masterDataSource); targetDataSources.put(DataSourceEnum.slaver, slaverDataSource); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources);// 該方法是AbstractRoutingDataSource的方法 dataSource.setDefaultTargetDataSource(masterDataSource);// 默認的datasource設置為myTestDbDataSource return dataSource; } /** * 根據數據源創建SqlSessionFactory */ @Bean public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource, @Value("mybatis.type-aliases-package") String typeAliasesPackage) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dynamicDataSource);// 指定數據源(這個必須有,否則報錯) // 下邊兩句僅僅用於*.xml文件,如果整個持久層操作不需要使用到xml文件的話(只用注解就可以搞定),則不加 factoryBean.setTypeAliasesPackage(typeAliasesPackage);// 指定實體類所在的包 factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/**/*Mapper.xml"));//掃描mapper.xml文件包 return factoryBean.getObject(); } /** * 配置事務管理器 */ @Bean public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception { return new DataSourceTransactionManager(dataSource); } }