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

来源:春秋战国程序猿 发布时间:2018-11-14 11:22:19 阅读量:823

  多数据源,说白了,就是多数据库。因为我们配置数据源需要指定特定的数据库名称,如下,这是我们经常使用的配置数据源的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,该如何操作呢?同理,我们只需要对数据源的配置参数进行修改就可以了。




好了,关于多数据源的配置,就说到这里。接下来会给大家测试几个多数据源配置的实例。理论上可行的,还要经过实践检验。

--------------------- 


标签: 数据库
分享:
评论:
你还没有登录,请先