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

来源:互联网 发布:uc手机淘宝 编辑:程序博客网 时间:2024/04/28 02:59

背景

      采取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 0