spring-boot的自动化配置中是包含数据源连接配置的。但有些时候我们需要自定义数据源连接的配置,比如:
这三种只是我曾经遇到的情形,当然还有些其他的情形。接下来就以曾经遇到的一个问题进行展开。
我们在生产环境使用的数据库连接池是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信息消失。
使用自定义数据源,还意味着需要承担一些损失,比如:
如果不想承担这些损失,我的建议是:换个数据库连接池试试,如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,有需要可以参考下。