Spring动态数据源创建以及切换方案

背景

      采取SSH框架,数据库是Mysql,实现了动态创建和动态切换数据源的功能

实现思路

   用数据表[datasource]管理数据源信息,从页面上选定要使用的数据源后,先从缓存里去查找该数据源,

   如果该数据源不存在的时候,从数据表里去取得该数据源,并将该数据源添加到缓存里。

具体实现

1.数据表结构                            
    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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值