springboot配置多数据源

摘要: 目前项目中持久化框架使用是mybatis,经过分析后不难发现,多数据源配置需要解决两个问题,一个是由原先的spring经典方式切换到了springboot方式下,多数据源如何配置?有无太大变化?另一个是怎样将多数据源与mybatis的配置关联起来?

前言

最近迫于项目需要,笔者踏上了springboot多数据源的配置之旅。之前笔者配置过spring的动态多数据源切换,当时使用的是JDBC Template。

目前项目中持久化框架使用是mybatis,经过分析后不难发现,多数据源配置需要解决两个问题,一个是由原先的spring经典方式切换到了springboot方式下,多数据源如何配置?有无太大变化?另一个是怎样将多数据源与mybatis的配置关联起来?

不妨先来看下,单数据源下mybatis如何配置的?

单数据源示例

首先要声明一点,项目只是依赖单个数据源时,如果你不介意springboot帮你做事的话,那么恭喜你,你省事儿了!你只需要在项目的属性文件中添加数据源的相关属性配置,springboot会“免费”提供给你一个数据源使用,默认采用的是tomcat jdbc connection pool

当然你可以拒绝springboot的好意,如果你依赖第三方的连接池技术,你可以配置自己的数据源,那么springboot检测到你自己定义了DataSource后,就不会自动配置数据源了。

笔者不能拒绝springboot的好意,所以仅在项目的application.properties中添加了如下属性:

 
  1. spring.datasource.url=jdbc:mysql://localhost:3306/test

  2. spring.datasource.username=root

  3. spring.datasource.password=root

  4. spring.datasource.driver-class-name=com.mysql.jdbc.Driver

  5. spring.datasource.max-idle=10

  6. spring.datasource.max-wait=10000

  7. spring.datasource.min-idle=5

  8. spring.datasource.initial-size=5

  9. spring.datasource.validation-query=SELECT 1

  10. spring.datasource.test-on-borrow=false

  11. spring.datasource.test-while-idle=true

  12. spring.datasource.time-between-eviction-runs-millis=18800

  13.  

