SpringBoot多數據源及MyBatis配置詳解

前言

最近迫於項目須要,筆者踏上了springboot多數據源的配置之旅。以前筆者配置過spring的動態多數據源切換,當時使用的是JDBC Template。html

目前項目中持久化框架使用是mybatis,通過分析後不難發現,多數據源配置須要解決兩個問題,一個是由原先的spring經典方式切換到了springboot方式下,多數據源如何配置?有無太大變化?另外一個是怎樣將多數據源與mybatis的配置關聯起來?mysql

不妨先來看下,單數據源下mybatis如何配置的?spring

單數據源示例

首先要聲明一點,項目只是依賴單個數據源時,若是你不介意springboot幫你作事的話,那麼恭喜你,你省事兒了!你只須要在項目的屬性文件中添加數據源的相關屬性配置,springboot會「免費」提供給你一個數據源使用,默認採用的是tomcat jdbc connection poolsql

固然你能夠拒絕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-spring配置的通常步驟:mybatis

  1. 配置數據源DataSource的Bean。
  2. 使用DataSource配置事務管理器。
  3. 使用DataSource配置SqlSessionFactory的Bean。
  4. 配置MapperScannerConfigurer的Bean。

這裏要求配置事務管理器和SqlSessionFactory的數據源必須是同一個,不然事務管理不起做用。配置MapperScannerConfigurer的目的是自動掃描mapper接口所在的包,自動幫你將mapper接口註冊爲Bean(代理生成接口的實現類),你就能夠直接拿來依賴注入了,建議將mapper接口及其對應的xml文件放在同一個包下,這樣的話你無需在SqlSessionFactory裏指定xml文件的位置了。app

OK,到此對比我上面貼出的配置類內容,你可能會發現筆者怎麼少了幾步?感謝springboot,由於它自動配置了一個DataSource,同時它還自動配置了一個事務管理器。因此筆者只配置了SqlSessionFactory和MapperScannerConfigurer。

固然若是看到這裏你仍然「執意」要配置本身的數據源,參照下面的多數據源配置說明,抽出來多箇中的一個就能夠實現自定義單數據源的配置了。

多數據源示例

通過上面單數據源的示例,能夠說當咱們切換到springboot的方式下寫代碼時,springboot爲咱們帶來了很大的便利,還不影響咱們自定義,因此筆者認爲,沒用使用springboot以前,不管你使用spring怎樣的配置,使用springboot以後,不會有阻礙,甚至會比原來更快!

簡單說下須要多數據源的場景,筆者參照了一下其餘的文章,絕大部分的須要來自於數據庫主從方式或讀寫分離。那麼就按照master和slave兩個數據源,直接貼出數據源的配置類。

  • application.properties
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
  • master數據源
@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);
    }
 }
  • slave數據源
@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);
    }
 }

不難看出兩個數據源的配置步驟吧:

  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
@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();
    }
 }
  • mybatis for slave
@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();
    }
 }

這裏須要強調幾個地方:

  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會幫你作關聯。

總結

  1. 以上是筆者親自操做事後的經驗總結,閱讀者須要對springboot、mybatis-spring有所瞭解,理解起來纔不會吃力。
  2. 經歷過多數據源的配置過程以後,筆者發現mybatis-spring官方文檔的中文版比英文版是有缺失的,不夠新!因此強烈建議查閱英文版文檔,上述貼出的連接中就是英文版的地址。
  3. 關於springboot多數據源配置的文章不少,但大多數只是貼出了代碼,沒有說明爲什麼,並且配置方法還各有不一樣。因此應用者不能只是應用,須要本身搞清楚緣由,纔是學習的過程。
  4. 筆者上面的描述內容不少,若有不明白之處,歡迎討論指正。
相關文章
相關標籤/搜索