最近迫於項目須要,筆者踏上了springboot多數據源的配置之旅。以前筆者配置過spring的動態多數據源切換,當時使用的是JDBC Template。html
目前項目中持久化框架使用是mybatis,通過分析後不難發現,多數據源配置須要解決兩個問題,一個是由原先的spring經典方式切換到了springboot方式下,多數據源如何配置?有無太大變化?另外一個是怎樣將多數據源與mybatis的配置關聯起來?mysql
不妨先來看下,單數據源下mybatis如何配置的?spring
首先要聲明一點,項目只是依賴單個數據源時,若是你不介意springboot幫你作事的話,那麼恭喜你,你省事兒了!你只須要在項目的屬性文件中添加數據源的相關屬性配置,springboot會「免費」提供給你一個數據源使用,默認採用的是tomcat jdbc connection pool。sql
固然你能夠拒絕springboot的好意,若是你依賴第三方的鏈接池技術,你能夠配置本身的數據源,那麼springboot檢測到你本身定義了DataSource後,就不會自動配置數據源了。數據庫
筆者不能拒絕springboot的好意,因此僅在項目的application.properties中添加了以下屬性:apache
spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.max-idle=10 spring.datasource.max-wait=10000 spring.datasource.min-idle=5 spring.datasource.initial-size=5 spring.datasource.validation-query=SELECT 1 spring.datasource.test-on-borrow=false spring.datasource.test-while-idle=true spring.datasource.time-between-eviction-runs-millis=18800
而後筆者建立了一個專門用於配置mybatis的類,以下:tomcat
@Configuration public class MybatisSpringConfig { @Bean(name = "sqlSessionFactory") public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("demo.model"); return factoryBean.getObject(); } [[[@Bean](http://my.oschina.net/bean)](http://my.oschina.net/bean)](http://my.oschina.net/bean) public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("demo.repository"); return mapperScannerConfigurer; } }
沒錯,mybatis在spring中就是能夠經過如此的簡練配置進而正常工做起來。你無需刻意地去建立mybatis的配置文件,無需刻意地去註冊mapper接口及指定對應xml文件的位置,這徹底得益於mybatis-spring,它就像一個「粘合劑」,能夠很方便地將mybatis和spring「粘合」在一塊兒。springboot
不妨先來講下mybatis-spring配置的通常步驟:mybatis
這裏要求配置事務管理器和SqlSessionFactory的數據源必須是同一個,不然事務管理不起做用。配置MapperScannerConfigurer的目的是自動掃描mapper接口所在的包,自動幫你將mapper接口註冊爲Bean(代理生成接口的實現類),你就能夠直接拿來依賴注入了,建議將mapper接口及其對應的xml文件放在同一個包下,這樣的話你無需在SqlSessionFactory裏指定xml文件的位置了。app
OK,到此對比我上面貼出的配置類內容,你可能會發現筆者怎麼少了幾步?感謝springboot,由於它自動配置了一個DataSource,同時它還自動配置了一個事務管理器。因此筆者只配置了SqlSessionFactory和MapperScannerConfigurer。
固然若是看到這裏你仍然「執意」要配置本身的數據源,參照下面的多數據源配置說明,抽出來多箇中的一個就能夠實現自定義單數據源的配置了。
通過上面單數據源的示例,能夠說當咱們切換到springboot的方式下寫代碼時,springboot爲咱們帶來了很大的便利,還不影響咱們自定義,因此筆者認爲,沒用使用springboot以前,不管你使用spring怎樣的配置,使用springboot以後,不會有阻礙,甚至會比原來更快!
簡單說下須要多數據源的場景,筆者參照了一下其餘的文章,絕大部分的須要來自於數據庫主從方式或讀寫分離。那麼就按照master和slave兩個數據源,直接貼出數據源的配置類。
datasource.master.url=jdbc:mysql://localhost:3306/master datasource.master.username=root datasource.master.password=root datasource.master.driver-class-name=com.mysql.jdbc.Driver datasource.master.max-idle=10 datasource.master.max-wait=10000 datasource.master.min-idle=5 datasource.master.initial-size=5 datasource.master.validation-query=SELECT 1 datasource.master.test-on-borrow=false datasource.master.test-while-idle=true datasource.master.time-between-eviction-runs-millis=18800 datasource.slave.url=jdbc:mysql://localhost:3306/slave datasource.slave.username=root datasource.slave.password=root datasource.slave.driver-class-name=com.mysql.jdbc.Driver datasource.slave.max-idle=10 datasource.slave.max-wait=10000 datasource.slave.min-idle=5 datasource.slave.initial-size=5 datasource.slave.validation-query=SELECT 1 datasource.slave.test-on-borrow=false datasource.slave.test-while-idle=true datasource.slave.time-between-eviction-runs-millis=18800
@Configuration public class MasterConfig { [[[@Primary](http://my.oschina.net/primary)](http://my.oschina.net/primary)](http://my.oschina.net/primary) @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "datasource.master") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Primary @Bean(name = "masterTransactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
@Configuration public class SlaveConfig { @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "datasource.slave") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "slaveTransactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("slaveDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
不難看出兩個數據源的配置步驟吧:
配置DataSource時,利用@ConfigurationProperties(prefix = "xxx.xxx")能夠依靠指定的前綴,在諸多的屬性值中「挑選」出數據源依賴的屬性,進而完成數據源的構建。
當本身定義了DataSource後,springboot就會取消自動配置的動做了。爲了各司其職,爲每一個數據源配置各自的事務管理器,springboot天然也會取消自動配置事務管理器的動做。因爲是多個數據源和多個事務管理器,都是一個類型的,你要是不起個區別的名字,任誰都分辨不出來吧?
@Primary 有什麼做用呢?簡單地說,當有兩個同一類型的Bean,依賴注入時你沒有指定name,正常狀況下會報錯,有兩個你要的Bean,識別不了。可是 @Primary 至關於指定這個Bean爲默認的,若是你沒有指定name,就採用 @Primary 標識的Bean。
OK,兩個數據源的配置配好了,還須要配置各自的Mybatis來進行持久化的操做。
@Configuration @MapperScan(basePackages = {"demo.repository.master"}, sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterConfig { @Primary @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "datasource.master") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Primary @Bean(name = "masterTransactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Primary @Bean(name = "masterSqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("demo.model"); return factoryBean.getObject(); } }
@Configuration @MapperScan(basePackages = {"demo.repository.slave"}, sqlSessionFactoryRef = "slaveSqlSessionFactory") public class SlaveConfig { @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "datasource.slave") public DataSource dataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "slaveTransactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("slaveDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "slaveSqlSessionFactory") public SqlSessionFactory basicSqlSessionFactory(@Qualifier("slaveDataSource") DataSource basicDataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(basicDataSource); factoryBean.setTypeAliasesPackage("demo.model"); return factoryBean.getObject(); } }
這裏須要強調幾個地方:
好了,來解釋下@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會幫你作關聯。