摘要: 目前專案中持久化框架使用是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中加入瞭如下屬性:
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的类,如下: @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「黏合」在一起。
MyBatis-Spring的設定步驟
不妨先來說下mybatis-spring配置的一般步驟:
設定資料來源DataSource的Bean。
使用DataSource設定事務管理器。
使用DataSource設定SqlSessionFactory的Bean。
設定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
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); } }
不難看出兩個資料來源的設定步驟吧:
在屬性檔中配置兩個資料來源需要用到的屬性值,注意好點的前綴名稱。
建立兩個資料來源的配置類,當然這不是必須的,願意堆在一個配置類中未嘗不可。
配置類別中,設定DataSource的Bean,記得起個能夠識別的name!
配置兩個資料來源各自對應的事務管理器,別嫌麻煩,否則會給自己埋坑里,記得起個能夠識別的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(); } }
这里需要强调几个地方:
细心人会发现上面的配置类中,少了MapperScannerConfigurer的Bean配置,改用了@MapperScan注解。其实两者的作用是一样的,但是@MapperScan比较新,稍后会做解释为什么它比较新。
由于两个数据源的原因,引出了两套SqlSessionFactory的配置,所以@MapperScan中需要指明依赖的是哪个SqlSessionFactory,“sqlSessionFactoryRef”对应就是SqlSessionFactory的name属性。
@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会帮你做关联。
以上是springboot如何配置多重資料來源的詳細內容。更多資訊請關注PHP中文網其他相關文章!