SPRING动态数据源使用方法

情况:

 项目过程中遇到这样一个需求,系统启动后动态设置数据源,不同用户登录系统后访问的数据库不同。

我们的系统有很多版本,不同版本开发在不同的数据库上,但是系统需要的一些配置依赖于数据库,所以需要有一个能配置数据源的后台,让开发人员自己处理。

 

解决方案:

 

在动态设置数据源方面,可以通过两种方式实现:

1.在action(项目使用struts)中进行设置,可以确保在每个servlet线程中数据源是一致的。

2.以aop方式,对service方法进行拦截,根据需求设置不同数据源。

数据库datasource,需要继承AbstractRoutingDataSource ,在spring包里面。

Java代码   收藏代码
  1. /** 
  2.  * <p> 
  3.  * Title: 动态获取数据源 
  4.  * </p> 
  5.  *  
  6.  * <p> 
  7.  * Description:  
  8.  * </p> 
  9.  *  
  10.  * <p> 
  11.  * Copyright: 融博技术有限公司 2010 
  12.  * </p> 
  13.  *  
  14.  * @author 袁泉锋HO174959 
  15.  * @version 1.0 
  16.  * @date Jul 23, 2010 
  17.  * 
  18.  */  
  19. public class CustomerRoutingDataSource extends AbstractRoutingDataSource {  
  20.   
  21.  /** 
  22.   * <Description>寻找当前线程数据库Key值 
  23.   * 
  24.   * @since Jul 23, 2010 
  25.   * @return <Description> 
  26.   * 
  27.   */  
  28.  @Override  
  29.  protected Object determineCurrentLookupKey() {  
  30.   return CustomerContextHolder.getDataBase();     
  31.  }  
  32. }  

 

AbstractRoutingDataSource 做了简单处理,我是反编译的,然后将属性targetDataSources修改为了map结构,key为数据源的ID,value是正常的datasource,看下面的spring配置文件:

Java代码   收藏代码
  1. package com.rb.util;  
  2.   
  3.   
  4.   
  5. import java.sql.Connection;  
  6. import java.sql.SQLException;  
  7. import java.util.*;  
  8. import javax.sql.DataSource;  
  9. import org.springframework.beans.factory.InitializingBean;  
  10. import org.springframework.jdbc.datasource.AbstractDataSource;  
  11. import org.springframework.jdbc.datasource.lookup.DataSourceLookup;  
  12. import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;  
  13. import org.springframework.util.Assert;  
  14.   
  15.   
  16. public abstract class AbstractRoutingDataSource extends AbstractDataSource  
  17.         implements InitializingBean {  
  18.   
  19.     public AbstractRoutingDataSource() {  
  20.         dataSourceLookup = new JndiDataSourceLookup();  
  21.     }  
  22.   
  23.     public void setTargetDataSources(Map targetDataSources) {  
  24.         this.targetDataSources = targetDataSources;  
  25.     }  
  26.     public Map getTargetDataSources() {  
  27.         return this.targetDataSources;  
  28.     }  
  29.     public void setDefaultTargetDataSource(Object defaultTargetDataSource) {  
  30.         this.defaultTargetDataSource = defaultTargetDataSource;  
  31.     }  
  32.   
  33.     public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {  
  34.         this.dataSourceLookup = ((DataSourceLookup) (dataSourceLookup == null ? ((DataSourceLookup) (new JndiDataSourceLookup()))  
  35.                 : dataSourceLookup));  
  36.     }  
  37.   
  38.     public void afterPropertiesSet() {  
  39.         if (targetDataSources == null)  
  40.             throw new IllegalArgumentException("targetDataSources is required");  
  41.         resolvedDataSources = new HashMap(targetDataSources.size());  
  42.         Object lookupKey;  
  43.         DataSource dataSource;  
  44.         for (Iterator it = targetDataSources.entrySet().iterator(); it  
  45.                 .hasNext(); resolvedDataSources.put(lookupKey, dataSource)) {  
  46.             java.util.Map.Entry entry = (java.util.Map.Entry) it.next();  
  47.             lookupKey = resolveSpecifiedLookupKey(entry.getKey());  
  48.             dataSource = resolveSpecifiedDataSource(entry.getValue());  
  49.         }  
  50.   
  51.         if (defaultTargetDataSource != null)  
  52.             resolvedDefaultDataSource = resolveSpecifiedDataSource(defaultTargetDataSource);  
  53.     }  
  54.   
  55.     protected DataSource resolveSpecifiedDataSource(Object dataSource)  
  56.             throws IllegalArgumentException {  
  57.         if (dataSource instanceof DataSource)  
  58.             return (DataSource) dataSource;  
  59.         if (dataSource instanceof String)  
  60.             return dataSourceLookup.getDataSource((String) dataSource);  
  61.         else  
  62.             throw new IllegalArgumentException(  
  63.                     "Illegal data source value - only [javax.sql.DataSource] and String supported: "  
  64.                             + dataSource);  
  65.     }  
  66.   
  67.     public Connection getConnection() throws SQLException {  
  68.         return determineTargetDataSource().getConnection();  
  69.     }  
  70.   
  71.     public Connection getConnection(String username, String password)  
  72.             throws SQLException {  
  73.         return determineTargetDataSource().getConnection(username, password);  
  74.     }  
  75.   
  76.     protected DataSource determineTargetDataSource() {  
  77.         Assert  
  78.                 .notNull(resolvedDataSources,  
  79.                         "DataSource router not initialized");  
  80.         Object lookupKey = determineCurrentLookupKey();  
  81.         DataSource dataSource = (DataSource) resolvedDataSources.get(lookupKey);  
  82.         if (dataSource == null)  
  83.             dataSource = resolvedDefaultDataSource;  
  84.         if (dataSource == null)  
  85.             throw new IllegalStateException(  
  86.                     "Cannot determine target DataSource for lookup key ["  
  87.                             + lookupKey + "]");  
  88.         else  
  89.             return dataSource;  
  90.     }  
  91.   
  92.     protected Object resolveSpecifiedLookupKey(Object lookupKey) {  
  93.         return lookupKey;  
  94.     }  
  95.   
  96.     protected abstract Object determineCurrentLookupKey();  
  97.   
  98.     private Map targetDataSources;  
  99.   
  100.     private Object defaultTargetDataSource;  
  101.   
  102.     private DataSourceLookup dataSourceLookup;  
  103.   
  104.     private Map resolvedDataSources;  
  105.   
  106.     private DataSource resolvedDefaultDataSource;  
  107. }  
  108.   
  109.   
  110. /* 
  111.     DECOMPILATION REPORT 
  112.  
  113.     Decompiled from: E:\myeclipse6.0_project\ToolServerFlex\WebRoot\WEB-INF\lib\spring.jar 
  114.     Total time: 218 ms 
  115.     Jad reported messages/errors: 
  116. The class file version is 48.0 (only 45.3, 46.0 and 47.0 are supported) 
  117.     Exit status: 0 
  118.     Caught exceptions: 
  119. */  

 

 

CustomerContextHolder如下:

Java代码   收藏代码
  1. /** 
  2.  * <p> 
  3.  * Title: 动态获取数据源 
  4.  * </p> 
  5.  *  
  6.  * <p> 
  7.  * Description:  
  8.  * </p> 
  9.  *  
  10.  * <p> 
  11.  * Copyright: 融博技术有限公司 2010 
  12.  * </p> 
  13.  *  
  14.  * @author 袁泉锋HO174959 
  15.  * @version 1.0 
  16.  * @date Jul 23, 2010 
  17.  * 
  18.  */  
  19. public class CustomerContextHolder {  
  20.     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
  21.   
  22.     public   static void setDataBase(String dataBase) {  
  23.         contextHolder.set(dataBase);  
  24.     }  
  25.   
  26.     public static String getDataBase() {  
  27.         return (String) contextHolder.get();  
  28.     }  
  29.   
  30.     public static void clearDataBase() {  
  31.         contextHolder.remove();  
  32.     }  
  33.   
  34. }  

 开发人员请求过来的时候,调用方法setDataBase,将数据库配置key放入线程变量中。

 

自己定义一个数据库事务管理器:

