数据库读写分离

实现原理:

master/slave数据库都对应不同的操作名称,执行Dao层方法时,判断方法名是否以给定的master操作名称开头(比如:add, delete, save, delete), 如果是,则用master DB,如果不是则用slave DB

实现步骤:

利用SpringAbstractRoutingDataSource解决多数据源的问题,使用AOP动态切换数据源。

a. 配置 master/slave等多个数据源

b. 写一个DynamicDataSource类继承AbstractRoutingDataSource,并实现etermineCurrentLookupKey

方法。配置一个动态数据源

c. dataSourceThreadLocal 绑定,利用ThreadLocal解决线程安全问题

d. 利用AOP, 编写DataSourceAdvice类,实现根据方法名动态切换数据源的功能。其中使用<util:set>来配置master Db对应的操作名称。

代码实现及分析:

a.配置数据源:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="lililocations">
<value>classpath:jdbc.properties</value>
</property>
</bean>

<bean id="masterDataSource" ...> … </bean>
<bean id="slaveDataSource" ...> … </bean>

<bean id="dataSource" class="com.lili.a.ds.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDataSource" />
<entry key="slave" value-ref="slaveDataSource" />
</map>
</property>
<property name="defaultTargetDataSource" ref="slaveDataSource" />
</bean>

b.DynamicDataSource类:

继承AbstractRoutingDataSource, AbstractRoutingDataSourcejavax.sql.DataSource的子类 ,于是我们自然地去看他的getConnection 方法:

public Connection getConnection() throws SQLException {

return determineTargetDataSource().getConnection();

}

我发现原来奥妙就在determineTargetDataSource()里:

通过看源码,我发现determineTargetDataSource()用到了我们需要进行实现的抽象方法determineCurrentLookupKey(),该方法返回使用的DataSourcekey值。根据key值从resolvedDataSources这个map 中获取对应的value值。如果找不到,则使用默认的resolvedDefaultDataSource

resolvedDataSources是一个map,程序会将配置中的targetDataSources 这个Map中的key-value复制到resolvedDataSources。同样的会将 defaultTargetDataSourceDataSource值赋给 defaultTargetDataSource

public class MyDynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

    return DynamicDataSourceHolder.getDataSouce();

   }

}

c. DynamicDataSourceHolder类:

public class DynamicDataSourceHolder {

    private static final ThreadLocal<String> datasource = new ThreadLocal<String>();

    public static void setDataSource(String dataSource) {
        datasource.set(dataSource);
 } public static String getDataSouce(){ String dataSource = datasource .get(); if (dataSource == null) { setMaster("master"); } returndatasource.get().toString(); }}

d.AOP

<bean id="dataSourceAdvice" class="com.lili.test.ds.DataSourceAdvice">
        <property name="MasterKeys" ref="MasterKeys"></property>
</bean>

<util:set id="MasterKeys">
        <value>delete</value>
        <value>add</value>
        <value>update</value>
        <value>save</value>
</util:set>
<aop:config>
        <aop:pointcut id="pc" expression="execution(* com.lili.a.dao..*.*(..)) || execution(* com.lili.a.mybatis..*.*(..))" />
        <aop:advisor pointcut-ref="pc" advice-ref="dataSourceAdvice" order="1" />
</aop:config>



public class DataSourceAdvice implements MethodBeforeAdvice{
    private Set<String> MasterKeys = new HashSet<String>();
    public void setMasterKeys(Set<String> MasterKeys) {
        this.MasterKeys = MasterKeys;
    }

    @Override
    public void before(Method method, Object[] arg, Object target) throws Throwable {
        boolean isMaster = false;
        String methodName = method.getName();
        for(String accessMasterKey : MasterKeys) {
            if (methodName.startsWith(accessMasterKey)) {
                isMaster = true;
                break;
            }
        }
           DynamicDataSourceHolder.setDataSource("master");
       } else {
           DynamicDataSourceHolder.setDataSource("slave");
       }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值