zoukankan      html  css  js  c++  java
  • 多数据源配置

    前面我们配置过单个数据源了,本节讲解下如何实现多数据源的动态切换(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,并进行设置,来达到动态切换的效果。

  • 相关阅读:
    MKMapVIew学习系列2 在地图上绘制出你运行的轨迹
    WPF SDK研究 Intro(6) WordGame1
    WPF SDK研究 Intro(3) QuickStart3
    WPF SDK研究 Layout(1) Grid
    WPF SDK研究 目录 前言
    WPF SDK研究 Intro(7) WordGame2
    WPF SDK研究 Layout(2) GridComplex
    对vs2005创建的WPF模板分析
    WPF SDK研究 Intro(4) QuickStart4
    《Programming WPF》翻译 第6章 资源
  • 原文地址:https://www.cnblogs.com/myitnews/p/13337380.html
Copyright © 2011-2022 走看看