您的位置:首页 > 编程语言 > Java开发

SpringMVC框架中多数据源的配置问题、datasource

2016-01-09 16:10 435 查看
多数据源,说白了,就是多数据库。因为我们配置数据源需要指定特定的数据库名称,如下,这是我们经常使用的配置数据源的XML文件内容中的一部分:

<!-- 配置数据源dataSource,连接MySQL数据库 、数据库:learn_system -->
    <!--
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName"
            value="com.mysql.jdbc.Driver">
        </property>
        <property name="url"
            value="jdbc:mysql://localhost:3306/learn_system">
        </property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    -->


这里value="jdbc:mysql://localhost:3306/learn_system"的learn_system就是我们的数据库名称。

如果我们需要配置多个数据源,我们就需要写多个bean,每个bean对应一个数据源。难点在于,如何控制多个数据源之间的切换。这里我们需要借助ThreadLocal类,这个类位于java.lang包下,首先说明ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后,能够优雅的解决一些实际问题,比如:

1,Hibernate中的OpenSessionInView,就是借助ThreadLocal保存Session对象;

2,数据库连接,借助ThreadLocal来传递Connection对象;

同样的,今天我们实现多数据源,也要借助ThreadLocal类,通过ThreadLocal类传递数据源的参数,我们这里传递的是bean的id,也就是SpringMVC中bean的名称,通过这个id,我们就可以调用相应的bean,这样就实现了不同数据源之间的切换。

首先要明确:

* ThreadLocal类,是什么?首先ThreadLocal不是Thread,因为如果我们要创建

* 一个线程,我们需要继承Thread类或者实现Runnable接口,ThreadLocal没有

* 继承Thread类,也没有继承Runnable接口。

ThreadLocal的作用,就是将本地变量,也就是当前内存中的变量,与线程关联起来。

好的,废话不多说,先上代码:

<bean id="datasource_test" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName"
			value="com.mysql.jdbc.Driver">
		</property>
		<property name="url"
			value="jdbc:mysql://localhost:3306/test">
		</property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
	</bean> 

	<bean id="datasource_learn_system" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName"
			value="com.mysql.jdbc.Driver">
		</property>
		<property name="url"
			value="jdbc:mysql://localhost:3306/learn_system">
		</property>
		<property name="username" value="root"></property>
		<property name="password" value="root"></property>
	</bean>


上面配置了2个数据源:datasource_test 和 datasource_learn_system,对应MySQL数据库中的2个不同的数据库;

然后我们需要写一个类:DynamicDataSource

<span style="font-size:14px;">import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/*
 * 配置多数据源
 */

public class DynamicDataSource extends AbstractRoutingDataSource{

	public static final String DATASOURCE_TEST = "datasource_test";
	
	public static final String DATASOURCE_LEARN_SYSTEM = "datasource_learn_system";
	//本地线程,获取当前正在执行的currentThread
	public static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 
	
	public static void setCustomerType(String customerType) {

        contextHolder.set(customerType);
        
    }

    public static String getCustomerType() {

        return contextHolder.get();
         
    }

    public static void clearCustomerType() {

        contextHolder.remove();
    
    }

	@Override
	protected Object determineCurrentLookupKey() {
		
		return getCustomerType();
		
	}
}</span>


接下来我们要在配置文件中进行配置,

<bean id="dataSource" class="com.spring.dynamic_datasource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="datasource_test" key="datasource_test"></entry>
                <entry value-ref="datasource_learn_system" key="datasource_learn_system"></entry>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="datasource_learn_system"></property>
	</bean>


好了,通过这些代码,我们就可以实现多数据源的切换了,接下来,我们只需要在执行数据库操作之前,切换数据源,就可以实现动态切换数据库的项目需求了。接下来看一下实例代码:

//查询当前用户的所有上传图片
	@Override
	public boolean saveUploadPicture(Picture_of_user picture_of_user) {
		//定义一个Boolean类型的flag,用来表示查询状态
		boolean flag = false;
		
		sql = "insert into picture_of_user(id,picture_name,picture_size,upload_date,picture_type,username) " +
				"values(?,?,?,?,?,?);";
		
		//切换数据源 datasource_test
		//这段代码相当于,把String类型的参数 datasource_test 放在了保存到了本地线程的当前线程中,也就是当前正在执行的线程。
		DynamicDataSource.setCustomerType(DynamicDataSource.DATASOURCE_LEARN_SYSTEM);

		int i = this.getJdbcTemplate().update(sql, new Object[]{
				null,
				picture_of_user.getPicture_name(),
				picture_of_user.getPicture_size(),
				picture_of_user.getUpload_date(),
				picture_of_user.getPicture_type(),
				picture_of_user.getUsername()
		});
		//如果插入操作执行成功,则flag=true;否则flag=flase
		if(i > 0){
			//测试输出
			System.out.println("i = " + i);
			flag = true;
		}
		else{
			//测试输出
			System.out.println("i = " + i);
			flag = false;
		}
		return flag;
	}


我们只需要加上这一行代码,就可以实现数据源切换了:

DynamicDataSource.setCustomerType(DynamicDataSource.DATASOURCE_LEARN_SYSTEM);

