zoukankan      html  css  js  c++  java
  • SSM多数据源配置


    继承Spring的AbstractRoutingDataSource来实现多数据源配置

    1. 数据源配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:task="http://www.springframework.org/schema/task"
        xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
            <!-- db配置目录 -->
            <context:property-placeholder file-encoding="utf-8" location="classpath:db.properties" />
            <!-- 扫描目录 -->
            <context:component-scan base-package="demo.controller"></context:component-scan>
        <!-- executor线程池,含义和java.util.concurrent.Executor是一样的,pool-size的大小官方推荐为5~10 -->
        <task:executor id="executor" pool-size="5" />
        <!-- scheduler的pool-size是ScheduledExecutorService线程池,默认为1 -->
        <task:scheduler id="scheduler" pool-size="5" />
        <task:annotation-driven executor="executor" scheduler="scheduler" />
         <!-- 数据库连接池配置 -->
         <bean id="dataSourceTask" name="dataSourceTask" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
            <!-- 指定连接数据库的驱动-->  
            <property name="driverClass" value="${db.task.jdbc.driverClassName}"/>  
            <!-- 指定连接数据库的URL-->  
            <property name="jdbcUrl" value="${db.task.jdbc.url}"/>  
            <!-- 指定连接数据库的用户名-->  
            <property name="user" value="${db.task.jdbc.user}"/>  
            <!-- 指定连接数据库的密码-->  
            <property name="password" value="${db.task.jdbc.password}"/>  
            <!-- 指定连接池中保留的最大连接数. Default:15-->  
            <property name="maxPoolSize" value="${db.task.jdbc.maxPoolSize}"/>  
            <!-- 指定连接池中保留的最小连接数-->  
            <property name="minPoolSize" value="${db.task.jdbc.minPoolSize}"/>  
            <!-- 指定连接池的初始化连接数  取值应在minPoolSize 与 maxPoolSize 之间.Default:3-->  
            <property name="initialPoolSize" value="${db.task.jdbc.initialPoolSize}"/>  
            <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。 Default:0-->  
            <property name="maxIdleTime" value="${db.task.jdbc.maxIdleTime}"/>  
            <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数. Default:3-->  
            <property name="acquireIncrement" value="${db.task.jdbc.acquireIncrement}"/>  
            <!-- JDBC的标准,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements属于单个connection
                而不是整个连接池所以设置这个参数需要考虑到多方面的因数.如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0-->  
            <property name="maxStatements" value="${db.task.jdbc.maxStatements}"/>  
            <!-- 每60秒检查所有连接池中的空闲连接.Default:0 -->  
            <property name="idleConnectionTestPeriod" value="${db.task.jdbc.idleConnectionTestPeriod}"/>
            <!--连接池获取新连接的时间,超时后将 抛出SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
            <property name="checkoutTimeout" value="${db.task.jdbc.checkoutTimeout}"/>  
        </bean>
         
         <bean id="dataSourceEnv" name="dataSourceEnv" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
            <!-- 指定连接数据库的驱动-->  
            <property name="driverClass" value="${db.lcmyw.jdbc.driverClassName}"/>  
            <!-- 指定连接数据库的URL-->  
            <property name="jdbcUrl" value="${db.lcmyw.jdbc.url}"/>  
            <!-- 指定连接数据库的用户名-->  
            <property name="user" value="${db.lcmyw.jdbc.user}"/>  
            <!-- 指定连接数据库的密码-->  
            <property name="password" value="${db.lcmyw.jdbc.password}"/>  
            <!-- 指定连接池中保留的最大连接数. Default:15-->  
            <property name="maxPoolSize" value="${db.lcmyw.jdbc.maxPoolSize}"/>  
            <!-- 指定连接池中保留的最小连接数-->  
            <property name="minPoolSize" value="${db.lcmyw.jdbc.minPoolSize}"/>  
            <!-- 指定连接池的初始化连接数  取值应在minPoolSize 与 maxPoolSize 之间.Default:3-->  
            <property name="initialPoolSize" value="${db.lcmyw.jdbc.initialPoolSize}"/>  
            <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。 Default:0-->  
            <property name="maxIdleTime" value="${db.lcmyw.jdbc.maxIdleTime}"/>  
            <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数. Default:3-->  
            <property name="acquireIncrement" value="${db.lcmyw.jdbc.acquireIncrement}"/>  
            <!-- JDBC的标准,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements属于单个connection
                而不是整个连接池所以设置这个参数需要考虑到多方面的因数.如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0-->  
            <property name="maxStatements" value="${db.lcmyw.jdbc.maxStatements}"/>  
            <!-- 每60秒检查所有连接池中的空闲连接.Default:0 -->  
            <property name="idleConnectionTestPeriod" value="${db.lcmyw.jdbc.idleConnectionTestPeriod}"/>
            <!--连接池获取新连接的时间,超时后将 抛出SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
            <property name="checkoutTimeout" value="${db.lcmyw.jdbc.checkoutTimeout}"/>  
        </bean>
        
        <bean id="dataSource" class="demo.comm.DynamicDataSource">
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <entry key="dataSourceTask" value-ref="dataSourceTask"></entry>
                    <entry key="dataSourceEnv" value-ref="dataSourceEnv"></entry>
                </map>
            </property>
            <property name="defaultTargetDataSource" ref="dataSourceEnv"></property>
        </bean>
        
        <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
         <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
             <property name="dataSource" ref="dataSource" />
             <!-- 自动扫描mapping.xml文件 -->
             <property name="mapperLocations" value="classpath*:demo/dao/*.xml"/>
         </bean>
         
         <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
         <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
             <property name="basePackage" value="demo.dao" />
             <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
         </bean>
         
         <!-- 配置事物管理器 -->
         <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
             <property name="dataSource" ref="dataSource" />
         </bean>
         
         <!-- 拦截器方式配置事物 -->
         <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="add*" propagation="REQUIRED" />
                <tx:method name="insert*" propagation="REQUIRED" />
                <tx:method name="update*" propagation="REQUIRED" />
                <tx:method name="delete*" propagation="REQUIRED" />
                
                <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
                <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
                <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
                <tx:method name="seach*" propagation="SUPPORTS" read-only="true" />
            </tx:attributes>         
         </tx:advice>
         
         <!-- spring aop事物管理 -->
         <aop:config>
        <aop:pointcut id="transactionPointcut" 
                     expression="execution(* demo.service..*Impl.*(..))" />
            <!-- 设置order的值为2,使得数据库事物开启在数据源切换之后,否则数据源切换不会达到效果  -->
           <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" order="2"/>
         </aop:config>
         
         <!-- 数据源动态切换切面配置 -->
         <aop:config>
            <aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor" order="1">
                <!-- 拦截所有service实现类的方法 -->
                <aop:pointcut id="dataSourcePointcut" 
                         expression="execution(* demo.service..*Impl.*(..))"/>
                    <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />           
            </aop:aspect>
          </aop:config>
         
         <!-- 数据源动态切换实体 -->
         <bean id="dataSourceInterceptor" class="demo.comm.DynamicDataSourceInterceptor"/>
         <context:component-scan base-package="demo.service.impl"></context:component-scan>
    </beans>

    2. 定义一个类继承AbstractRoutingDataSource实现determineCurrentLookupKey方法,来实现数据库的动态切换

    package demo.comm;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            // 从自定义的位置获取数据源标识
            return DynamicDataSourceHolder.getDataSource();
        }
    
    }

    3. 定义工具类,用于动态切换数据源

    package demo.comm;
    
    public class DynamicDataSourceHolder {
        /**
         * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
         */
        private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>();
    
        public static String getDataSource() {
            return THREAD_DATA_SOURCE.get();
        }
    
        public static void setDataSource(String dataSource) {
            THREAD_DATA_SOURCE.set(dataSource);
        }
    
        public static void clearDataSource() {
            THREAD_DATA_SOURCE.remove();
        }
    
    }

    4.定义注解,通过注解的值来获取当前数据源,并进行切换

      

    package demo.comm;
    
    import java.lang.annotation.ElementType;  
    import java.lang.annotation.Retention;  
    import java.lang.annotation.RetentionPolicy;  
    import java.lang.annotation.Target;  
      
    @Target({ ElementType.TYPE, ElementType.METHOD})  
    @Retention(RetentionPolicy.RUNTIME)  
    public @interface DataSource {  
      
        String value();  
    }  

    定义拦截器,解析注解切换数据源

    package demo.comm;
    
    import java.lang.reflect.Method;  
    
    import org.aspectj.lang.JoinPoint;  
    import org.aspectj.lang.reflect.MethodSignature;  
      
    /** 
     *  
    * ClassName: DynamicDataSourceInterceptor <br/>  
    * Function: 数据源动态切换拦截器. <br/>  
    * date: 2017年3月15日 下午10:28:17 <br/>  
    *  
    * @author JohnFNash  
    * @version   
    * @since JDK 1.6 
     */  
    public class DynamicDataSourceInterceptor {  
              
        /** 
         * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源 
         *  
         * @param point 
         * @throws Exception 
         */  
        public void intercept(JoinPoint point) throws Exception {  
            Class<?> target = point.getTarget().getClass();  
            MethodSignature signature = (MethodSignature) point.getSignature();  
            resolveDataSource(target, signature.getMethod());  
        }  
      
        /** 
         * 提取目标对象方法注解和类型注解中的数据源标识 
         *  
         * @param clazz 
         * @param method 
         */  
        private void resolveDataSource(Class<?> clazz, Method method) {  
            try {  
                Class<?>[] types = method.getParameterTypes();  
                // 默认使用类型注解  
                if (clazz.isAnnotationPresent(DataSource.class)) {  
                    DataSource source = clazz.getAnnotation(DataSource.class);  
                    DynamicDataSourceHolder.setDataSource(source.value());  
                }  
                // 方法注解可以覆盖类型注解  
                Method m = clazz.getMethod(method.getName(), types);  
                if (m != null && m.isAnnotationPresent(DataSource.class)) {  
                    DataSource source = m.getAnnotation(DataSource.class);  
                    DynamicDataSourceHolder.setDataSource(source.value());  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  

    注意:设置执行顺序为1,并使用 aop:before 在数据库事物开启前进行数据源切换

    @DataSource注解使用示例:

    package demo.service.impl;
    
    import javax.annotation.Resource;
    
    import org.springframework.stereotype.Service;
    
    import demo.comm.DataSource;
    import demo.dao.FxnsrDAO;
    import demo.dao.TestDAO;
    import demo.entity.FxnsrEntity;
    import demo.entity.TestEntity;
    import demo.service.FxnsrService;
    import demo.service.TestService;
    
    
    @Service("testService")
    @DataSource("dataSourceEnv") 
    public class TestServiceImpl implements TestService{
    
        @Resource(name="testDAO")
        TestDAO testDAO;
        @Override
        public String seachTest() {
            return testDAO.seachTest();
        }
        
        
        
        
        //set
        public void setTestDAO(TestDAO testDAO) {
            this.testDAO = testDAO;
        }
    
        
        
    }

    上面 @DataSource 注解是定义在类上的,也可以定义在方法上,方法上定义的注解优先级高于定义在类上的注解

    文章转自:http://blog.csdn.net/johnf_nash/article/details/63260561

  • 相关阅读:
    20169215 缓冲区溢出漏洞实验
    20169215 2016-2017-2 实验二Nmap的使用与分析
    20169215 2016-2017-2 《网络攻防实践》/《网络攻击与防范》第八周学习总结
    Numpy Usage Introduction
    [Example of Sklearn]
    [Example of Sklearn]
    [Example of Sklearn]
    [Scikit-Learn]
    [Scikit-Learn]
    [Scikit-Learn]
  • 原文地址:https://www.cnblogs.com/tytr/p/8565838.html
Copyright © 2011-2022 走看看