spring+hibernate动态数据源

spirng配置多个数据源呢,网上有许多帖子,有不清楚的同学可以搜一下,写的都很详细的。

大致思路就是:

  1. 配置多个数据源

  2. 配置一个MultiDataSource,里面的targetDataSources配置成一个map,map中裝上面配置好的数据源。这个MultiDataSource是继承自AbstractRoutingDataSource的,重写了determineCurrentLookupKey方法,里面返回一个正在使用的datasource的key(就是ThreadLocal里面的key

  3. datasource的key可以放到ThreadLocal中,然后通过设置ThreadLocal里面的key,来切换数据源。

  4. 当然SessionFactory里面的dataSource也就配置MultiDataSource就可以了

接下来主要讲讲动态数据源的实现

    按照上面多数据源的思路,我最早是根据动态的数据源配置,用代码创建DataSource,然后放入到DynamicDataSource的map中,然后reload配置(spring配置),SessionFactory是用的是同一个。

    后来使用了一段时间,发现不太好,一个SessionFactory,就会有多个用户争资源的问题,然后呢,我就给他加锁,每次只有一个用户在使用SessionFactory,这样一来所有业务必须排队执行,效率极其低下,然后就有了我以下的这套方案。

核心思路呢就是:DynamicDataSource+DynamicSessionFactory

废话少说,直接上配置:

<bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${database.driver.class.name}" />
		<property name="validationQuery" value="${database.validation.query}" />
		<property name="testWhileIdle" value="true" />
		<property name="timeBetweenEvictionRunsMillis" value="3600000" />
		<property name="minEvictableIdleTimeMillis" value="1800000" />
		<property name="testOnBorrow" value="true" />
        <property name="maxWait" value="6000"/>
        <property name="maxActive" value="20"/>
        <property name="maxIdle" value="20"/>
        <property name="minIdle" value="3"/>
    </bean>
	<bean id="defaultDataSource" parent="parentDataSource">
		<property name="url" value="${uc.database.url}" />
		<property name="username" value="${uc.database.username}" />
		<property name="password" value="${uc.database.password}" />
	</bean>
	<bean id="dataSource" class="com.itentek.gamebi.util.datasource.DynamicDataSource">
	   <property name="targetDataSources">
            <map>
            </map>
   		</property>
   		<property name="defaultTargetDataSource" ref="defaultDataSource"/>
   </bean>
    <!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>-->

    <bean id="parentSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="packagesToScan">
            <list>
                <value>com.itentek.**.bo</value>
            </list>
        </property>
        <property name="mappingLocations">
            <list>
                <value>classpath*:/mapping/**/*.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.cache.use_second_level_cache">true</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
                <prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache.xml</prop>
                <prop key="hibernate.jdbc.batch_size">30</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.connection.release_mode">auto</prop>
            </props>
        </property>
    </bean>

    <bean id="defaultSessionFactory" parent="parentSessionFactory">
         <property name="dataSource" ref="defaultDataSource"/>
    </bean>

    <bean id="dynamicSessionFactory" parent="parentSessionFactory" class="com.itentek.gamebi.util.datasource.DynamicSessionFactory">
        <property name="targetSessionFactory">
            <map>
                <entry key="default" value-ref="defaultSessionFactory"/>
            </map>
        </property>
    </bean>

   就是将SessionFactory也变成动态,每个datasource拥有一个SessionFactory。配置中DataSource和SessionFactory都配置一个parent,用来配置一些相同的默认配置,代码中新创建的DataSource和SessionFactory就可以只考虑需要修改的部分。然后也配置了一个default,就是系统启动后一些配置、用户等数据所在的数据源(因为我的多个数据源配置直接是存放数据库的)。

插播广告:javaQQ群 :84436262

下面是部分java代码

package com.itentek.gamebi.util.datasource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;

/*******************************************************************************
 *  功能说明: 动态数据源
  
 *  2012-11-29 下午6:16:09 awcui 创建文件
 * 
 *  修改说明: 创建文件

 *  2012-11-29 下午6:16:09 awcui 修改文件
 * 
 ******************************************************************************/

public class DynamicDataSource extends AbstractRoutingDataSource{
	
	/**
	 * logdb的数据源key
	 */
	public static final String DB_KEY_LOGDB = "logdb";
	
	/**
	 * subdb的数据源key(需要加subdb序号进行使用)
	 */
	public static final String DB_KEY_SUBDB = "subdb";


	/**
	 * 动态数据源
	 */
	private static DynamicDataSource dynamicDataSource;


    private Map<Object, Object> targetDataSources;

    /**
	 * 构造方法
	 */
	public DynamicDataSource(){
		dynamicDataSource=this;
	}

	/**
	 * 
	 * 功能 :得到动态数据源
	 * 开发:awcui 2012-11-30
	 * @return 动态数据源
	 */
	public DynamicDataSource getDynamicDataSource() {
		return dynamicDataSource;
	}

	/**
	 * 
	 * 功能 :设置动态数据源
	 * 开发:awcui 2012-11-30
	 * @param dynamicDataSource 动态数据源
	 */
	public void setDynamicDataSource(DynamicDataSource dynamicDataSource) {
		DynamicDataSource.dynamicDataSource = dynamicDataSource;
	}

	 
    @Override
     protected Object determineCurrentLookupKey() {
         return DataSourceContextHolder.getDataSourceKey();
     }
 
    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
         this.targetDataSources = targetDataSources;
         super.setTargetDataSources(targetDataSources);
         afterPropertiesSet();
     }
 
    /**
     * 
     * 功能 :添加数据源 
     * 开发:awcui 2012-11-30
     * @param key 数据源key
     * @param dataSource 数据源
     */
    public void addTargetDataSource(String key, DataSource dataSource) {
         targetDataSources.put(key, dataSource);
         this.setTargetDataSources(targetDataSources);
     }
 
    /**
     * 
     * 功能 : 创建数据源
     * 开发:awcui 2012-11-30 
     * @param driverClassName 数据库驱动
     * @param url url
     * @param username 账号
     * @param password 密码
     * @return 数据源
     */
    public static BasicDataSource createDataSource(String driverClassName,
             String url, String username, String password) {
         BasicDataSource dataSource = new BasicDataSource();
         dataSource.setDriverClassName(driverClassName);
         dataSource.setUrl(url);
         dataSource.setUsername(username);
         dataSource.setPassword(password);
         dataSource.setTestWhileIdle(true);
 
        if ("oracle.jdbc.driver.OracleDriver".equals(driverClassName)) {
             dataSource.setValidationQuery("SELECT 1 FROM DUAL");
         } else if ("com.mysql.jdbc.Driver".equals(driverClassName)) {
             dataSource.setValidationQuery("SELECT NOW()");
         }
        return dataSource;
     }
    
    /**
     * 
     * 功能 :改变数据源
     * 开发:awcui 2012-11-30
     * @param driverClassName 数据库驱动
     * @param url url
     * @param username 账号
     * @param password 密码
     */
    public static void changeDataSource(String driverClassName,
            String url, String username, String password,String dbkey){
    	BasicDataSource dataSource = createDataSource(driverClassName, url, username, password);
    	dynamicDataSource.addTargetDataSource(dbkey, dataSource);
    	DataSourceContextHolder.setDataSourceKey(dbkey);
    }
    
 }

