1. 程式人生 > >spring boot jpa動態切換資料庫

spring boot jpa動態切換資料庫

專案上有兩個資料庫需要切換使用jpa,查閱了網上的資料都是xml進行的配置,所以自己研究了稍加改動。
注:這裡僅適用與1.5.x版本的spring boot 如果是2.0的請自行修改
1.配置資料庫資訊(application.properties):

spring.datasource.url:
spring.datasource.username:
spring.datasource.password:
spring.datasource.initialize=false   

spring.datasource.secondary.url=
spring.datasource.secondary.username=
spring.datasource.secondary.password=
spring.datasource.secondary.driverClassName=com.mysql.jdbc.Driver
spring.datasource.driverClassName:com.mysql.jdbc.Driver
spring.jpa.database-platform:org.hibernate.dialect.MySQLDialect
spring.datasource.maxActive:200
spring.datasource.maxIdle:10
spring.datasource.initialSize:10
spring.datasource.testOnBorrow:true
spring.datasource.validationQuery:select 1
spring.datasource.autoReconnect:true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=false
spring.aop.auto=true

spring.datasource.secondary.maxActive=200
spring.datasource.secondary.maxIdle=10
spring.datasource.secondary.initialSize=10
spring.datasource.secondary.testOnBorrow=true
spring.datasource.secondary.validationQuery=select 1
spring.datasource.secondary.autoReconnect:true
spring.jpa.hibernate.secondary.ddl-auto=none
spring.jpa.secondary.show-sql=false
spring.aop.secondary.auto=true

2.建立儲存資料來源資訊的類,並自定義實現AbstractRoutingDataSource

儲存類:

public class DynamicDataSourceContextHolder {
    /**
     * 當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,
     * 所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。
     */
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    /**
     * 校驗輸入的資料庫名稱是否正確
     */
    private static List<String> dataSourceList=new ArrayList<>();

    /**
     * 使用setDataSourceType設定當前的
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get()==null?"primaryDataSource":contextHolder.get();
    }


    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    public static void saveDataSourceTypeName(String name){
        dataSourceList.add(name);
    }

    public static boolean checkDataSourceType(String name){
        return dataSourceList.contains(name);
    }




}

3.載入資料庫資訊:

@Bean(name = "primaryDataSource")
    @Qualifier("primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource primaryDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryDataSource")
    @Qualifier("secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource(){
        return DataSourceBuilder.create().build();
    }

   @Bean(name="multipleDataSource")
    @Qualifier("multipleDataSource")
    @Primary
    public DataSource MultipleDataSourceToChoose(@Qualifier("primaryDataSource")DataSource dataSource,@Qualifier("secondaryDataSource")DataSource secondaryDataSource){
        DynamicDataSource dynamicDataSource=new DynamicDataSource();
        Map<Object, Object> targetDataSources=new HashMap<>();
        targetDataSources.put("primaryDataSource",dataSource);
        targetDataSources.put("secondaryDataSource",secondaryDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(dataSource);
        DynamicDataSourceContextHolder.saveDataSourceTypeName("primaryDataSource");
        DynamicDataSourceContextHolder.saveDataSourceTypeName("secondaryDataSource");

        return dynamicDataSource;
    }

4.建立自定義註解並利用aop實現動態切換
自定義註解:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ChangDataSource {

    String name() default "primaryDataSource";
}

aop動態切換:

@Aspect
@Order(-10)//保證該AOP在@Transactional之前執行
@Component
public class DynamicDataSourceAspect {

    Logger logger= LoggerFactory.getLogger(DynamicDataSourceAspect.class);

   @Before(value = "@annotation(source)")
    public void changeDataSource(JoinPoint point,ChangDataSource source) throws Exception {
        String name=source.name();
        logger.info("change dataSource :"+name);
        if(!DynamicDataSourceContextHolder.checkDataSourceType(name)){
            throw new Exception("DataSource Not Exist,Please Check Your Annotation");
        }
        DynamicDataSourceContextHolder.setDataSourceType(name);

    }

    @AfterReturning(value = "@annotation(source)")
    public void restoreDataSource(JoinPoint point,ChangDataSource source) {
        //方法執行完畢之後,銷燬當前資料來源資訊,進行垃圾回收。
        DynamicDataSourceContextHolder.clearDataSourceType();
        logger.info("clear change dataSource");
    }



}