背景

  项目要求,开发环境使用postgres数据库,给客户部署时使用oracle,而只需要修改一个配置来完成。

方案

  1. 对于单数据源,保证容器中只有一个DataSource。
  2. 对于多数据源,保证需要灵活变化的那个DataSource容器中只存在一个。
  3. 项目中应该是一套Mapper接口,两套Mapper.xml。不同的Driver,扫对应包下的Mapper.xml

SpringBoot篇

  Springboot架构下,我们可以通过@Configuration来手动配置数据源,实现方式如下:

单数据源情况

1.通过spring.profile.active控制

例:sit配置postgre,pro配置oracle,
使用@Configuration手动配置数据源,在配置sqlSessionFactory时根据DriverType来指定扫对应的包,下面是代码示例:

application-sit.yml配置postgre数据源:

datasource:
  driver-class-name: org.postgresql.Driver
  url: jdbc:postgresql://localhost:5432/pgsqltest
  username: xx
  password: xx

application-dev.yml配置oracle数据源:

datasource:
  driver-class-name: oracle.jdbc.driver.OracleDriver
  url: jdbc:oracle:thin:@//xx:xx/xx
  username: xx
  password: xx

数据源配置类代码如下:

@Configuration
@MapperScan(basePackages = "com.bigblue.mapper")
public class DataSourceConfig {

    private static final String TYPE_ORACLE = "oracle";
    private static final String TYPE_MYSQL = "mysql";
    private static final String TYPE_POSTGRE = "postgre";

    @Bean
    @ConfigurationProperties(prefix = "datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        String driverClassName = ((DruidDataSource) dataSource).getDriverClassName();
        //根据具体的驱动类型,扫对应的包
        String driverType = TYPE_MYSQL;
        if(driverClassName.contains(TYPE_ORACLE)){
            driverType = TYPE_ORACLE;
        }else if(driverClassName.contains(TYPE_POSTGRE)) {
            driverType = TYPE_POSTGRE;
        }
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/"+driverType+"/**/**Mapper.xml"));
        return bean.getObject();
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

  可以看到指定读取的属性前缀是datasource下的属性,然后在创建sqlSessionFactory时,根据具体的DriverType来扫对应的包下xml,这个时候就可以根据spring.profile.active配置指定激活哪个环境了,如果激活sit环境,则就会注入postgre的datasource,并且扫/mapper/postgre/**/Mapper.xml相关的文件;如果激活了oracle环境,则会注入oracle的datasource,扫/mapper/oracle//**Mapper.xml相关的文件。

2.通过指定的配置参数决定数据源

一个环境中指定了2个数据源,但只用其中一个,根据配置来决定用哪个数据源
application-dev.yml配置如下:

datasource:
  active: datasource.postgre
  postgre:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/pgsqltest
    username: xx
    password: xx
  oracle:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@//xx:xx/xx
    username: xx
    password: xx

数据源配置类代码如下:

@Configuration
@MapperScan(basePackages = "com.bigblue.mapper")
public class DataSourceConfig {

    private static final String TYPE_ORACLE = "oracle";
    private static final String TYPE_MYSQL = "mysql";
    private static final String TYPE_POSTGRE = "postgre";

    @Autowired
    private ApplicationContext context;

    @Bean
//    @ConfigurationProperties(prefix = "datasource")
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        String activeDataSource = context.getEnvironment().getProperty("datasource.active");
        if (StringUtils.isEmpty(activeDataSource)) {
            //如果该配置为空,则读取默认配置,
            configDataSource(dataSource, "datasource.driver-class-name",
                    "datasource.url", "datasource.username", "datasource.password");
        } else {
            //如果该配置不为空,读取该配置激活的数据源配置
            configDataSource(dataSource, activeDataSource + ".driver-class-name",
                    activeDataSource + ".url", activeDataSource + ".username", activeDataSource + ".password");
        }
        return dataSource;
    }

    private void configDataSource(DruidDataSource dataSource, String driverConf, String urlConf, String nameConf, String passwdConf) {
        dataSource.setDriverClassName(getProp(driverConf));
        dataSource.setUrl(getProp(urlConf));
        dataSource.setUsername(getProp(nameConf));
        dataSource.setPassword(getProp(passwdConf));
    }

    private String getProp(String confName) {
        return context.getEnvironment().getProperty(confName);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        String driverClassName = ((DruidDataSource) dataSource).getDriverClassName();
        //根据具体的驱动类型,扫对应的包
        String driverType = TYPE_MYSQL;
        if (driverClassName.contains(TYPE_ORACLE)) {
            driverType = TYPE_ORACLE;
        } else if (driverClassName.contains(TYPE_POSTGRE)) {
            driverType = TYPE_POSTGRE;
        }
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/" + driverType + "/**/**Mapper.xml"));
        return bean.getObject();
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

  可以看到,配置类仍然只有一个,目的是保证容器中只有一个DataSource。因为yml配置有两个datasource,但我们一个环境只激活一个,我们可以通过datasource.active这个属性来配置需要激活的数据源配置前缀。下面sqlSessionFactory的创建和上面的一样,都是根据DriverType来决定扫对应的xml。

多数据源情况

  其实和单数据源是一样的,无非多出来的那个数据源,再用另外一个配置类把多出来这个数据源注入即可。
例:yml中配置如下,其中oracle2是第二个数据源,postgre和oracle1只会启用其中一个:

datasource:
  active: datasource.postgre
  postgre:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/pgsqltest
    username: xx
    password: xx
  oracle1:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@//xx:xx/xx
    username: xx
    password: xx
  oracle2:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@//xx:xx/xx
    username: xx
    password: xx

配置类需要两个,一个配置类注入oracle2,一个配置类注入postgre或oracle1,根据配置决定,代码如下:
Oracle2的配置类代码:

@Configuration
@MapperScan(basePackages = "com.bigblue.mapper.oracle2", sqlSessionFactoryRef = "oracle2SqlSessionFactory")
public class OracleDataSource {

    @Bean(name = "oracle2DS")
    @ConfigurationProperties(prefix = "datasource.oracle2")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "oracle2SqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("oracle2DS") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/oracle2/**Mapper.xml"));
        return bean.getObject();
    }

