分库设计

<!-- 数据源(主库) -->                        
        <bean id="dataSource_main" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverclass}" />
            <property name="jdbcUrl" value="${jdbc.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            
            <property name="maxPoolSize" value="${c3p0.pool.size.max}" />
            <property name="minPoolSize" value="${c3p0.pool.size.min}" />
            <property name="initialPoolSize" value="${c3p0.pool.size.ini}" />
            <property name="acquireIncrement" value="${c3p0.pool.size.increment}" />
        </bean>
        
        <!-- 数据源(从库) -->                        
        <bean id="dataSource_1" parent="dataSource_main">
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/lsn_surveypark0909_1" />
        </bean>
        
        
        <!-- 数据源路由器 -->
        <bean id="dataSource_router" class="cn.itcast.surveypark.datasource.SurveyparkDataSourceRouter">
            <property name="targetDataSources">
                <map>
                    <entry key="odd" value-ref="dataSource_main" />
                    <entry key="even" value-ref="dataSource_1" />
                </map>
            </property>
            <property name="defaultTargetDataSource" ref="dataSource_main" />
        </bean>
    <!-- aop事务配置 -->
        <aop:config>
            <!-- 事务切入点 -->
            <aop:pointcut expression="execution(* *..*Service.*(..))" id="txPointcut"/>
            <!-- 日志切入点 -->
            <aop:pointcut expression="(execution(* *..*Service.save*(..))
                                    or execution(* *..*Service.update*(..))
                                    or execution(* *..*Service.delete*(..))
                                    or execution(* *..*Service.batch*(..))
                                    or execution(* *..*Service.new*(..))) and !bean(logService)" 
                        id="loggerPointcut"/>
            
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" order="1"/>
            
            <!-- 配置日志切面 -->
            <aop:aspect id="loggerAspect" ref="logger" order="0">
                <aop:around method="record" pointcut-ref="loggerPointcut"/>
            </aop:aspect>
        </aop:config>
aop
/**
 * 调查令牌,绑定到当前的线程,传播到数据源路由器.进行分库判断
 */
public class SurveyToken {
    
    private Survey currentSurvey ;
    
    private static ThreadLocal<SurveyToken> t = new ThreadLocal<SurveyToken>();
    
    public Survey getCurrentSurvey() {
        return currentSurvey;
    }

    public void setCurrentSurvey(Survey currentSurvey) {
        this.currentSurvey = currentSurvey;
    }

    /**
     * 将令牌对象绑定到当前线程
     */
    public static void bindingToken(SurveyToken token){
        t.set(token);
    }
    
    /**
     * 从当前线程取得绑定的令牌对象
     */
    public static SurveyToken getCurrentToken(){
        return t.get() ;
    }
    
    /**
     * 解除令牌的绑定
     */
    public static void unbindToken(){
        t.remove() ;
    }
}
token

 

主要思想是自定义一个方法继承AbstractRoutingDataSource,在保存答案方法前绑定令牌到当前线程,然后用debug模式看在哪里会调用数据路由器,需等答案保存完毕立刻

解除令牌的绑定,然后log保存就不会受影响(因为从库没有log表)。所以要注意2个切面配置顺序,可以看图

特别注意: 由于分库会有并发情况,需要把令牌的绑定到当前线程,然后取也是在同一个线程取。

 

    //绑定令牌到当前线程
            SurveyToken token = new SurveyToken();
            token.setCurrentSurvey(getCurrentSurvey());
            SurveyToken.bindingToken(token);
            
            //TODO 答案入库
            surveyService.saveAnswers(processAnswers());
View Code
/**
 * 自定义数据源路由器,根据id来判断是保存在哪个库中,实际工作中情况肯定比这复杂。
 */
public class SurveyparkDataSourceRouter extends AbstractRoutingDataSource {

    protected Object determineCurrentLookupKey() {
        SurveyToken token = SurveyToken.getCurrentToken();
        if(token != null){
            int id = token.getCurrentSurvey().getId();
            //解除绑定
            SurveyToken.unbindToken();
            return (id % 2) == 0?"even":"odd" ;
        }
        return null;
    }
}
posted @ 2017-03-01 23:03  SKYisLimit  阅读(168)  评论(0编辑  收藏  举报