spring多数据源使用详解

数据库读写分离必然会引入多数据源,那么spring项目如何实现多数据源呢?

思路:spring在每次操作数据库的时候都会通过AbstractRoutingDataSource类中的determineTargetDataSource()方法获取当前数据源,我们可以通过AOP技术,在不同的切面,切入不同的数据源名称,使得spring获取的时候拿到的是不同的数据源。当然我们也可以不实用AOP技术,而是在数据库操作前手动改变将要实用的数据源名称,从而使得spring获取的时候拿到我们想要的数据源。

determineCurrentLookupKey()方法是抽象的,我们是可以实现这个类,重写此方法,然后按照思路中的想法来实现多数据源的自由切换

下面我们具体来实现

第一步:首先在spring的配置文件中进行多数据源配置

<?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:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    //这里我只写关键配置点
    
    <!-- 第一个基于druid的datasource -->
    <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        //连接信息省略
    </bean>

    <!-- 第二个基于druid的datasource -->
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        //连接信息省略
    </bean>

    <!-- 数据源集合 -->
    <bean id="dataSource"
        class="winclpt.spring.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="winclpt.spring.DataSourceType">
                <entry key="dataSource1" value-ref="dataSource1" />
                <entry key="dataSource2" value-ref="dataSource2" />
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="dataSource1" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="entityInterceptor" ref="hiberAspect" />
        <property name="hibernateProperties">
            <props>
                <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> -->
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
            </props>
        </property>
        <!-- 注解方式配置 -->
        <property name="packagesToScan">
            <list>
                <value></value>
            </list>
        </property>
    </bean>
    <!-- 配置事物管理器,在*ServiceImpl里写@Transactional就可以启用事物管理 -->
    <bean name="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="dataSourceInterceptor" class="winclpt.spring.DSAscept"/>    
    <aop:config>
        <aop:aspect ref="dataSourceInterceptor">
            <aop:pointcut id="test1"
                expression="execution(* winclpt.spring.*.*(..))" />
            <aop:pointcut id="test2"
                expression="execution(* winclpt.service.*.*(..))" />
            <aop:before pointcut-ref="test1" method="setdataSource1" />//在test1切面设置数据源为dataSource1
            <aop:before pointcut-ref="test2" method="setdataSource2" />//在test1切面设置数据源为dataSource1
 </aop:aspect> </aop:config> </beans>
DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource {

    /* 
     * 该方法必须要重写  方法是为了根据数据库标示符取得当前的数据库
     */
    protected Object determineCurrentLookupKey() {
        DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType();
        return dataSourceType;
    }
    public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
        super.setDataSourceLookup(dataSourceLookup);
    }
    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
    }
    public void setTargetDataSources(Map targetDataSources) {
        super.setTargetDataSources(targetDataSources);
    }

}
DataSourceType.java
public enum DataSourceType {
    dataSource1,dataSource2//所有spring xml中配置的数据源名称都应该在这里存在
}
DataSourceContextHolder.java
/**
 *类名:DataSourceContextHolder.java
 *功能:获得和设置上下文环境的类,主要负责改变上下文数据源的名称
 */
public class DataSourceContextHolder {

    private static final ThreadLocal contextHolder=new ThreadLocal();
    
    public static void setDataSourceType(DataSourceType dataSourceType){
        contextHolder.set(dataSourceType);
    }
    
    public static DataSourceType getDataSourceType(){
        return (DataSourceType) contextHolder.get();
    }
    
    public static void clearDataSourceType(){
        contextHolder.remove();
    }
    
}
DSAscept.java
public class DSAscept{
    public viod setdataSource1(){
     DataSourceContextHolder.setDataSourceType(DataSourceType.dataSource1);
    }          

    public viod setdataSource2(){
     DataSourceContextHolder.setDataSourceType(DataSourceType.dataSource2);
    } 

}

通过上面的操作,我们就可以通过AOP技术轻松的实现不同方法不同数据源,切换自由

当然我们也可以不实用AOP切面技术,可以在入库操作前手动调用如下方法改变当前ThreadLocal中的数据源名称,从而实现多数据源切换

public void save(int id){
    DataSourceContextHolder.setDataSourceType(DataSourceType.winclpt);
    //数据库操作
}

 

 
 

 

 

 

 

posted @ 2017-07-10 12:06  暗夜心慌方  阅读(1304)  评论(0编辑  收藏  举报