Spring动态数据源创建以及切换方案
来源:互联网 发布:uc手机淘宝 编辑:程序博客网 时间:2024/04/28 02:59
背景
采取SSH框架,数据库是Mysql,实现了动态创建和动态切换数据源的功能
实现思路
用数据表[datasource]管理数据源信息,从页面上选定要使用的数据源后,先从缓存里去查找该数据源,
如果该数据源不存在的时候,从数据表里去取得该数据源,并将该数据源添加到缓存里。
具体实现
CREATE TABLE `datasource` ( `dsId` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '数据源ID 自增长', `dsName` varchar(20) NOT NULL COMMENT '数据源名 ', `username` varchar(20) NOT NULL COMMENT '用户名 ', `password` varchar(20) NOT NULL COMMENT '密码 ', `url` varchar(120) NOT NULL COMMENT 'URL ', `driverClassName` varchar(60) NOT NULL COMMENT '驱动类名 ', `remark` varchar(200) NOT NULL COMMENT '备注 ', PRIMARY KEY (`dsId`) ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8
2.主要Java类
CustomerContextHolder.java 用ThreadLocal实现了对数据源创建的线程管理
MultipleDataSource.java 实现数据源的动态创建和切换【CustomerContextHolder.java】
public class CustomerContextHolder { /** * 系统默认数据源 */ public final static String DATA_SOURCE_SYSTEM = "systemDataSource"; private 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(); } }
【MultipleDataSource.java】
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.apache.commons.dbcp.BasicDataSource; import org.apache.log4j.Logger; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * <b>动态切换数据源</b><br> * 用法:在页面上选定要使用的数据源,CustomerContextHolder.setCustomerType(选定的数据源ID)<br> * 主要思想:重写AbstractRoutingDataSource类的determineCurrentLookupKey()方法<br> * 当页面上选定的数据源在缓存的目标数据源集合里不存在时,从DB里查找数据源信息,并将该数据源添加到缓存 * @author fanlikuo * */ public class MultipleDataSource extends AbstractRoutingDataSource { /** log4j */ private Logger log = Logger.getLogger(this.getClass()); /** 目标数据源集合*/ private Map<Object, Object> _targetDataSources; /** * 获取与数据源相关的key * 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值 * 在通过determineTargetDataSource获取目标数据源时使用 */ @Override protected Object determineCurrentLookupKey() { // 获取当前线程的数据源路由的key String dataSourceName = CustomerContextHolder.getCustomerType(); // 当key为空时,key设为默认数据源:systemDataSource // 当key不为空时,从缓存的目标数据源里查找数据源 if (dataSourceName == null) { dataSourceName = CustomerContextHolder.DATA_SOURCE_SYSTEM; } else { this.selectDataSource(Integer.valueOf(dataSourceName)); if (dataSourceName.equals("0")) dataSourceName = CustomerContextHolder.DATA_SOURCE_SYSTEM; } log.debug("--------> use datasource= " + dataSourceName); return dataSourceName; } /** * 从缓存的目标数据源查找指定数据源ID对应的数据源<br> * 不存在时创建新的数据源链接,并将新数据链接添加至缓存 * @param dsId 数据源ID */ public void selectDataSource(Integer dsId) { // 获取当前线程的数据源路由的key Object sid = CustomerContextHolder.getCustomerType(); // 当数据源ID为"0"的时候,当前线程的数据源路由的key设为"0",本处理结束 if ("0".equals(dsId + "")) { CustomerContextHolder.setCustomerType("0"); return; } // 从缓存的目标数据源MAP里查找数据源ID对应的数据源 Object obj = this._targetDataSources.get(dsId); // 当缓存里该数据源存在时,本处理结束 if (obj != null && sid.equals(dsId + "")) { return; // 当缓存里该数据源不存在时,从DB的数据源表里查询对应的数据源信息, // 并将查找到的数据源信息添加到缓存 } else { // 从DB里获取数据源信息 BasicDataSource dataSource = this.getDataSource(dsId); // 将查询到的数据源添加至缓存 if (null != dataSource) this.setDataSource(dsId, dataSource); } } /** * 查询dsId对应的数据源记录 * @param dsId 数据源ID * @return BasicDataSource 数据源对象 */ public BasicDataSource getDataSource(Integer dsId) { // 选择默认的数据源 this.selectDataSource(0); // 获取与数据源相关的key this.determineCurrentLookupKey(); Connection conn = null; HashMap<String, Object> map = null; try { // 创建连接 conn = this.getConnection(); // 查询用SQL语句 PreparedStatement ps = conn .prepareStatement("SELECT * FROM datasource WHERE dsId = ?"); ps.setInt(1, dsId); ResultSet rs = ps.executeQuery(); // 结果集编辑 if (rs.next()) { map = new HashMap<String, Object>(); log.debug("-------->getDataSource id=" +rs.getInt("dsId")); // 数据源ID map.put("DBS_ID", rs.getInt("dsId")); // 驱动类名 map.put("DBS_DriverClassName", rs .getString("driverClassName")); // URL map.put("DBS_URL", rs.getString("url")); // 用户名 map.put("DBS_UserName", rs.getString("username")); // 密码 map.put("DBS_Password", rs.getString("password")); } // 关闭结果集 rs.close(); // 关闭PreparedStatement ps.close(); } catch (SQLException e) { log.error(e); } finally { try { // 关闭连接 if(conn != null) { conn.close(); } } catch (SQLException e) { log.error(e); } } // 数据源存在时 if (null != map) { String driverClassName = map.get("DBS_DriverClassName").toString(); String url = map.get("DBS_URL").toString(); String userName = map.get("DBS_UserName").toString(); String password = map.get("DBS_Password").toString(); log.debug("-------->getDataSource url=" +url); // 创建BasicDataSource对象 BasicDataSource dataSource = this.createDataSource(driverClassName, url, userName, password); return dataSource; } return null; } /** * 创建BasicDataSource对象 * * @param driverClassName 驱动类名 * @param url URL * @param username 用户名 * @param password 密码 * @return BasicDataSource对象 */ public 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); return dataSource; } /** * 把新建的数据源设为当前使用的目标数据源,并将该数据源添加到缓存 * @param dsId 数据源ID * @param dataSource 数据源对象 */ public void setDataSource(Integer dsId, BasicDataSource dataSource) { // 添加目标数据源 this.addTargetDataSource(dsId + "", dataSource); // 设置当前线程的数据源路由的key CustomerContextHolder.setCustomerType(dsId + ""); } /** * 添加目标数据源 * @param key 数据源路由的key * @param dataSource 数据源对象 */ public void addTargetDataSource(String key, BasicDataSource dataSource) { // 添加目标数据源 this._targetDataSources.put(key, dataSource); // 设置目标数据源 this.setTargetDataSources(this._targetDataSources); } /** * 设置父类的目标数据源 * @param targetDataSources 目标数据源 */ public void setTargetDataSources(Map targetDataSources) { this._targetDataSources = targetDataSources; // 设置父类的目标数据源 super.setTargetDataSources(this._targetDataSources); // 调用父类的afterPropertiesSet方法初始化bean super.afterPropertiesSet(); } }
3.XML配置
init.properties 数据源配置项目的property文件里,配置数据源的各项信息
applicationContext.xml spring应用上下文配置文件里需要配置一个默认的数据源【 init.properties】
#=======SYSTEM DATASOURCE=======================================================================
datasource.type=mysql
datasource.driverClassName=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://192.168.8.53:3306/querydb?useUnicode=true&characterEncoding=utf-8
datasource.username=root
datasource.password=root
#===============================================================================================
【applicationContext.xml 】
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"> <value>classpath:init.properties</value> </property> </bean> <context:component-scan base-package="com.tiangong"></context:component-scan> <bean id="dataSource_system" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${datasource.driverClassName}"></property> <property name="url" value="${datasource.url}"></property> <property name="username" value="${datasource.username}"></property> <property name="password" value="${datasource.password}"></property> </bean> <bean id="multipleDataSource" class="com.tiangong.comm.MultipleDataSource"> <property name="defaultTargetDataSource" ref="dataSource_system"/> <property name="targetDataSources"> <map> </map> </property> </bean> …… </beans>
4.测试 【DataSourceAction】类摘录
/** * 测试数据源是否可用 */ public void testDataSource() { String pDriver = this.getHttpServletRequest().getParameter("pDriver"); String pUrl = this.getHttpServletRequest().getParameter("pUrl"); String pUserName = this.getHttpServletRequest().getParameter("pUserName"); String pPassword = this.getHttpServletRequest().getParameter("pPassword"); BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(pDriver); dataSource.setUrl(pUrl); dataSource.setUsername(pUserName); dataSource.setPassword(pPassword); dataSource.setTestWhileIdle(true); Connection conn = null; String msg = null; try { conn = dataSource.getConnection(); } catch (SQLException e) { msg = e.getMessage(); e.printStackTrace(); } finally { // 关闭连接 if(conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } try { if(msg != null) { this.getHttpServletResponse().getWriter().write(msg); } } catch (IOException e) { e.printStackTrace(); } } /** * 数据源选定 TODO */ public void selDataSource() { String id = this.getHttpServletRequest().getParameter("dsId"); System.out.println("从前台获得的数据源ID="+id);//TODO CustomerContextHolder.setCustomerType(id); }
0 0
- Spring动态数据源创建以及切换方案
- Spring动态创建bean切换数据源
- Spring动态切换数据源
- spring 动态数据源切换
- spring动态切换数据源
- spring动态切换数据源
- Spring动态切换数据源
- spring 动态切换数据源
- spring动态创建切换数据源动态之原理
- Spring+Hibernate动态切换数据源以及失败解决方案
- 动态切换数据源(spring+hibernate)
- 数据源动态切换(Spring+Hibernate)
- 动态切换数据源(spring+hibernate)
- 动态切换数据源(spring+hibernate)
- spring 动态数据源切换实例
- 动态切换数据源(spring+hibernate)
- 数据源动态切换(Spring+Hibernate)
- 动态切换数据源(spring+hibernate)
- Unity3D中的Coroutine详解
- android 音乐播放工具类MediaPlayer
- MFC 添加窗口背景
- TextView部分文字变色、可点击。(微博话题 变色、点击)
- 笔记4(判断语句、循环语句、list、dict)
- Spring动态数据源创建以及切换方案
- 硬件加速引起的问题
- SQL的锁
- 商品详情页系统的Servlet3异步化实践
- erlang md5 生成
- MySQL 的 RowNum 实现
- 京东商品详情页碎碎念
- PCB行业ERP解决方案
- Servlet 3.0 之@WebFilter怎么控制多个filter的执行顺序