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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM