zoukankan      html  css  js  c++  java
  • AbstractRoutingDataSource -- Spring提供的轻量级数据源切换方式

    AbstractRoutingDataSource 只支持单库事务,也就是说切换数据源要在开启事务之前执行。 spring DataSourceTransactionManager进行事务管理,开启事务,会将数据源缓存到DataSourceTransactionObject对象中进行后续的commit rollback等事务操作。
    出现多数据源动态切换失败的原因是因为在事务开启后,数据源就不能再进行随意切换了,也就是说,一个事务对应一个数据源。

    1.传统的Spring管理事务是放在Service业务层操作的,所以更换数据源的操作要放在这个操作之前进行。也就是切换数据源操作放在Controller层,可是这样操作会造成Controller层代码混乱的结果。
    故而想到的解决方案是将事务管理在数据持久 (Dao层) 开启,切换数据源的操作放在业务层进行操作,就可在事务开启之前顺利进行数据源切换,不会再出现切换失败了。
    2.如果管理事务是放在Service业务层操作的,Spring事务会在方法前获取数据连接connection,动态数据源的切面是在Dao层,但是这时还没有到DAO层进行路由选择,因此需要延迟加载数据源,需要用到LazyConnectionDataSourceProxy

    <bean id="parentDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <property name="initialSize" value="1"/>
            <property name="minIdle" value="2"/>
            <property name="validationQuery" value="SELECT 1 FROM DUAL"/>
            <property name="testOnCreate" value="true"/>
            <property name="testWhileIdle" value="true"/>
            <property name="testOnBorrow" value="true"/>
        </bean>
    
    
        <bean id="dataSource" parent="parentDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <bean id="dataSourceM2" parent="parentDataSource">
            <property name="driverClassName" value="${jdbcM2.driverClassName}"/>
            <property name="url" value="${jdbcM2.url}"/>
            <property name="username" value="${jdbcM2.username}"/>
            <property name="password" value="${jdbcM2.password}"/>
        </bean>
    
        <bean id="dataSourceR" parent="parentDataSource">
            <property name="driverClassName" value="${jdbcR.driverClassName}" />
            <property name="url" value="${jdbcR.url}" />
            <property name="username" value="${jdbcR.username}" />
            <property name="password" value="${jdbcR.password}" />
        </bean>
    
        <bean id="dynamicDataSource" class="com.cmcc.open.ss.config.DynamicDataSource">
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <entry key="dataSource" value-ref="dataSource"/>
                    <entry key="dataSourceM2" value-ref="dataSourceM2"/>
                    <entry key="dataSourceR" value-ref="dataSourceR"/>
                </map>
            </property>
            <property name="defaultTargetDataSource" ref="dataSource"/>
        </bean>
    
        <!-- Spring事务会在方法前获取数据连接connection,但是这时还没有到DAO层进行路由选择,因此需要延迟加载数据源,需要用到LazyConnectionDataSourceProxy。 -->
        <bean id="lazyDataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
            <property name="targetDataSource" ref="dynamicDataSource">
            </property>
        </bean>
    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="lazyDataSource"/>
            <property name="configLocation" value="classpath:acs_mysql.xml"></property>
        </bean>
    
        <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
        </bean>
    
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="lazyDataSource"/>
        </bean>
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
    

    3.如果不用LazyConnectionDataSourceProxy,可以给切面类上加上@Order(-1),让此切面优先于事务的切面执行

    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    @Component
    @Slf4j
    @Aspect
    @Order(-1)
    public class DataSourceAspect {
        /**
         *
         */
        @Pointcut("@within(com.hy.api.aop.DataSource) || @annotation(com.hy.api.aop.DataSource)")
        public void pointCut() {
    
        }
    
        /**
         *
         * @param dataSource
         */
        @Before("pointCut() && @annotation(dataSource)")
        public void doBefore(DataSource dataSource) {
            String ds = dataSource.value().getValue();
            log.info("====================dataSource: " + ds);
            MultipleDataSource.setDataSource(ds);
    
        }
    
        /**
         * 
         */
        @After("pointCut()")
        public void doAfter() {
            MultipleDataSource.clear();
        }
    }
    
    

    https://blog.csdn.net/qq_37502106/article/details/91044952

  • 相关阅读:
    20175310 《Java程序设计》第11周学习总结
    20175310 《Java程序设计》第10周学习总结
    2018-2019-2 20175310 实验三《敏捷开发与XP实践》实验报告
    20175310 《Java程序设计》第9周学习总结
    类定义(课下选做)
    # 2019-2020-4 《Java 程序设计》结对项目总结
    # 2019-2020-3 《Java 程序设计》第五周学习总结
    # 2019-2020-3 《Java 程序设计》实验一:Java开发环境的熟悉
    # 2019-2020-3 《Java 程序设计》第四周总结
    # 2019-2020-3 《Java 程序设计》第三周总结
  • 原文地址:https://www.cnblogs.com/zhangww/p/12510753.html
Copyright © 2011-2022 走看看