然后笔者创建了一个专门用于配置mybatis的类,如下:

 
  1. @Configuration

  2. public class MybatisSpringConfig {

  3. @Bean(name = "sqlSessionFactory")

  4. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

  5. SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

  6. factoryBean.setDataSource(dataSource);

  7. factoryBean.setTypeAliasesPackage("demo.model");

  8.  
  9. return factoryBean.getObject();

  10. }

  11.  
  12. [[[@Bean](http://my.oschina.net/bean)](http://my.oschina.net/bean)](http://my.oschina.net/bean)

  13. public MapperScannerConfigurer mapperScannerConfigurer() {

  14. MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();

  15. mapperScannerConfigurer.setBasePackage("demo.repository");

  16.  
  17. return mapperScannerConfigurer;

  18. }

  19. }

没错,mybatis在spring中就是可以通过如此的简练配置进而正常工作起来。你无需刻意地去创建mybatis的配置文件,无需刻意地去注册mapper接口及指定对应xml文件的位置,这完全得益于mybatis-spring,它就像一个“粘合剂”,可以很方便地将mybatis和spring“粘合”在一起。

MyBatis-Spring的配置步骤

不妨先来说下mybatis-spring配置的一般步骤:

  1. 配置数据源DataSource的Bean。
  2. 使用DataSource配置事务管理器。
  3. 使用DataSource配置SqlSessionFactory的Bean。
  4. 配置MapperScannerConfigurer的Bean。

这里要求配置事务管理器和SqlSessionFactory的数据源必须是同一个,否则事务管理不起作用。配置MapperScannerConfigurer的目的是自动扫描mapper接口所在的包,自动帮你将mapper接口注册为Bean(代理生成接口的实现类),你就可以直接拿来依赖注入了,建议将mapper接口及其对应的xml文件放在同一个包下,这样的话你无需在SqlSessionFactory里指定xml文件的位置了。

OK,到此对比我上面贴出的配置类内容,你可能会发现笔者怎么少了几步?感谢springboot,因为它自动配置了一个DataSource,同时它还自动配置了一个事务管理器。所以笔者只配置了SqlSessionFactory和MapperScannerConfigurer。

当然如果看到这里你仍然“执意”要配置自己的数据源,参照下面的多数据源配置说明,抽出来多个中的一个就可以实现自定义单数据源的配置了。

多数据源示例

经过上面单数据源的示例,可以说当我们切换到springboot的方式下写代码时,springboot为我们带来了很大的便利,还不影响我们自定义,所以笔者认为,没用使用springboot之前,无论你使用spring怎样的配置,使用springboot之后,不会有阻碍,甚至会比原来更快!

简单说下需要多数据源的场景,笔者参照了一下其他的文章,绝大部分的需要来自于数据库主从方式或读写分离。那么就按照master和slave两个数据源,直接贴出数据源的配置类。

  • application.properties
 
  1. datasource.master.url=jdbc:mysql://localhost:3306/master

  2. datasource.master.username=root

  3. datasource.master.password=root

  4. datasource.master.driver-class-name=com.mysql.jdbc.Driver

  5. datasource.master.max-idle=10

  6. datasource.master.max-wait=10000

  7. datasource.master.min-idle=5

  8. datasource.master.initial-size=5

  9. datasource.master.validation-query=SELECT 1

  10. datasource.master.test-on-borrow=false

  11. datasource.master.test-while-idle=true

  12. datasource.master.time-between-eviction-runs-millis=18800

  13.  
  14. datasource.slave.url=jdbc:mysql://localhost:3306/slave

  15. datasource.slave.username=root

  16. datasource.slave.password=root

  17. datasource.slave.driver-class-name=com.mysql.jdbc.Driver

  18. datasource.slave.max-idle=10

  19. datasource.slave.max-wait=10000

  20. datasource.slave.min-idle=5

  21. datasource.slave.initial-size=5

  22. datasource.slave.validation-query=SELECT 1

  23. datasource.slave.test-on-borrow=false

  24. datasource.slave.test-while-idle=true

  25. datasource.slave.time-between-eviction-runs-millis=18800

  26.  
  • master数据源
 
  1. @Configuration

  2. public class MasterConfig {

  3. [[[@Primary](http://my.oschina.net/primary)](http://my.oschina.net/primary)](http://my.oschina.net/primary)

  4. @Bean(name = "masterDataSource")

  5. @ConfigurationProperties(prefix = "datasource.master")

  6. public DataSource dataSource() {

  7. return DataSourceBuilder.create().build();

  8. }

  9.  
  10. @Primary

  11. @Bean(name = "masterTransactionManager")

  12. public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) {

  13. return new DataSourceTransactionManager(dataSource);

  14. }

  15. }

  • slave数据源
 
  1. @Configuration

  2. public class SlaveConfig {

  3. @Bean(name = "slaveDataSource")

  4. @ConfigurationProperties(prefix = "datasource.slave")

  5. public DataSource dataSource() {

  6. return DataSourceBuilder.create().build();

  7. }

  8.  
  9. @Bean(name = "slaveTransactionManager")

  10. public DataSourceTransactionManager transactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {

  11. return new DataSourceTransactionManager(dataSource);

  12. }

  13. }

不难看出两个数据源的配置步骤吧:

  1. 在属性文件中配置两个数据源需要用到的属性值,注意起个好点的前缀名称。
  2. 构建两个数据源的配置类,当然这不是必须的,愿意堆在一个配置类中未尝不可。
  3. 配置类中,配置DataSource的Bean,记得起个能够标识的name!
  4. 配置两个数据源各自对应的事务管理器,别嫌麻烦,否则会给自己埋坑里,记得起个能够标识的name!

配置DataSource时,利用@ConfigurationProperties(prefix = "xxx.xxx")可以依靠指定的前缀,在诸多的属性值中“挑选”出数据源依赖的属性,进而完成数据源的构建。

当自己定义了DataSource后,springboot就会取消自动配置的动作了。为了各司其职,为每个数据源配置各自的事务管理器,springboot自然也会取消自动配置事务管理器的动作。由于是多个数据源和多个事务管理器,都是一个类型的,你要是不起个区别的名字,任谁都分辨不出来吧?

@Primary 有什么作用呢?简单地说,当有两个同一类型的Bean,依赖注入时你没有指定name,正常情况下会报错,有两个你要的Bean,识别不了。但是 @Primary 相当于指定这个Bean为默认的,如果你没有指定name,就采用 @Primary 标识的Bean。

OK,两个数据源的配置配好了,还需要配置各自的Mybatis来进行持久化的操作。

MyBatis-Spring相关配置

  • mybatis for master
 
  1. @Configuration

  2. @MapperScan(basePackages = {"demo.repository.master"}, sqlSessionFactoryRef = "masterSqlSessionFactory")

  3. public class MasterConfig {

  4. @Primary

  5. @Bean(name = "masterDataSource")

  6. @ConfigurationProperties(prefix = "datasource.master")

  7. public DataSource dataSource() {

  8. return DataSourceBuilder.create().build();

  9. }

  10.  
  11. @Primary

  12. @Bean(name = "masterTransactionManager")

  13. public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) {

  14. return new DataSourceTransactionManager(dataSource);

  15. }

  16.  
  17. @Primary

  18. @Bean(name = "masterSqlSessionFactory")

  19. public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {

  20. SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

  21. factoryBean.setDataSource(dataSource);

  22. factoryBean.setTypeAliasesPackage("demo.model");

  23.  
  24. return factoryBean.getObject();

  25. }

  26. }

  • mybatis for slave
 
  1. @Configuration

  2. @MapperScan(basePackages = {"demo.repository.slave"}, sqlSessionFactoryRef = "slaveSqlSessionFactory")

  3. public class SlaveConfig {

  4. @Bean(name = "slaveDataSource")

  5. @ConfigurationProperties(prefix = "datasource.slave")

  6. public DataSource dataSource() {

  7. return DataSourceBuilder.create().build();

  8. }

  9.  
  10. @Bean(name = "slaveTransactionManager")

  11. public DataSourceTransactionManager transactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {

  12. return new DataSourceTransactionManager(dataSource);

  13. }

  14.  
  15. @Bean(name = "slaveSqlSessionFactory")

  16. public SqlSessionFactory basicSqlSessionFactory(@Qualifier("slaveDataSource") DataSource basicDataSource) throws Exception {

  17. SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

  18. factoryBean.setDataSource(basicDataSource);

  19. factoryBean.setTypeAliasesPackage("demo.model");

  20.  
  21. return factoryBean.getObject();

  22. }

  23. }

  24.  

另外还需配置sqlsessionfactorybean中mybatis的配置文件位置和mapper.xml文件位置

PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mapping/*.xml")); sqlSessionFactoryBean.setConfigLocation(resolver.getResource("classpath:/configuration.xml")); 

这里需要强调几个地方:

  1. 细心人会发现上面的配置类中,少了MapperScannerConfigurer的Bean配置,改用了@MapperScan注解。其实两者的作用是一样的,但是@MapperScan比较新,稍后会做解释为什么它比较新。
  2. 由于两个数据源的原因,引出了两套SqlSessionFactory的配置,所以@MapperScan中需要指明依赖的是哪个SqlSessionFactory,“sqlSessionFactoryRef”对应就是SqlSessionFactory的name属性。
  3. @MapperScan会将扫描的mapper接口代理生成实现类,并自动注册为Bean。由于两个数据源的配置类中都有@MapperScan注解,为了避免造成冲突和排错时的困扰,猛烈提醒,两个数据源的配置,mybatis对应的mapper接口及对应xml文件也构建两套,最好接口名上也做些区分。model类使用同一套倒是没什么影响。所以你会看到上面的配置中,@MapperScan中basePackages指向的是两个包路径。

好了,来解释下@MapperScan为何比较新,并且笔者推荐使用@MapperScan。

首先@MapperScan要求的mybatis-spring版本比较新,说明它是新推出的特性。

其次@MapperScan要比配置MapperScannerConfigurer的Bean要简练的多,代码量上就看得出来。

最后,@MapperScan中的basePackageClasses属性是MapperScannerConfigurer所没有的。并且笔者用到了这个basePackageClasses属性,所以这里强力推荐使用@MapperScan注解。

多聊一些,描述下笔者为何会用到@MapperScan中的basePackageClasses属性吧,况且与上述示例中的basePackages有何区别呢?

上面提到了多数据源的一般场景,笔者的不同。笔者的项目中划分了n个子模块,每个子模块有各自的数据库,现在需要每个子模块共享一个公共信息的数据库。

从代码上来说,由于各个子模块依赖的公共信息数据库-数据源、mapper接口和xml映射文件是相同的,笔者希望将这些类和文件抽离到maven的一个公共module(最后会打包为一个jar文件)中,供其他n个子模块依赖使用,这样可以避免重复代码嘛。

笔者这么做之后,发现配置MapperScannerConfigurer的basePackages找不到mapper接口所在的包路径,因为笔者是在子模块中配置的MapperScannerConfigurer,它自然会在子模块的结构中去寻找指定的包路径,是mapper接口被笔者放到了公共的module中,所以是找不到的!

不过还好在@MapperScan中发现了basePackageClasses属性,它会“接受”你指定的mapper接口的全名。再次提醒,记得把xml映射文件和mapper接口放在一起,mybatis-spring会帮你做关联。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值