首页 > 代码库 > hibernate动态切换数据源

hibernate动态切换数据源

起因:

公司的当前产品,主要是两个项目集成的,一个是java项目,还有一个是php项目,两个项目用的是不同的数据源,但都是mysql数据库,因为java这边的开发工作已经基本完成了,而php那边任务还很多,人手也比较紧,产品上线日期紧促,所以领导希望java这边能够帮助php那边写接口,所以需要切换数据源

思路:

动态切换数据源确切的来说是在同一类型数据库的情况下的。意思就是说 , 在系统中的使用的数据库分布在多台数据库服务器或者在同台服务器上的多个数据库. 在运行时期间根据某种标识符来动态的选择当前操作的数据库.
     1.数据源是相同类型的数据库: 一个SessionFactory+动态数据源+一个事务管理器
     2.数据源是不同类型的数据库: 根据类型 配置多套SessionFactory

困难解决:

1、配置dataSource的时候将id写成那么,而动态数据源的设置引用的是id

2、动态数据源的切换不能用枚举来标识,得用常量

3、在mysql数据库中不同种类的table也照样起作用

4、spring的事务处理可能会影响动态数据源的切换(尚未发现该问题)

测试环境:

1、hibernate

2、spring

3、mysql

最终实现:

1、首先得配置多个数据源

<!-- 配置数据源 使用的是Druid数据源 -->
    <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url1}" />
        <property name="username" value="${jdbc.username1}" />
        <property name="password" value="${jdbc.password1}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />
        
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true" />
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat" />
    </bean>
    
    <!-- 配置数据源 使用的是Druid数据源 -->
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="url" value="http://www.mamicode.com/${jdbc.url2}" />
        <property name="username" value="http://www.mamicode.com/${jdbc.username2}" />
        <property name="password" value="http://www.mamicode.com/${jdbc.password2}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="http://www.mamicode.com/0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="http://www.mamicode.com/20" />
        
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="http://www.mamicode.com/0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="http://www.mamicode.com/60000" />
        <property name="poolPreparedStatements" value="http://www.mamicode.com/true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="http://www.mamicode.com/33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="http://www.mamicode.com/${validationQuery}" />
        <property name="testOnBorrow" value="http://www.mamicode.com/false" />
        <property name="testOnReturn" value="http://www.mamicode.com/false" />
        <property name="testWhileIdle" value="http://www.mamicode.com/true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="http://www.mamicode.com/60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="http://www.mamicode.com/25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="http://www.mamicode.com/true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="http://www.mamicode.com/1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="http://www.mamicode.com/true" />
        <!-- 监控数据库 -->
        <property name="filters" value="http://www.mamicode.com/mergeStat" />
    </bean>

然后配置多数据源映射关系

<!-- 编写spring 配置文件的配置多数源映射关系 -->  
    <bean id="dataSource" class="com.asen.util.DynamicDataSource">  
        <property name="targetDataSources">  
            <map key-type="java.lang.String">  
                <entry key="CCVZB" value-ref="dataSource1"></entry>  
                <entry key="CCSC" value-ref="dataSource2"></entry>  
            </map>  
        </property>
        <property name="defaultTargetDataSource" ref="dataSource1"></property>  
    </bean>

然后是hibernate的SessionFactory

<!-- 配置hibernate的SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <!-- 注入数据源 相关信息看源码 -->
        <property name="dataSource" ref="dataSource" />
        <!-- hibernate配置信息 -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            </props>
        </property>
        <!-- 扫描hibernate注解配置的entity -->
        <property name="packagesToScan" value="com.asen.app.*" />
    </bean>

这样配置文件就写好了,但是还需要三个类

package com.asen.util;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override  
    protected Object determineCurrentLookupKey() {  
        return DynamicDataSourceHolder.getDataSourceType();  
    }  
  
}
package com.asen.util;

public class DynamicDataSourceGlobal {
    public static final String CCVZB="CCVZB";  
    public static final String CCSC="CCSC"; 
}
package com.asen.util;

public class DynamicDataSourceHolder {
    // 线程本地环境  
    private static final ThreadLocal contextHolder = new ThreadLocal();  
      
    // 设置数据源类型  
    public static void setDataSourceType(String dataSourceType) {  
        contextHolder.set(dataSourceType);  
    }  
  
    // 获取数据源类型  
    public static String getDataSourceType() {  
        return (String) contextHolder.get();  
    }  
  
    // 清除数据源类型  
    public static void clearDataSourceType() {  
        contextHolder.remove();  
    }  

}

具体切换

package com.asen.app.dao.impl;

import java.util.List;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.asen.app.dao.IHomeFunctionDao;
import com.asen.util.DynamicDataSourceGlobal;
import com.asen.util.DynamicDataSourceHolder;

@Repository("homeFunctionDao")
public class HomeFunctionDaoImpl extends HibernateDaoSupport implements IHomeFunctionDao {
    
    @Resource  
    public void setSessionFacotry(SessionFactory sessionFacotry) {
        super.setSessionFactory(sessionFacotry);  
    }

    @Override
    public List getHomeFunctionList() throws Exception {
        DynamicDataSourceHolder.setDataSourceType(DynamicDataSourceGlobal.CCVZB);
        SessionFactory sessionFactory = getHibernateTemplate().getSessionFactory();
        Session session = sessionFactory.openSession();
        Query query = session.createQuery("from HomeFunction");
        return query.list();
    }
}

hibernate动态切换数据源