    @Bean(name = "oracle2TransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("oracle2DS") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

  可以看到该配置类将oracle2数据源注入,beanId是oracle2DS,并且扫描oracle2文件夹下mapper和xml,使用oracle2SqlSessionFactory。

postgre和oracle1切换配置类代码:

@Configuration
@MapperScan(basePackages = "com.bigblue.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class DataSourceConfig {

    private static final String TYPE_ORACLE = "oracle";
    private static final String TYPE_MYSQL = "mysql";
    private static final String TYPE_POSTGRE = "postgre";

    @Autowired
    private ApplicationContext context;

    @Bean(name = "primaryDS")
//    @ConfigurationProperties(prefix = "datasource")
    @Primary
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        String activeDataSource = context.getEnvironment().getProperty("datasource.active");
        if (StringUtils.isEmpty(activeDataSource)) {
            //如果该配置为空,则读取默认配置,
            configDataSource(dataSource, "datasource.driver-class-name",
                    "datasource.url", "datasource.username", "datasource.password");
        } else {
            //如果该配置不为空,读取该配置激活的数据源配置
            configDataSource(dataSource, activeDataSource + ".driver-class-name",
                    activeDataSource + ".url", activeDataSource + ".username", activeDataSource + ".password");
        }
        return dataSource;
    }

    private void configDataSource(DruidDataSource dataSource, String driverConf, String urlConf, String nameConf, String passwdConf) {
        dataSource.setDriverClassName(getProp(driverConf));
        dataSource.setUrl(getProp(urlConf));
        dataSource.setUsername(getProp(nameConf));
        dataSource.setPassword(getProp(passwdConf));
    }

    private String getProp(String confName) {
        return context.getEnvironment().getProperty(confName);
    }

    @Bean(name = "primarySqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("primaryDS") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        String driverClassName = ((DruidDataSource) dataSource).getDriverClassName();
        //根据具体的驱动类型,扫对应的包
        String driverType = TYPE_MYSQL;
        if (driverClassName.contains(TYPE_ORACLE)) {
            driverType = TYPE_ORACLE;
        } else if (driverClassName.contains(TYPE_POSTGRE)) {
            driverType = TYPE_POSTGRE;
        }
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/" + driverType + "/**/**Mapper.xml"));
        return bean.getObject();
    }

    @Bean(name = "primaryTransactionManager")
    @Primary
    public DataSourceTransactionManager transactionManager(@Qualifier("primaryDS") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

Spring篇

  Springboot其实就是通过注解和配置类取代了xml的配置方式,cfs的DataSource、SqlSessionFactory、TransctionManager都是通过xml方式配置的,已经是通过配置的方式来决定数据源了,这里就不再赘述了。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