mysql+spring+mybatis实现数据库读写分离[代码配置]

场景:一个读数据源一个读写数据源。


原理:借助spring的【org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource】这个抽象类实现,看名字可以了解到是一个路由数据源的东西,这个类中有一个方法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Determine the current lookup key. This will typically be 
  3.  * implemented to check a thread-bound transaction context. 
  4.  * <p>Allows for arbitrary keys. The returned key needs 
  5.  * to match the stored lookup key type, as resolved by the 
  6.  * {@link #resolveSpecifiedLookupKey} method. 
  7.  */  
  8. protected abstract Object determineCurrentLookupKey();  

每次去连数据库的时候,spring会调用这个方法去找对应的数据源。返回值即对应的数据源的LookUpKey.那么这个LookUpKey在哪定义的呢?看下面的dataBase.xml的配置


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <!--数据源 读写 -->  
  2. <bean id="dataSourceRW" class="org.logicalcobwebs.proxool.ProxoolDataSource">  
  3.     <property name="alias" value="ihotelRW"></property>  
  4.     <property name="delegateProperties">  
  5.         <value>user=${jdbc.username},password=${jdbc.password}  
  6.         </value>  
  7.     </property>  
  8.     <property name="user" value="${jdbc.username}" />  
  9.     <property name="password" value="${jdbc.password}" />  
  10.     <property name="driver" value="${jdbc.driverClassName}" />  
  11.     <property name="driverUrl" value="${jdbc.url}" />  
  12.     <property name="maximumConnectionCount" value="${jdbc.maximumConnectionCount}"></property>  
  13.     <property name="maximumActiveTime" value="${jdbc.maximumActiveTime}"></property>  
  14.     <property name="maximumConnectionLifetime" value="${jdbc.maximumConnectionLifetime}"></property>  
  15.     <property name="prototypeCount" value="${jdbc.prototypeCount}"></property>  
  16.     <property name="houseKeepingSleepTime" value="${jdbc.houseKeepingSleepTime}"></property>  
  17.     <property name="simultaneousBuildThrottle" value="${jdbc.simultaneousBuildThrottle}"></property>  
  18.     <property name="houseKeepingTestSql" value="${jdbc.houseKeepingTestSql}"></property>  
  19.     <property name="verbose" value="${jdbc.verbose}"></property>  
  20.     <property name="statistics" value="${jdbc.statistics}"></property>  
  21.     <property name="statisticsLogLevel" value="${jdbc.statisticsLogLevel}"></property>  
  22. </bean>  
  23.   
  24.    <!--数据源 读-->  
  25.    <bean id="dataSourceR" class="org.logicalcobwebs.proxool.ProxoolDataSource">  
  26.        <property name="alias" value="ihotelR"></property>  
  27.        <property name="delegateProperties">  
  28.            <value>user=${jdbc.r.username},password=${jdbc.r.password}  
  29.            </value>  
  30.        </property>  
  31.        <property name="user" value="${jdbc.r.username}" />  
  32.        <property name="password" value="${jdbc.r.password}" />  
  33.        <property name="driver" value="${jdbc.r.driverClassName}" />  
  34.        <property name="driverUrl" value="${jdbc.r.url}" />  
  35.        <property name="maximumConnectionCount" value="${jdbc.maximumConnectionCount}"></property>  
  36.        <property name="maximumActiveTime" value="${jdbc.maximumActiveTime}"></property>  
  37.        <property name="maximumConnectionLifetime" value="${jdbc.maximumConnectionLifetime}"></property>  
  38.        <property name="prototypeCount" value="${jdbc.prototypeCount}"></property>  
  39.        <property name="houseKeepingSleepTime" value="${jdbc.houseKeepingSleepTime}"></property>  
  40.        <property name="simultaneousBuildThrottle" value="${jdbc.simultaneousBuildThrottle}"></property>  
  41.        <property name="houseKeepingTestSql" value="${jdbc.houseKeepingTestSql}"></property>  
  42.        <property name="verbose" value="${jdbc.verbose}"></property>  
  43.        <property name="statistics" value="${jdbc.statistics}"></property>  
  44.        <property name="statisticsLogLevel" value="${jdbc.statisticsLogLevel}"></property>  
  45.    </bean>  
  46.      
  47.    <!-- 动态数据源 -->  
  48.    <bean id="dynamicDataSource" class="com.dao.datasource.DynamicDataSource">  
  49.        <!-- 通过key-value关联数据源 -->  
  50.        <property name="targetDataSources">  
  51.            <map>  
  52.                <entry value-ref="dataSourceRW" key="dataSourceKeyRW"></entry>  
  53.                <entry value-ref="dataSourceR" key="dataSourceKeyR"></entry>  
  54.            </map>  
  55.        </property>  
  56.        <property name="defaultTargetDataSource" ref="dataSourceRW" />      
  57.    </bean>  
  58.   
  59. <!--mybatis与Spring整合 开始 -->  
  60. <bean id="sqlSessionFactory" name="sqlSessionFactory"  
  61.     class="org.mybatis.spring.SqlSessionFactoryBean">  
  62.     <property name="configLocation" value="classpath:conf/core/sqlMapConfig.xml" />  
  63.     <property name="dataSource" ref="dynamicDataSource" />  
  64. </bean>  

动态数据源dynamicDataSource中的dataSourceKeyRW、dataSourceKeyR就是

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. protected abstract Object determineCurrentLookupKey();  
这个方法要返回的值。那么如何设置,让这个方法的返回值是根据我们的需要返回dataSourceKeyRW、dataSourceKeyR呢?由于这个方法没有入参,并且是spring自动调用的,因此考虑使用静态变量存储dataSource的key,在调用sql语句前设置静态变量的值,然后在这个方法中得到静态变量的值,返回。又考虑到多线程,同时可能会有很多请求,为避免线程之间相互干扰,考虑使用threadLocal。

先看存储dataSourceKey的容器类。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DBContextHolder {  
  2.   
  3.     /** 
  4.      * 线程threadlocal 
  5.      */  
  6.     private static ThreadLocal<String> contextHolder = new ThreadLocal<>();  
  7.   
  8.     private String DB_TYPE_RW = "dataSourceKeyRW";  
  9.     private String DB_TYPE_R = "dataSourceKeyR";  
  10.   
  11.     public String getDbType() {  
  12.         String db = contextHolder.get();  
  13.         if (db == null) {  
  14.             db = DB_TYPE_RW;// 默认是读写库  
  15.         }  
  16.         return db;  
  17.     }  
  18.   
  19.     /** 
  20.      *  
  21.      * 设置本线程的dbtype 
  22.      *  
  23.      * @param str 
  24.      * @see [相关类/方法](可选) 
  25.      * @since [产品/模块版本](可选) 
  26.      */  
  27.     public void setDbType(String str) {  
  28.         contextHolder.set(str);  
  29.     }  
  30.   
  31.     /** 
  32.      * clearDBType 
  33.      *  
  34.      * @Title: clearDBType 
  35.      * @Description: 清理连接类型 
  36.      */  
  37.     public static void clearDBType() {  
  38.         contextHolder.remove();  
  39.     }  
  40. }  

动态数据源的实现类。


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DynamicDataSource extends AbstractRoutingDataSource {  
  2.   
  3.     /* 
  4.      * (non-Javadoc) 
  5.      * @see javax.sql.CommonDataSource#getParentLogger() 
  6.      */  
  7.     @Override  
  8.     public Logger getParentLogger() throws SQLFeatureNotSupportedException {  
  9.         // TODO Auto-generated method stub  
  10.         return null;  
  11.     }  
  12.   
  13.     /** 
  14.      *  
  15.      * override determineCurrentLookupKey 
  16.      * <p> 
  17.      * Title: determineCurrentLookupKey 
  18.      * </p> 
  19.      * <p> 
  20.      * Description: 自动查找datasource 
  21.      * </p> 
  22.      *  
  23.      * @return 
  24.      */  
  25.     @Override  
  26.     protected Object determineCurrentLookupKey() {  
  27.         return DBContextHolder.getDbType();  
  28.     }  
  29.   
  30. }  

在DAO层中设置数据库类型。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 添加邮件 
  3.      *  
  4.      * @param sms 
  5.      * @return 
  6.      */  
  7.     public boolean insertEmail(Email email) {  
  8.           
  9.         //根据具体需要设置不同的数据库  
  10.         DBContextHolder.setDbType(DBContextHolder.DB_TYPE_RW);  
  11.         //DBContextHolder.setDbType(DBContextHolder.DB_TYPE_R);  
  12.         int result = this.getSqlSession().insert(STATEMENT + ".addEntity",  
  13.                 email);  
  14.         return result == 1;  
  15.     }  




在本例中,我们是在DAO中指定数据库,我们也可以根据需要在service或者controller中指定DB类型,需要记住的是setDbType是针对线程维度的。要考虑多线程的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值