SpringMVC框架中多数据源的配置问题、datasource
2016-01-09 16:10
435 查看
多数据源,说白了,就是多数据库。因为我们配置数据源需要指定特定的数据库名称,如下,这是我们经常使用的配置数据源的XML文件内容中的一部分:
这里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的作用,就是将本地变量,也就是当前内存中的变量,与线程关联起来。
好的,废话不多说,先上代码:
上面配置了2个数据源:datasource_test 和 datasource_learn_system,对应MySQL数据库中的2个不同的数据库;
然后我们需要写一个类:DynamicDataSource
接下来我们要在配置文件中进行配置,
好了,通过这些代码,我们就可以实现多数据源的切换了,接下来,我们只需要在执行数据库操作之前,切换数据源,就可以实现动态切换数据库的项目需求了。接下来看一下实例代码:
我们只需要加上这一行代码,就可以实现数据源切换了:
DynamicDataSource.setCustomerType(DynamicDataSource.DATASOURCE_LEARN_SYSTEM);
这段代码做了什么呢?这段代码,把当前要选择的数据源,保存到ThreadLocal对象中。
接下来我们看一下源码:
public class ThreadLocal<T> 类没有继承其他类,只是间接继承了Object类,我们来看一下官方对这个类的使用说明:
最后还是要重复说明:
* 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,该如何操作呢?同理,我们只需要对数据源的配置参数进行修改就可以了。
好了,关于多数据源的配置,就说到这里。接下来会给大家测试几个多数据源配置的实例。理论上可行的,还要经过实践检验。
<!-- 配置数据源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,该如何操作呢?同理,我们只需要对数据源的配置参数进行修改就可以了。
好了,关于多数据源的配置,就说到这里。接下来会给大家测试几个多数据源配置的实例。理论上可行的,还要经过实践检验。
相关文章推荐
- javaEE开发中使用session同步和token机制来防止并发重复提交
- Ubuntu jdk安装及环境配置
- spring mvc 上传图片
- java操作Properties属性文件及获取项目部署服务器路径
- springmvc之接收model类
- WIN7 JDK 环境变量配置
- spring 事务 Transactional
- Eclipse luna启动后立马就卡死的一种原因及解决办法(HotSpot虚拟机上)
- 简单配置Spring MVC里面的UEditor
- Java中的SSH框架之spring(1)
- JAVA CPU 100 分析
- SpringMVC入门实例及详细讲解 (一)
- java-拆包和装包简单分析
- java高并发框架 SSM框架 Spring+SpringMVC+MyBatis
- Java类加载原理解析
- 用自己的keystore替换eclipse默认签名文件
- 内省(introspector)和javabean
- Java泛型通配符extends与super
- eclipse自动补全代码以及jadoc文档注释
- Java类当中初始化模块