• 多数据源配置


    前面我们配置过单个数据源了,本节讲解下如何实现多数据源的动态切换(c3p0和druid)。

    修改下数据源的连接,使其不属于同一个数据库:

    # c3p0.properties
    c3p0.jdbc.jdbcUrl=jdbc:mysql://localhost:3305/spring?useSSL=false&characterEncoding=UTF-8
    
    # druiddb.properties
    druid.jdbc.url=jdbc:mysql://127.0.0.1:3305/db_link?characterEncoding=utf-8&useSSL=false
     org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

    AbstractRoutingDataSource本身实现了javax.sql.DataSource接口(由其父类抽象类AbstractDataSource实现),因此其实际上也是一个标准数据源的实现类。该类是Spring专为多数据源管理而增加的一个接口层。它根据一个数据源唯一标识key来寻找已经配置好的数据源队列,它通常是与当前线程绑定在一起的。

    AbstractRoutingDataSource的内部维护了一个名为targetDataSources的Map,并提供的setter方法用于设置数据源关键字与数据源的关系,实现类被要求实现其determineCurrentLookupKey()方法,由此方法的返回值决定具体从哪个数据源中获取连接。

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * DynamicDataSource类继承了Spring的抽象类AbstractRoutingDataSource,
     * 而AbstractRoutingDataSource本身实现了javax.sql.DataSource接口(由其父类抽象类AbstractDataSource实现),
     * 因此其实际上也是一个标准数据源的实现类。该类是Spring专为多数据源管理而增加的一个接口层。
     * 它根据一个数据源唯一标识key来寻找已经配置好的数据源队列,它通常是与当前线程绑定在一起的。
     *
     * AbstractRoutingDataSource的内部维护了一个名为targetDataSources的Map,
     * 并提供的setter方法用于设置数据源关键字与数据源的关系,实现类被要求实现其determineCurrentLookupKey()方法,
     * 由此方法的返回值决定具体从哪个数据源中获取连接。
     *
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            String dataSource = DynamicDataSourceContextHolder.getDataSourceType();
            System.out.println("当前数据源:" + dataSource);
            return dataSource;
        }
    
    }
    设置数据源的工具类DynamicDataSourceContextHolder 
    public class DynamicDataSourceContextHolder {
    
        //存放当前线程使用的数据源类型信息
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    
        //存放数据源id
        public static List<String> dataSourceIds = new ArrayList();
    
        //设置数据源
        public static void setDataSourceType(String dataSourceType) {
            contextHolder.set(dataSourceType);
        }
    
        //获取数据源
        public static String getDataSourceType() {
            return contextHolder.get();
        }
    
        //清除数据源
        public static void clearDataSourceType() {
            contextHolder.remove();
            System.out.println("清除数据源:" + contextHolder.get());
        }
    
        //判断当前数据源是否存在
        public static boolean isContainsDataSource(String dataSourceId) {
            return dataSourceIds.contains(dataSourceId);
        }
    }
    创建动态数据源和JdbcTemplate

    在Spring根容器(SpringConfig)中使用@Bean创建动态数据源和JdbcTemplate。

    /**
     * 这个是Spring容器,相当于ApplicationContext.xml,负责扫描相关的service和dao,排除controller的扫描
     * 数据源、事务等均在这里配置
     */
    @ComponentScan(value = {"com.codedot"},
            excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}),
                    @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {RestController.class})})
    @Import({C3p0DBConfig.class, DruidDBConfig.class})
    @PropertySource(value = {"classpath:c3p0.properties", "classpath:druiddb.properties"}) //读取resources下的配置文件
    @Configuration
    public class SpringConfig {
    
    
        @Bean
        public JdbcTemplate jdbcTemplate(DynamicDataSource dynamicDataSource) {
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dynamicDataSource);
            return jdbcTemplate;
        }
        
        // 前提:需要将DruidDataSource和ComboPooledDataSource的单数据源,可以查看单数据源的配置方式
        @Bean
        public DynamicDataSource dynamicDataSource(DruidDataSource druidDataSource, ComboPooledDataSource c3p0DataSource) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            dynamicDataSource.setDefaultTargetDataSource(c3p0DataSource);
            Map<Object, Object> targetDataSourceMap = new HashMap();
            targetDataSourceMap.put("defaultTargetDataSource", c3p0DataSource);
            targetDataSourceMap.put("druidTargetDataSource", druidDataSource);
            dynamicDataSource.setTargetDataSources(targetDataSourceMap);
            return dynamicDataSource;
        }
    }
    使用AOP拦截JdbcTemplate方法

    注意:需要在Spring根容器(SpringConfig)添加类注解@EnableAspectJAutoProxy(proxyTargetClass = true)来开启aop。

    /**
     * 这里使用JdbcTemplate操作数据库,所以选择JdbcTemplate的方法作为切入点
     */
    @Component
    @Order(-1)//这里一定要保证在@Transactional之前执行
    @Aspect
    public class TemplateDbAspect {
    
        @Before("execution(* org.springframework.jdbc.core.JdbcTemplate.update*(..)) || execution(* org.springframework.jdbc.core.JdbcTemplate.batchUpdate(..))")
        public void setMasterDb(){
            System.out.println("master db");
            DynamicDataSourceContextHolder.setDataSourceType("defaultTargetDataSource");
        }
    
        @Before("execution(* org.springframework.jdbc.core.JdbcTemplate.query*(..)) || execution(* org.springframework.jdbc.core.JdbcTemplate.execute(..))")
        public void setSlaveDb(){
            System.out.println("slave db");
            DynamicDataSourceContextHolder.setDataSourceType("druidTargetDataSource");
        }
    }
    单元测试
    @RunWith(SpringRunner.class)
    @WebAppConfiguration
    @ContextHierarchy({
            @ContextConfiguration(classes = SpringConfig.class),
            @ContextConfiguration(classes = SpringMVCConfig.class)
    })
    public class DbTest {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Test
        public void test() throws SQLException {
            //DynamicDataSourceContextHolder.setDataSourceType("defaultTargetDataSource");
            System.out.println(jdbcTemplate.getDataSource());
            jdbcTemplate.update("update sp_user set name = 'codedot' where id = 5");
            //DynamicDataSourceContextHolder.setDataSourceType("druidTargetDataSource");
            Map<String, Object> userMap = jdbcTemplate.queryForMap("select * from fm_user where id = 1");
            System.out.println(jdbcTemplate.getDataSource());
            System.out.println(userMap.get("name"));
        }
    }

    有时我们需要在自定义的方法上动态切入数据源,可以定义一个注解,使用aop在方法进入前解析注解,获取注解中指定的数据源key,并进行设置,来达到动态切换的效果。

  • 相关阅读:
    Spring MVC @RequestMapping注解详解
    (转)Cesium教程系列汇总
    spring boot +mybatis(通过properties配置) 集成
    SpringBoot2.0之四 简单整合MyBatis
    在Windows下使用Git+TortoiseGit+码云管理项目代码
    TortoiseGit之配置密钥
    Spring Boot 学习之路二 配置文件 application.yml
    SpringBoot学习笔记(2) Spring Boot的一些配置
    【入门】Spring-Boot项目配置Mysql数据库
    Spring 的application.properties项目配置与注解
  • 原文地址:https://www.cnblogs.com/myitnews/p/13337380.html
Copyright © 2020-2023  润新知