您的位置:首页 > 编程语言 > Java开发

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的动态数据库切换是怎么实现的了。至于其他的地方,大家有不明白的就看看源码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: