zoukankan      html  css  js  c++  java
  • 基于spring的数据库读写分离

    背景:
      Spring读写分离是大家都比较常见并一直在使用的技术。
      本博文再次对其进行阐述,一方面是为了更好的分享给大伙,一方面也是对最近做"XXX系统"遇到的问题做一次整理。方便大家以后遇到类似问题可以很快解决。
    技术实现:
      1、多数据源配置。配置包括一个主库master_dataSource,一个个从库slave_dataSource。
      数据源托管给tomcat控制,系统通过jndi方式寻找。配置内容如下:

    <beans profile="production">
    <jee:jndi-lookup id="master_dataSource" jndi-name="java:comp/env/jdbc/master"/>
    <jee:jndi-lookup id="slave_dataSource" jndi-name="java:comp/env/jdbc/slave"/>
    <bean id="dataSource" class="com.mmb.framework.support.DynamicDataSource">
    <property name="targetDataSources">
    <map key-type="java.lang.String">
    <entry key="master" value-ref="master_dataSource"></entry>
    <entry key="slave" value-ref="slave_dataSource"></entry>
    <entry key="slave2" value-ref="slave2_dataSource"></entry>
    </map>
    </property>
    <property name="defaultTargetDataSource" ref="master_dataSource"></property>
    </bean>
    </beans>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />

      2、实现类
      从配置文件发现dataSource的实现类为:com.mmb.framework.support.DynamicDataSource,该类继承了spring中类:AbstractRoutingDataSource。并重写了方法:determineCurrentLookupKey。该方法是主从数据源切换的关键,通过该方法判定当前上下文的数据源key,进而跟进key通过Jndi找到对应的数据源。该类具体内容如下:

      

    public class DynamicDataSource extends AbstractRoutingDataSource {
    public static String MASTER = "master";
    public static String SLAVE = "slave";
    private static ThreadLocal<String> local = new ThreadLocal<String>();
    @Override
    protected Object determineCurrentLookupKey() {
    String dString = local.get() == null ? MASTER : local.get();
    setRoute(DynamicDataSource.MASTER);
    return dString;
    }
    public static void setRoute(String route) {
    if (route == null || route.equals("")) {
    route = MASTER;
    }
    local.set(route);
    }
    public static void removeRoute() {
    local.remove();
    }
    public static Object getRoute() {
    return local.get();
    }
    }

      通过determineCurrentLookupKey方法可以看见,程序将当前线程key放入到ThreadLocal变量local中。这样就可以很好的解决多线程问题,避免争抢
    数据库连接导致数据错误。那么setRoute方法在何时被调用,请读者继续向下看。

      3、使用方法
      系统采用ibatis作为持久层,并使用AbstractDaoSupport辅助获取数据库连接。AbstractDaoSupport属于org.apache.ibatis.spring.support包,我们在其中getSession和getJdbcTemplate。如下:

       

    public Session getSession(String sessionFactorySign)
    {
    dynamicSession(sessionFactorySign);
    return session;
    }
    
    public JdbcTemplate getJdbcTemplate(String sessionFactorySign)
    {
    dynamicSession(sessionFactorySign);
    return JdbcTemplate;
    }
    private void dynamicSession(String sessionFactorySign)
    {
    if(null == sessionFactorySign || "".equals(sessionFactorySign))
    DynamicDataSource.setRoute(DynamicDataSource.MASTER);
    else
    DynamicDataSource.setRoute(sessionFactorySign);
    }

      通过代码可以发现,每次获取JdbcTemplate模板类或者获取session时,都会调用DynamicDataSource.setRoute进行数据源路由的设定。这里也回答了上面提到的问题。
    具体使用:this.getJdbcTemplate(DynamicDataSource.MASTER).update();

      4、类图和序列图
      到此可能大家依然不是太清晰,到底如何做到了读写分离。下面我给出了上面涉及到的类关系和序列图。方便大家更好的理解。图形工具为:Enterprise Architect 11。

      类图:

      

      序列图:

      
    1、TestMapper调用AbstractDaoSupport.getJdbcTemplate方法获取JdbcTemplate
    2、AbstractDaoSupport调用自身dynamicSession方法,该方法中调用DynamicDataSource.setRoute方法设置数据源路由key。
    3、TestMapper获取到jdbcTemplate后,调用JdbcTemplate.update方法
    4、JdbcTemplate调用DataSourceUtil获取数据连接
    5、DataSourceUtil调用自身doGetConnection,该方法中调用TransactionSynchronizationManager.getResource方法找到数据源
    6、如果当前TransactionSynchronizationManager中包含ConnectionHolder并且开启了事务同步,并且ConnectionHolder中有连接,那么转到7.否则转到8.
    7、调用ConnectionHolder.getConnection返回连接,转到9。
    8、调用AbstractRoutingDataSource.getConnection方法,该方法调用DynamicDataSource.determineCurrentLookupKey()从ThreadLodal变量DynamicDataSource.local中获取
    当前数据源路由key,根据key获取当前的数据源,调用具体数据源的getConnection方法,返回当前数据库连接。转到9。
    9、JdbcTemplate接受到数据库连接后执行execute方法完成更新操作,结束。

    在实际使用用还遇到如下两个问题,请参看博文:http://www.cnblogs.com/belen/p/4926206.html

  • 相关阅读:
    深浅拷贝的区别
    vue中动态加载组件
    一些工具软件
    echarts 自传作品入口
    Echarts progress 设置背景颜色
    Echarts柱状图,柱子设置不一样的渐变色
    .net 控制台学习笔记 一、控制台程序的异常捕获
    IdentityServer4实战:自定义用户名和密码校验
    IdentityServer4实战:Token 中返回用户属性
    IdentityServer4实战:持久化 Resource
  • 原文地址:https://www.cnblogs.com/belen/p/4926098.html
Copyright © 2011-2022 走看看