MyBatis多數據源配置(讀寫分離)
首先說明,本文的配置使用的最直接的方式,實際用起來可能會很麻煩。
實際應用中可能存在多種結合的情況,你可以理解本文的含義,不要死板的使用。
多數據源的可能情況
1.主從
通常是MySQL一主多從的情況,本文的例子就是主從的情況,但是只有兩個數據源,所以采用直接配置不會太麻煩,但是不利于后續擴展,主要是作為一個例子來說明,實際操作請慎重考慮。
2.分庫
當業務獨立性強,數據量大的時候的,為了提高并發,可能會對表進行分庫,分庫后,每一個數據庫都需要配置一個數據源。
這種情況可以參考本文,但是需要注意每一個數據庫對應的Mapper要在不同的包下方便區分和配置。
另外分庫的情況下也會存在主從的情況,如果你的數據庫從庫過多,就參考上面提供的方法,或者尋找其他方式解決。
Mapper分包
分庫的情況下,不同的數據庫的Mapper一定放在不同的包下。
主從的情況下,同一個Mapper會同時存在讀寫的情況,創建兩個并不合適,使用同一個即可。但是這種情況下需要注意,Spring對Mapper自動生成的名字是相同的,而且類型也相同,這是就不能直接注入Mapper接口。需要通過SqlSession來解決。
Spring基礎配置
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.isea533.mybatis.service"/> <context:property-placeholder location="classpath:config.properties"/> <aop:aspectj-autoproxy/> <import resource="spring-datasource-master.xml"/> <import resource="spring-datasource-slave.xml"/></beans>
這個文件,主要是引入了spring-datasource-master.xml和spring-datasource-slave.xml。
spring-datasource-master.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="dataSourceMaster" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${master.jdbc.driverClass}"/> <property name="url" value="${master.jdbc.url}"/> <property name="username" value="${master.jdbc.user}"/> <property name="password" value="${master.jdbc.password}"/> <property name="filters" value="stat"/> <property name="maxActive" value="20"/> <property name="initialSize" value="1"/> <property name="maxWait" value="60000"/> <property name="minIdle" value="1"/> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="validationQuery" value="SELECT 'x'"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> </bean> <bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceMaster"/> <property name="mapperLocations"> <array> <value>classpath:mapper/*.xml</value> </array> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.isea533.mybatis.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory1"/> </bean> <bean id="sqlSessionMaster" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype"> <constructor-arg index="0" ref="sqlSessionFactory1"/> </bean> <aop:config> <aop:pointcut id="appService" expression="execution(* com.isea533.mybatis.service..*Service*.*(..))"/> <aop:advisor advice-ref="txAdvice1" pointcut-ref="appService"/> </aop:config> <tx:advice id="txAdvice1" transaction-manager="transactionManager1"> <tx:attributes> <tx:method name="select*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="get*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSourceMaster"/> </bean></beans>spring-datasource-slave.xml
和master區別不大,主要是id名字和數據源配置有區別。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="dataSourceSlave" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${slave.jdbc.driverClass}"/> <property name="url" value="${slave.jdbc.url}"/> <property name="username" value="${slave.jdbc.user}"/> <property name="password" value="${slave.jdbc.password}"/> <property name="filters" value="stat"/> <property name="maxActive" value="20"/> <property name="initialSize" value="1"/> <property name="maxWait" value="60000"/> <property name="minIdle" value="1"/> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="validationQuery" value="SELECT 'x'"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> </bean> <bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSourceSlave"/> <property name="mapperLocations"> <array> <value>classpath:mapper/*.xml</value> </array> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.isea533.mybatis.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2"/> </bean> <bean id="sqlSessionSlave" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype"> <constructor-arg index="0" ref="sqlSessionFactory2"/> </bean> <aop:config> <aop:pointcut id="appService" expression="execution(* com.isea533.mybatis.service..*Service*.*(..))"/> <aop:advisor advice-ref="txAdvice2" pointcut-ref="appService"/> </aop:config> <tx:advice id="txAdvice2" transaction-manager="transactionManager2"> <tx:attributes> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSourceSlave"/> </bean></beans>這里需要注意<tx:method name="*" read-only="true"/>是只讀的。如果不是從庫,可以按主庫進行配置。
在下面代碼中:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.isea533.mybatis.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2"/></bean>
必須通過sqlSessionFactoryBeanName來指定不同的sqlSessionFactory。
config.properties
# 數據庫配置 - Mastermaster.jdbc.driverClass = com.mysql.jdbc.Drivermaster.jdbc.url = jdbc:mysql://192.168.1.11:3306/testmaster.jdbc.user = rootmaster.jdbc.password = jj# - Slaveslave.jdbc.driverClass = com.mysql.jdbc.Driverslave.jdbc.url = jdbc:mysql://192.168.1.22:3306/testslave.jdbc.user = rootslave.jdbc.password = jj
使用Mapper
這里是針對主從的情況進行設置的,兩個配置掃描的Mapper是一樣的,所以沒法直接注入,需要通過下面的麻煩方式注入。
@Servicepublic class DemoService { private CountryMapper writeMapper; private CountryMapper readMapper; @Resource(name = "sqlSessionMaster") public void setWriteMapper(SqlSession sqlSession) { this.writeMapper = sqlSession.getMapper(CountryMapper.class); } @Resource(name = "sqlSessionSlave") public void setReadMapper(SqlSession sqlSession) { this.readMapper = sqlSession.getMapper(CountryMapper.class); } public int save(Country country){ return writeMapper.insert(country); } public List<Country> selectPage(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return readMapper.select(null); }}因為sqlSession能通過name區分開,所以這里從sqlSession獲取Mapper。
另外如果需要考慮在同一個事務中寫讀的時候,需要使用相同的writeMapper,這樣在讀的時候,才能獲取事務中的最新數據。
以上是主從的情況。
在分庫的情況時,由于不同Mapper在不同的包下,所以可以直接使用@Resource或者@Autowired注入Mapper,不需要通過sqlSession獲取。
本篇文章,只是一個多數據源的參考,實際應用時,請根據自己的情況進行考慮。
后續,我會利用業余時間,在本文和上面兩個相關鏈接的基礎上,針對MySql多數據源,嘗試開發可以自動切換數據源的插件,因為我對這方面的實際應用不是很熟,所以歡迎大家留言分享自己的解決方案,對這些了解的越多,就越有可能開發出通用的數據源切換插件。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
|
新聞熱點
疑難解答