以上代码中还有历史遗留痕迹,实际上目前的机制,changeDataSource和determineCurrentLookupKey都是用不上的,这个就是最老早切换数据源用,直接改key来切换,DataSourceContextHolder实际上里面只放了个ThreadLocal。

package com.itentek.gamebi.util.datasource;

import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;

import javax.sql.DataSource;
import java.util.Map;

/**
 * 功能: 动态sessionFactory
 * <p/>
 * User: awcui
 * Date: 13-8-7
 * Time: 下午5:04
 */
public class DynamicSessionFactory extends AnnotationSessionFactoryBean{
    private static Map<String, SessionFactory> targetSessionFactory;

    public SessionFactory createSessionFactory(DataSource dataSource) throws Exception {
        this.setDataSource(dataSource);
        this.afterPropertiesSet();

       return this.buildSessionFactory();
    }

    public Map<String,SessionFactory> getTargetSessionFactory(){
        return this.targetSessionFactory;
    }

   public void setTargetSessionFactory(Map<String,SessionFactory> targetSessionFactory) throws Exception {
       this.targetSessionFactory = targetSessionFactory;
       afterPropertiesSet();
   }

    public void addTargetSessionFactory(String key,SessionFactory sessionFactory) throws Exception {
        this.targetSessionFactory.put(key,sessionFactory);
        this.setTargetSessionFactory(targetSessionFactory);

    }


}

上面是DynamicSessionFactory,实际上也就是一些创建SessionFactory和存取map的方法。

这样一来就保证了SessionFactory和DataSource一一对应的关系,使用上也是方便灵活的多

  1. 如果是多数据源,不需要动态,可以直接配置多套,在业务的Dao里分别注入不同的SessionFactory即可

  2. 如果是动态的,可以使用aop,写一定的规则,根据规则来选取SessionFactory

  3. 当然也可以直接取到DynamicSessionFactory(可以直接注入),然后根据key去直接取对应的SesionFactory来用

我是第三种用法,而且我不光使用了Hibernate,还使用了部分JDBC,由于DataSource也是动态,而且也有map缓存,随时组装JDBC来用也是非常方便的,当然不用spring,直接自己写个缓存来管理这些SessionFactory也是可以的。

下面是DynamicSessionFactory注入和组装JdbcTemplate的部分demo

    protected static Map<String,JdbcTemplate> jdbcTemplateMap = new HashMap<String,JdbcTemplate>();

    @Autowired
    private DynamicSessionFactory dynamicSessionFactory;
    public Map<String, JdbcTemplate> getJdbcTemplateMap() {
        return jdbcTemplateMap;
    }

    public void addJdbcTemplate(String jdbcTemplateKey,JdbcTemplate jdbcTemplate){
        BaseDaoImpl.jdbcTemplateMap.put(jdbcTemplateKey,jdbcTemplate);
    }
    
    /**
     * 得到jdbcTemplate
     * @param jdbcTemplateKey
     * @return
     */
    public JdbcTemplate getJdbcTemplate(String jdbcTemplateKey){
        JdbcTemplate jdbcTemplate = this.getJdbcTemplateMap().get(jdbcTemplateKey);
        if(StringUtils.isEmpty(jdbcTemplate)){
            DataSource ds = this.createDataSource(jdbcTemplateKey);
            jdbcTemplate = new JdbcTemplate(ds);
            this.addJdbcTemplate(jdbcTemplateKey,jdbcTemplate);
        }
        return jdbcTemplate;
    }


你可能感兴趣的:(多数据源,Spring多数据源,动态数据源,hibernate多数据源,hibernate动态数据源,spring动态数据源)