Java代码   收藏代码
  1. package com.rb.util;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.SQLException;  
  5.   
  6. import javax.sql.DataSource;  
  7.   
  8. public class TransactionManager {  
  9.     //private Connection conn;  
  10.     private DataSource dataSource;  
  11.     public static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();  
  12.       
  13.     public DataSource getDataSource() {  
  14.         return dataSource;  
  15.     }  
  16.     public void setDataSource(DataSource dataSource) {  
  17.         this.dataSource = dataSource;  
  18.     }  
  19.     public TransactionManager() {  
  20.     }  
  21.       
  22. //  public synchronized Connection  getConnection(){  
  23. //      Connection conn = tl.get();  
  24. //      if(conn==null){  
  25. //          try {  
  26. //              conn = dataSource.getConnection();  
  27. //          } catch (SQLException e) {  
  28. //              e.printStackTrace();  
  29. //          }  
  30. //          tl.set(conn);  
  31. //      }  
  32. //      return conn;  
  33. //  }  
  34.     /** 开启事务 */  
  35.     public void beginTransaction() throws SQLException{  
  36.         try {  
  37.             Connection conn = tl.get();  
  38.             if(conn==null){  
  39.                 conn = dataSource.getConnection();  
  40.                 tl.set(conn);  
  41.             }  
  42.             conn.setAutoCommit(false); //把事务提交方式改为手工提交  
  43.         } catch (SQLException e) {  
  44.             throw new SQLException("开户事务时出现异常");  
  45.         }  
  46.     }  
  47.     /** 提交事务并关闭连接 */  
  48.     public void commitAndClose() throws SQLException{  
  49.         Connection conn  = null;  
  50.         try {  
  51.             conn = tl.get();  
  52.             conn.commit(); //提交事务  
  53.         } catch (SQLException e) {  
  54.             throw new SQLException("提交事务时出现异常");  
  55.         }finally{  
  56.             if(conn!=null){  
  57.                 conn.close();  
  58.             }  
  59.             tl.remove(); //卸装线程绑定  
  60.         }  
  61.     }  
  62.     /** 回滚并关闭连接 */  
  63.     public void rollbackAndClose()throws SQLException{  
  64.         Connection conn  = null;  
  65.         try {  
  66.             conn = tl.get();  
  67.             conn.rollback();  
  68.         } catch (SQLException e) {  
  69.             throw new SQLException("回滚事务时出现异常");  
  70.         } finally{  
  71.             if(conn!=null){  
  72.                 conn.close();  
  73.             }  
  74.             tl.remove(); //卸装线程绑定  
  75.         }  
  76.     }  
  77. }  

 调用完setDataBase方法之后,调用beginTransaction方法,第一次取的时候,connection是null,然后去datasource取连接,这个时候的datasource里面有很多数据源的连接,因为是一个map结构。当调用conn = dataSource.getConnection();的时候,会调用AbstractRoutingDataSource类的

方法:

Java代码   收藏代码
  1. public Connection getConnection() throws SQLException {  
  2.   return determineTargetDataSource().getConnection();  
  3.  }  

 

然后是:

Java代码   收藏代码
  1. protected DataSource determineTargetDataSource() {  
  2.         Assert  
  3.                 .notNull(resolvedDataSources,  
  4.                         "DataSource router not initialized");  
  5.         Object lookupKey = determineCurrentLookupKey();  
  6.         DataSource dataSource = (DataSource) resolvedDataSources.get(lookupKey);  
  7.         if (dataSource == null)  
  8.             dataSource = resolvedDefaultDataSource;  
  9.         if (dataSource == null)  
  10.             throw new IllegalStateException(  
  11.                     "Cannot determine target DataSource for lookup key ["  
  12.                             + lookupKey + "]");  
  13.         else  
  14.             return dataSource;  
  15.     }  

 里面会用到方法:

Java代码   收藏代码
  1. Object lookupKey = determineCurrentLookupKey();  

 但是我们的子类重写了他,是去线程变量里面取刚才我们setDataBase进去的,所以只会取到一个datasource。

这样就能取到自己选择的数据库连接了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值