这段代码做了什么呢?这段代码,把当前要选择的数据源,保存到ThreadLocal对象中。

接下来我们看一下源码:

<span style="font-size:14px;">package com.spring.dynamic_datasource;

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

/*
 * 配置多数据源
 */

public class DynamicDataSource extends AbstractRoutingDataSource{

	public static final String DATASOURCE_TEST = "datasource_test";
	
	public static final String DATASOURCE_LEARN_SYSTEM = "datasource_learn_system";
	//本地线程,获取当前正在执行的currentThread
	public static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); 
	
	public static void setCustomerType(String customerType) {
		//把当前请求的参数,存入当前线程,这个参数是我们定义的DATASOURCE_TEST 或者 DATASOURCE_LEARN_SYSTEM
		//我们来看一下源码:
		//public void set(T value) {
		//    Thread t = Thread.currentThread();
		//    ThreadLocalMap map = getMap(t);
		//    if (map != null)
		//        map.set(this, value);
		//    else
		//        createMap(t, value);
		//这段代码的官方说明如下
	    /**
	     * Sets the current thread's copy of this thread-local variable
	     * to the specified value.  Most subclasses will have no need to 
	     * override this method, relying solely on the {@link #initialValue}
	     * method to set the values of thread-locals.
	     *
	     * @param value the value to be stored in the current thread's copy of
	     *        this thread-local.
	     */
		
        //测试输出
        System.out.println("当前切换的数据源 = " + customerType);

        contextHolder.set(customerType);
        
    }

    public static String getCustomerType() {

    	//官方文档如下:

        /**
         * Returns the value in the current thread's copy of this
         * thread-local variable.  If the variable has no value for the
         * current thread, it is first initialized to the value returned
         * by an invocation of the {@link #initialValue} method.
         *
         * @return the current thread's value of this thread-local
         */
        //public T get() {
            //Thread t = Thread.currentThread();
            //ThreadLocalMap map = getMap(t);
            //if (map != null) {
                //ThreadLocalMap.Entry e = map.getEntry(this);
                //if (e != null)
              //      return (T)e.value;
            //}
          //  return setInitialValue();
        //}
        return contextHolder.get();
         
    }

    public static void clearCustomerType() {

        /**
         * Removes the current thread's value for this thread-local
         * variable.  If this thread-local variable is subsequently
         * {@linkplain #get read} by the current thread, its value will be
         * reinitialized by invoking its {@link #initialValue} method,
         * unless its value is {@linkplain #set set} by the current thread
         * in the interim.  This may result in multiple invocations of the
         * <tt>initialValue</tt> method in the current thread.
         *
         * @since 1.5
         */
    	//源代码如下:
         //public void remove() {
           //  ThreadLocalMap m = getMap(Thread.currentThread());
             //if (m != null)
               //  m.remove(this);
         //}
        contextHolder.remove();
    
    }

	@Override
	protected Object determineCurrentLookupKey() {
		
		return getCustomerType();
		
	}

}</span>


public class ThreadLocal<T> 类没有继承其他类,只是间接继承了Object类,我们来看一下官方对这个类的使用说明:

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
 * copy of the variable.  <tt>ThreadLocal</tt> instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is
 * assigned the first time it invokes <tt>UniqueThreadIdGenerator.getCurrentThreadId()</tt> and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class UniqueThreadIdGenerator {
 *
 *     private static final AtomicInteger uniqueId = new AtomicInteger(0);
 *
 *     private static final ThreadLocal < Integer > uniqueNum = 
 *         new ThreadLocal < Integer > () {
 *             @Override protected Integer initialValue() {
 *                 return uniqueId.getAndIncrement();
 *         }
 *     };
 * 
 *     public static int getCurrentThreadId() {
 *         return uniqueId.get();
 *     }
 * } // UniqueThreadIdGenerator
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the <tt>ThreadLocal</tt>
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist). 
 *
 * @author  Josh Bloch and Doug Lea
 * @version 1.42, 06/23/06
 * @since   1.2
 */


最后还是要重复说明:

* ThreadLocal类,是什么?首先ThreadLocal不是Thread,因为如果我们要创建

* 一个线程,我们需要继承Thread类或者实现Runnable接口,ThreadLocal没有

* 继承Thread类,也没有继承Runnable接口。

ThreadLocal做了什么?ThreadLocal的作用,就是将本地变量,也就是当前内存中的变量,与线程关联起来,就像官方描述文档中说的:

* ThreadLocal provides thread-local variables. These variables differ from

* their normal counterparts in that each thread that accesses one has its own, independently initialized

* copy of the variable. ThreadLocal instances are typically private

* static fields in classes that wish to associate state with a thread (e.g.,

* a user ID or Transaction ID).

思考:如果我们需要配置3个数据源,该如何操作呢?其实很简单,我们只需要再配置一个数据源就可以了。

思考:如果我们在项目中使用不同的数据库系统,比如MySQL、Oracle,该如何操作呢?同理,我们只需要对数据源的配置参数进行修改就可以了。

好了,关于多数据源的配置,就说到这里。接下来会给大家测试几个多数据源配置的实例。理论上可行的,还要经过实践检验。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: