spring多数据源的配置-以及原理
2015-12-29 17:45
561 查看
spring多数据源的配置
创建一个类 继承 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource重写方法 determineCurrentLookupKey
package com.sky.lp.util; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class ExtendsAbstractRoutingDataSource extends AbstractRoutingDataSource{ //使用ThreadLocal 保证线程安全 private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>(); public static void setDataSourceKey(String dataSource) { dataSourceKey.set(dataSource); } @Override protected Object determineCurrentLookupKey() { return dataSourceKey.get(); } }
数据源配置
<!-- 定义数据源 使用 druid 包 --> <bean id ="dataSource1" class= "com.alibaba.druid.pool.DruidDataSource" init-method= "init" destroy-method ="close"> <property name ="name" value="druidOne" /> <property name ="url" value= "jdbc:mysql://localhost:3306/littledemo" /> <property name ="username" value="root" /> <property name ="password" value= "laixu785^@#"></property > <property name ="driverClassName" value= "com.mysql.jdbc.Driver"></property > <property name ="initialSize" value="2" /> <property name ="maxActive" value="10" /> <property name ="minIdle" value="5" /> <property name ="validationQuery" value= "SELECT COUNT(*) FROM DUAL" /> <property name ="testWhileIdle" value="true" /> <property name ="timeBetweenEvictionRunsMillis" value= "5000" /> </bean > <!-- 定义数据源 使用 druid 包 --> <bean id ="dataSource2" class= "com.alibaba.druid.pool.DruidDataSource" init-method= "init" destroy-method ="close"> <property name ="name" value="druidTwo" /> <property name ="url" value= "jdbc:mysql://localhost:3306/doubleo" /> <property name ="username" value="root" /> <property name ="password" value= "laixu785^@#"></property > <property name ="driverClassName" value= "com.mysql.jdbc.Driver"></property > <property name ="initialSize" value="2" /> <property name ="maxActive" value="10" /> <property name ="minIdle" value="5" /> <property name ="validationQuery" value= "SELECT COUNT(*) FROM DUAL" /> <property name ="testWhileIdle" value="true" /> <property name ="timeBetweenEvictionRunsMillis" value= "5000" /> </bean > <!-- 多数据源 --> <bean id ="extendsAbstractRoutingDataSource" class= "com.sky.lp.util.ExtendsAbstractRoutingDataSource" > <!-- 默认数据源 --> <property name ="defaultTargetDataSource" ref= "dataSource2"/> <!-- 不用的数据源对应不用的key值 --> <property name ="targetDataSources"> <map > <entry key ="dataSource1" value-ref= "dataSource1"/> <entry key ="dataSource2" value-ref= "dataSource2"/> </map > </property > </bean > spring jdbcTemplate 数据源的使用, <bean id= "jdbcTemplate" class= "org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref= "extendsAbstractRoutingDataSource" /> </bean >
注意:
spring集成 ibatis 或者是 hibernate 或者其他的 ORM 框架,使用的数据源都是,extendsAbstractRoutingDataSource。
测试代码
public static void main(String[] args) { ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-servlet.xml"); ExtendsAbstractRoutingDataSource myDataSource = appCtx.getBean("extendsAbstractRoutingDataSource", ExtendsAbstractRoutingDataSource.class); myDataSource.setDataSourceKey("dataSource1"); JdbcTemplate jdbc = appCtx.getBean("jdbcTemplate", JdbcTemplate.class); List<?> list = jdbc.queryForList("select * from user"); System.out.println(list); }
spring多数据源工作原理(即多个数据源到底应该选择哪一下)
原理分析(这里以 spring 的 JdbcTemplate 为例子)JdbcTemplate 中执行sql语句,是从 dataSource 中获取 数据库 Connection,通过Connection 执行sql 语句。
那么Connection是怎么获取到的呢,是通过
org.springframework.util.Assert.DataSourceUtils.getConnection(getDataSource()) 获取Connection的。
方法 getDataSource() 源码
public DataSource getDataSource() { return dataSource; }
返回的dataSource,即配置 jdbcTemplate bean 的属性dataSource
<property name ="dataSource" ref= "extendsAbstractRoutingDataSource" />
看一下org.springframework.util.Assert.DataSourceUtils.getConnection(getDataSource()) 的源码
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException { try { return doGetConnection(dataSource); } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); } } public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified"); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) conHolder.requested(); if (!conHolder.hasConnection()) logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); return conHolder.getConnection(); logger.debug("Fetching JDBC Connection from DataSource"); Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) logger.debug("Registering transaction synchronization for JDBC Connection"); ConnectionHolder holderToUse = conHolder; if (holderToUse == null) holderToUse = new ConnectionHolder(con); else holderToUse.setConnection(con); holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction( true); if (holderToUse != conHolder) TransactionSynchronizationManager.bindResource(dataSource, holderToUse); return con; }
解读上面的代码,最终的目的就是要从dataSource中获取数据库的Connection。而获取数据库的Connection必须调用dataSource的getConnection。
(即 javax.sql.DataSource的接口规定 getConnection() 方法 )。
所以,可以在从dataSource获取Connection的过程中做手脚。这就是
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 做的工作。
AbstractRoutingDataSource 继承 org.springframework.jdbc.datasource.AbstractDataSource,AbstractDataSource 实现 javax.sql.DataSource 接口。
下面看一下 AbstractRoutingDataSource 的方法 getConnection 的源码
public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } protected DataSource determineTargetDataSource() { Assert.notNull(resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = (DataSource)resolvedDataSources.get(lookupKey); if(dataSource == null && (lenientFallback || lookupKey == null)) dataSource = resolvedDefaultDataSource; if(dataSource == null) throw new IllegalStateException((new StringBuilder()).append("Cannot determine target DataSource for lookup key [").append(lookupKey).append("]").toString()); return dataSource; }
注意 源码中的方法 determineCurrentLookupKey(),我们自己定义的类 ExtendsAbstractRoutingDataSource 重写了 AbstractRoutingDataSource 中的 determineCurrentLookupKey 方法。
到这里大家应该明白了,spring的动态数据库切换是怎么实现的了。至于其他的地方,大家有不明白的就看看源码。
相关文章推荐
- eclipse创建父子工程
- java中ArrayList使用remove注意事项
- 25个Java机器学习工具&库
- Java API —— IO流小结
- Spring Web MVC
- Java中getResourceAsStream的
- MVC框架详解--Servlet+JSP+JavaBean模式(MVC)开发复杂的web应用
- 关于struts页面数字的格式化
- Java基本排序算法
- 如何修改maven默认JDK版本
- eclipse修改项目名称或copy
- springMVC 整合测试 freemarker
- JAVA序列化的总结
- Castor实现XML与Java的互转
- Spring Boot学习笔记-SQL数据库使用
- java中Integer比较需要注意的问题
- 开始使用Java Lambda Expressions
- (转)eclipse jee配置jetty的两种方法
- Java套接字编程
- Java并发同步器--Exchanger