博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring多数据源配置,实现读写分离
阅读量:6229 次
发布时间:2019-06-21

本文共 7041 字,大约阅读时间需要 23 分钟。

hot3.png

实现思路:在中配置多个数据源,然后在service层通过注解方式标明方法所要使用的数据源,利用springAOP在service方法执行前根据方法上的注解明确所要使用的数据源。如下图

 

 

以上分析可得出,需要抽象出一个DynamicDataSource数据源,在spring中关于数据源的配置都是基于DynamicDataSource,在运行时根据service上的注解从DynamicDataSource选取需要的数据源进行实际的持久化操作,下面开始上代码

首先,介绍spring 的AbstractRoutingDataSource 

     AbstractRoutingDataSource这个类 是spring2.0以后增加的,我们先来看下AbstractRoutingDataSource的定义:

Java代码 

  1. public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean  {}  

 

Java代码  

  1. public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {  
  2.   
  3.     private Map<Object, Object> targetDataSources;  
  4.   
  5.     private Object defaultTargetDataSource;  
  6.   
  7.     private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();  
  8.   
  9.     private Map<Object, DataSource> resolvedDataSources;  
  10.   
  11.     private DataSource resolvedDefaultDataSource;  

 

    AbstractRoutingDataSource继承了AbstractDataSource ,而AbstractDataSource 又是DataSource 的子类。

DataSource   是javax.sql 的数据源接口,定义如下:

Java代码  

  1. public interface DataSource  extends CommonDataSource,Wrapper {  
  2.   
  3.   Connection getConnection() throws SQLException;  
  4.    
  5.   Connection getConnection(String username, String password)  
  6.     throws SQLException;  
  7. }  

 DataSource 接口定义了2个方法,都是获取连接。我们在看下AbstractRoutingDataSource 如何实现了DataSource接口:

 

Java代码  

  1. public Connection getConnection() throws SQLException {  
  2.     return determineTargetDataSource().getConnection();  
  3. }  
  4.   
  5. public Connection getConnection(String username, String password) throws SQLException {  
  6.     return determineTargetDataSource().getConnection(username, password);  
  7. }  

 很显然就是调用自己的determineTargetDataSource()  方法获取到connection。determineTargetDataSource方法定义如下:

 

Java代码  收藏代码

  1. protected DataSource determineTargetDataSource() {  
  2.         Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");  
  3.         Object lookupKey = determineCurrentLookupKey();  
  4.         DataSource dataSource = this.resolvedDataSources.get(lookupKey);  
  5.         if (dataSource == null && (this.lenientFallback || lookupKey == null)) {  
  6.             dataSource = this.resolvedDefaultDataSource;  
  7.         }  
  8.         if (dataSource == null) {  
  9.             throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");  
  10.         }  
  11.         return dataSource;  
  12.     }  

 

我们最关心的还是下面2句话:

  Object lookupKey = determineCurrentLookupKey();

    DataSource dataSource = this.resolvedDataSources.get(lookupKey);

 

    determineCurrentLookupKey方法返回lookupKey,resolvedDataSources方法就是根据lookupKey从Map中获得数据源。resolvedDataSources 和determineCurrentLookupKey定义如下:

 

  private Map<Object, DataSource> resolvedDataSources;

  protected abstract Object determineCurrentLookupKey()

 

  看到以上定义,我们是不是有点思路了,resolvedDataSources是Map类型,我们可以把MasterDataSource和SlaveDataSource存到Map中,如下:

 

    key        value

    master           MasterDataSource

    slave              SlaveDataSource

 

  我们在写一个类DynamicDataSource  继承AbstractRoutingDataSource,实现其determineCurrentLookupKey() 方法,该方法返回Map的key,master或slave。这样就达到了切换数据库的目的。

现在开始介绍实现方式:

1. 创建注解类,用来标记service方法所要使用的数据源key

 

  1. @Retention(RetentionPolicy.RUNTIME)  
  2. (ElementType.METHOD)  
  3. public  DataSource {  
  4.     String value();  

2. 创建DynamicDataSource类,该类继承自AbstractRoutingDataSource

 

[java]

  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  2.   
  3. public class DynamicDataSource extends AbstractRoutingDataSource {  
  4.     private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();  
  5.   
  6.     public static void setDataSourceKey(String dataSource) {  
  7.         dataSourceKey.set(dataSource);  
  8.     }  
  9.   
  10.     public static String getDatasourcekey() {  
  11.         return dataSourceKey.get();  
  12.     }  
  13.   
  14.     protected Object determineCurrentLookupKey() {  
  15.         return dataSourceKey.get();  
  16.     }  
  17.   
  18. }  

 

 

方法中实现了determineCurrentLookupKey()方法,该方法会从ThreadLocal对象中获取到一个key来表明所要使用的数据源

3. 实现springAop来根据service中方法上的注解设置ThreadLocal对象

 

[java]

  1. import java.lang.reflect.Method;  
  2.   
  3. import org.springframework.core.annotation.AnnotationUtils;  
  4. import org.springframework.transaction.support.TransactionSynchronizationManager;  
  5. import com.chinalife.clap.core.MultDataSource;  
  6. import com.chinalife.clap.core.annotation.DataSource;  
  7.   
  8. public class DataSourceAspect {  
  9.   
  10.     public void before(JoinPoint joinPoint) {  
  11.         if (TransactionSynchronizationManager.isActualTransactionActive()  
  12.                 && DynamicDataSource.getDatasourcekey() != null)  
  13.             return;  
  14.         // 获取方法签名  
  15.         Method declareMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();  
  16.         Method instanceMethod = joinPoint.getTarget().getClass().getMethod(declareMethod.getName(),  
  17.                 declareMethod.getParameterTypes());  
  18.         DataSource methodAnnotation = AnnotationUtils.findAnnotation(instanceMethod, DataSource.class);  
  19.         if (methodAnnotation == null)  
  20.             return;  
  21.         if (methodAnnotation != null) {  
  22.             MultDataSource.setDataSourceKey(methodAnnotation.value());  
  23.         }  
  24.     }  
  25.   
  26.     /** 
  27.      * 方法执行完后置空 
  28.      */  
  29.     public void after(JoinPoint joinPoint) {  
  30.         if (TransactionSynchronizationManager.isActualTransactionActive())  
  31.             return;  
  32.         if (TransactionSynchronizationManager.isSynchronizationActive())  
  33.             TransactionSynchronizationManager.clearSynchronization();  
  34.         MultDataSource.setDataSourceKey(null);  
  35.     }  
  36.   
  37. }  

 

 

 

3.开始配置spring数据源 application-jdbc.xml

 

[html]

  1. <bean id="masterdataSource"    
  2.         class="org.springframework.jdbc.datasource.DriverManagerDataSource">    
  3.         <property name="driverClassName" value="com.mysql.jdbc.Driver" />    
  4.         <property name="url" value="jdbc:mysql://127.0.0.1:3306/shop" />    
  5.         <property name="username" value="root" />    
  6.         <property name="password" value="yangyanping0615" />    
  7.     </bean>    
  8.     
  9.     <bean id="slavedataSource"    
  10.         class="org.springframework.jdbc.datasource.DriverManagerDataSource">    
  11.         <property name="driverClassName" value="com.mysql.jdbc.Driver" />    
  12.         <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" />    
  13.         <property name="username" value="root" />    
  14.         <property name="password" value="yangyanping0615" />    
  15.     </bean>    
  16.         
  17.         <beans:bean id="dynamicDataSource" class="com.air.shop.common.db.DynamicDataSource">    
  18.         <property name="targetDataSources">      
  19.               <map key-type="java.lang.String">      
  20.                   <!-- write -->    
  21.                  <entry key="master" value-ref="masterdataSource"/>      
  22.                  <!-- read -->    
  23.                  <entry key="slave" value-ref="slavedataSource"/>      
  24.               </map>      
  25.                   
  26.         </property>      
  27.         <property name="defaultTargetDataSource" ref="masterdataSource"/>      
  28.     </beans:bean>    
  29.     
  30.     <bean id="transactionManager"    
  31.         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
  32.         <property name="dataSource" ref="dynamicDataSource" />    
  33.     </bean>    
  34.     
  35.     
  36.     <!-- 配置SqlSessionFactoryBean -->    
  37.     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">    
  38.         <property name="dataSource" ref="dynamicDataSource" />    
  39.         <property name="configLocation" value="classpath:config/mybatis-config.xml" />    
  40.     </bean>    
  41. <!-- 配置数据库注解aop -->    
  42.     <beans:bean id="manyDataSourceAspect" class="com.air.shop.proxy.DataSourceAspect" />    
  43.     <aop:config>    
  44.         <aop:aspect id="c" ref="manyDataSourceAspect">    
  45.             <aop:pointcut id="tx" expression="execution(* com.air.shop.mapper.service.*(..))"/>    
  46.             <aop:before pointcut-ref="tx" method="before"/>    
  47.             <aop:after pointcut-ref="tx" method="after"/>    
  48.          </aop:aspect>   
  49. </aop:config> <!-- 配置数据库注解aop -->  

 

 

下面给出service实例

 

[java]

  1. public interface Userservic {    
  2.     @DataSource("master")    
  3.     public void add(User user);    
  4.     
  5.     @DataSource("master")    
  6.     public void update(User user);    
  7.     
  8.     @DataSource("master")    
  9.     public void delete(int id);    
  10.     
  11.     @DataSource("slave")    
  12.     public User loadbyid(int id);    
  13.   
  14. }  

save_snippets.png

 

转载于:https://my.oschina.net/yukong/blog/870379

你可能感兴趣的文章
Linux coredump
查看>>
Ubuntu 10.04安装水晶(Mercury)无线网卡驱动
查看>>
Myeclipes快捷键
查看>>
ToRPC:一个双向RPC的Python实现
查看>>
我的友情链接
查看>>
nginx在reload时候报错invalid PID number
查看>>
神经网络和深度学习-第二周神经网络基础-第二节:Logistic回归
查看>>
Myeclipse代码提示及如何设置自动提示
查看>>
c/c++中保留两位有效数字
查看>>
ElasticSearch 2 (32) - 信息聚合系列之范围限定
查看>>
VS2010远程调试C#程序
查看>>
[MicroPython]TurniBit开发板DIY自动窗帘模拟系统
查看>>
由String类的Split方法所遇到的两个问题
查看>>
Python3.4 12306 2015年3月验证码识别
查看>>
从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
查看>>
windows查看端口占用
查看>>
Yii用ajax实现无刷新检索更新CListView数据
查看>>
JDBC的事务
查看>>
Io流的概述
查看>>
App 卸载记录
查看>>