springboot入门02 – 自定义数据源

spring-boot的自动化配置中是包含数据源连接配置的。但有些时候我们需要自定义数据源连接的配置,比如:

  • 使用的数据库连接池Spring暂时还不支持;
  • 需要配置连接多数据源;
  • 需要自定义一些数据库连接配置项。

这三种只是我曾经遇到的情形,当然还有些其他的情形。接下来就以曾经遇到的一个问题进行展开。

自定义数据源

我们在生产环境使用的数据库连接池是alibaba的druid。数据库连接配置大致是这样的:

spring:
  datasource:
    type:  com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:worker

这里我用h2的内存数据库做个演示。在启动的时候会遇到一个错误日志:

2019-09-08 17:11:15.129 ERROR 2088 --- [           main] com.alibaba.druid.pool.DruidDataSource   : testWhileIdle is true, validationQuery not set

解决这个错误的方法按理来说是很简单,只需要在数据库的配置中添加一行

validation

query


就好了:

validation-query: select 1

不过不幸的是SpringBoot目前的版本( 2.1.7.RELEASE
)中的数据库连接自动化配置中没有

validation

query


选项,也不支持druid数据库连接池的自动化配置。
要解决这个问题,自定义Druid的自动化配置当然是一个好办法;另一个较为简单些的办法就是使用自定义数据源连接配置了。
首先,修改下项目配置:

spring:
  application:
    name: spring-boot-database
 
custom:
  datasource:
    worker:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: org.h2.Driver
      url: jdbc:h2:mem:worker
      validation-query: select 1

为了避免因自动化配置产生干扰,这里将数据库连接配置移动到了 custom
下。
然后,创建一个数据库连接配置类:

@Configuration
@MapperScan(basePackages = "org.chobit.spring.service.mapper.worker",
        sqlSessionFactoryRef = "workerSqlSessionFactory")
public class WorkerDbConfig {
 
 
    @Bean(name = "workerDataSource")
    @ConfigurationProperties(prefix = "custom.datasource.worker")
    public DataSource setDataSource() {
        return new DruidDataSource();
    }
 
 
    @Bean(name = "workerTransactionManager")
    public DataSourceTransactionManager setTransactionManager(@Qualifier("workerDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
 
 
    @Bean(name = "workerSqlSessionFactory")
    public SqlSessionFactory setSqlSessionFactory(@Qualifier("workerDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }
}

在配置类的

MapperScan


注解中,通过 basePackages
指明了该数据源所有相关的Mapper类的位置。另外,为了支持事务,这里还创建了

DataSourceTransactionManager


的Bean实例。如果不需要事务支持可以取消这个Bean方法。

MyBatis有个非常有用的配置项“mapUnderscoreToCamelCase”用来自动识别下划线并转为驼峰结构,这个配置项需要在

setSqlSessionFactory


方法中完成配置:

    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
 
    org.apache.ibatis.session.Configuration conf = new org.apache.ibatis.session.Configuration();
    conf.setMapUnderscoreToCamelCase(true);
    bean.setConfiguration(conf);

这样完成配置后,启动程序可以看到日志中的ERROR信息消失。
使用自定义数据源,还意味着需要承担一些损失,比如:

  • 大部分配置项没有默认值,需要手动配置;
  • springboot原生数据源一些非常有用的特性如 schema
    platform
    将不可用;
  • MyBatis的自动化配置将不可用

如果不想承担这些损失,我的建议是:换个数据库连接池试试,如Hikari或DBCP2。

多数据源支持

多数据源通常是自定义数据源配置应用最多的场景了,接下来演示下是如何完成的。
首先,肯定是要在spring配置文件中添加另一个数据库的配置信息了:

custom:
  datasource:
    worker:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: org.h2.Driver
      url: jdbc:h2:mem:worker
    master:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: org.h2.Driver
      url: jdbc:h2:mem:master

这里添加了另一个h2内存数据库 master
,然后添加master数据库对应的数据源配置:

@Configuration
@MapperScan(basePackages = "org.chobit.spring.service.mapper.master",
        sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDbConfig {
 
    @Primary
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "custom.datasource.master")
    public DataSource setDataSource() {
        return new DruidDataSource();
    }
 
    @Primary
    @Bean(name = "masterTransactionManager")
    public DataSourceTransactionManager setTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
 
    @Primary
    @Bean(name = "masterSqlSessionFactory")
    public SqlSessionFactory setSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
 
        return bean.getObject();
    }
}

可以看到Master数据库的配置与Worker数据库的差别不大。唯一不同的是Master数据库配置中,每个Bean的set方法上多了一个

@
Primary


注解。



@
Primary


注解表示同一个类出现多个可用Bean时,将绑定

@
Primary


注解的Bean。这里的几个Bean,transactionManager的

@
Primary


注解不使用事务的话可以省略,sqlSessionFactory的

@
Primary


注解没有的话使用某些spring版本启动会出错。不过在我的测试应用(版本:springboot 2.1.7.RELEASE)中,没有使用

@
Primary


也没问题。通过debug日志可以看到,使用

@
Primary


注解会影响数据源自动化配置和MyBatis自动化配置,前者用不着,后者用不了,所以也就没啥影响了。

transactionManager的

@
Primary


注解还是有用的。如果没有

@
Primary


注解,为方法添加事务注解需要指明使用哪个transactionManager的Bean,如下:

    @Transactional(rollbackFor = Exception.class, transactionManager = "masterTransactionManager")
    public Master get0() {
        insert();
        Master m = get(1);
        if (null == m.getJobTitle()) {
            throw new RuntimeException();
        }
        return m;
    }

如果为transactionManager添加了

@
Primary


注解,就会默认使用

@
Primary


注解的transactionManager。因此建议为使用事务较多的数据源的transactionManager添加

@
Primary


注解。

还有一点,每个数据源指向的 basePackages
是不一样的,需要将不同数据源的Mapper类置于不同的目录下。

写了一个测试应用 spring-boot-database
,上传到了GitHub,有需要可以参考下。