Springboot+Mybatis/JPA 动态数据源切换

最终结果:动态的根据不同的方法访问不同的数据库

代码也不记得是在哪复制的了

 

application.properties

spring.primary-datasource.driver-class-name =org.postgresql.Driver
spring.primary-datasource.url = jdbc:postgresql://xxxxxxxxx/primary
spring.primary-datasource.username = postgres
spring.primary-datasource.password = 123

spring.second-datasource.driver-class-name = org.postgresql.Driver
spring.second-datasource.url = jdbc:postgresql://xxxxxxxxx/second
spring.second-datasource.username = postgres
spring.second-datasource.password = 123

 

1.

package net.itraf.drivingbehavior.common.DynamicDataSource;

import java.util.ArrayList;
import java.util.List;

/**
 * CreateTime: 2018-08-30 10:54
 * ClassName: DynamicDataSourceContextHolder
 * Package: net.itraf.drivingbehavior.common.util
 * Describe:
 * 动态数据源上下文
 *
 * @author JONEE
 */

public class DynamicDataSourceContextHolder{

    /**
     * 默认数据源
     */
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {

        /** * 将 master 数据源的 key 作为默认数据源的 key */
        @Override
        protected String initialValue() {
            return "primaryDataSource";
        }
    };


    /** * 数据源的 key 集合,用于切换时判断数据源是否存在 */
    public static List<Object> dataSourceKeys = new ArrayList<>();

    /** * To switch DataSource * * @param key the key */
    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    /** * Get current DataSource * * @return data source key */
    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    /** * To set DataSource as default */
    public static void clearDataSourceKey() {
        contextHolder.remove();
    }

    /** * Check if give DataSource is in current DataSource list * * @param key the key * @return boolean boolean */
    public static boolean containDataSourceKey(String key) {
        return dataSourceKeys.contains(key);
    }

}

 

2.

package net.itraf.drivingbehavior.common.DynamicDataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * CreateTime: 2018-08-30 10:58
 * ClassName: DataSourceConfig
 * Package: net.itraf.drivingbehavior.common.util
 * Describe:
 * 数据源配置
 *
 * @author JONEE
 */
@Configuration
public class DataSourceConfig{

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

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

        @Autowired
        @Qualifier("primaryDataSource")
        private DataSource primaryDataSource;

        @Autowired
        @Qualifier("secondaryDataSource")
        private DataSource secondaryDataSource;

        @Bean("dynamicDataSource")
        public DynamicDataSource dynamicDataSource(){

        DynamicDataSource dataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put("primaryDataSource", primaryDataSource);
        targetDataSources.put("secondaryDataSource", secondaryDataSource);

        DynamicDataSourceContextHolder.dataSourceKeys.addAll(targetDataSources.keySet());

        //设置数据源映射
        dataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源,当无法映射到数据源时会使用默认数据源
        dataSource.setDefaultTargetDataSource(primaryDataSource);
        dataSource.afterPropertiesSet();

        return dataSource;
    }

    /**
     * 配置 SqlSessionFactoryBean
     * @ConfigurationProperties 在这里是为了将 MyBatis 的 mapper 位置和持久层接口的别名设置到
     * Bean 的属性中,如果没有使用 *.xml 则可以不用该配置,否则将会产生 invalid bond statement 异常
     *
     * @return the sql session factory bean
     */
    @Bean
    @ConfigurationProperties(prefix = "mybatis")
    public SqlSessionFactoryBean sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource 作为数据源则不能实现切换
        sqlSessionFactoryBean.setDataSource(dynamicDataSource());
        //PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/**/*.xml"));
        return sqlSessionFactoryBean;
    }

    /**
     * 注入 DataSourceTransactionManager 用于事务管理
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }
}

 

3.

package net.itraf.drivingbehavior.common.DynamicDataSource;

import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * CreateTime: 2018-08-29 17:22
 * ClassName: ApplicationConfiguration
 * Package: net.itraf.drivingbehavior.common.util
 * Describe:
 * 动态数据源配置文件
 *
 * @author JONEE
 */
@Configuration
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

 

4.

package net.itraf.drivingbehavior.common.DynamicDataSource;

import java.lang.annotation.*;

/**
 * CreateTime: 2018-08-30 11:15
 * ClassName: TargetDataSource
 * Package: net.itraf.drivingbehavior.common.DynamicDataSource
 * Describe:
 * 动态数据源自定义注解
 *
 * @author JONEE
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {

    String value() default "primaryDataSource";;

}

 

5.

package net.xxx.xxx.common.DynamicDataSource;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * CreateTime: 2018-08-30 14:08
 * ClassName: DynamicDataSourceAspect
 * Package: net.xxx.xxx.common.DynamicDataSource
 * Describe:
 * 动态数据源切面类
 *
 * @author JONEE
 */
@Aspect
// 该切面应当先于 @Transactional 执行
@Order(-1)
@Component
public class DynamicDataSourceAspect {

    @Before("@annotation(targetDataSource))")
    public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())) {
            System.out.println("DataSource [{}] doesn't exist, use default DataSource [{}] " + targetDataSource.value());
        } else {
            // 切换数据源
            DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value());
            System.out.println("Switch DataSource to [{}] in Method [{}] " +
                    DynamicDataSourceContextHolder.getDataSourceKey() + point.getSignature());
        }
    }


    @After("@annotation(targetDataSource))")
    public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        // 将数据源置为默认数据源
        DynamicDataSourceContextHolder.clearDataSourceKey();
        System.out.println("Restore DataSource to [{}] in Method [{}] " +
                DynamicDataSourceContextHolder.getDataSourceKey() + point.getSignature());
    }
}

JPA:

package net.data.common.dynamicdatasource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * CreateTime: 2019-04-09 17:00
 * ClassName: DataSourceConfig
 * Package: net.data.common.DynamicDataSource
 * Describe:
 *
 * @author JONEE
 */

@Configuration
@EnableJpaRepositories(value = "net.xxx.xxx",entityManagerFactoryRef = "EntityManagerFactory",transactionManagerRef="TransactionManager")
public class DataSourceConfig{

    @Autowired
    JpaProperties jpaProperties;

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

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

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    @Bean("dynamicDataSource")
    public DynamicDataSource dynamicDataSource(){
        DynamicDataSource dataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put("primaryDataSource", primaryDataSource);
        targetDataSources.put("secondaryDataSource", secondaryDataSource);
        DynamicDataSourceContextHolder.dataSourceKeys.addAll(targetDataSources.keySet());
        //设置数据源映射
        dataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源,当无法映射到数据源时会使用默认数据源
        dataSource.setDefaultTargetDataSource(primaryDataSource);
        dataSource.afterPropertiesSet();
        return dataSource;
    }

    @Bean(name = "EntityManagerFactory")
    @Primary
    public EntityManagerFactory EntityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("net.xxx.xxx");
        factory.setDataSource(dynamicDataSource());
        factory.setJpaPropertyMap(jpaProperties.getProperties());
        factory.afterPropertiesSet();//在完成了其它所有相关的配置加载以及属性设置后,才初始化
        return factory.getObject();
    }
    /**
     * 配置事物管理器
     * @return
     */
    @Bean(name = "TransactionManager")
    @Primary
    public PlatformTransactionManager TransactionManager() {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(this.EntityManagerFactory());
        return jpaTransactionManager;
    }

}

 

使用:

在Service方法上加 @TargetDataSource(value = "secondaryDataSource")

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值