标签:
1、核心思想,spring提供了一个DataSource的子类,该类支持多个数据源
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
该类的源码如下:
org.springframework.jdbc.datasource.lookup;
java.sql.Connection;
java.sql.SQLException;
java.util.HashMap;
java.util.Iterator;
java.util.Map;
java.util.Map.Entry;
javax.sql.DataSource;
org.springframework.beans.factory.InitializingBean;
org.springframework.jdbc.datasource.AbstractDataSource;
org.springframework.jdbc.datasource.lookup.DataSourceLookup;
org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
org.springframework.util.Assert;
AbstractRoutingDataSource AbstractDataSource InitializingBean {
// 注意这里,这里使用Map存放多个数据源
Map<Object, Object> targetDataSources;
... // 篇幅有限,省略其他代码,有兴趣的同学可以自行研究,还是支持蛮多功能的,比如JNDI等数据源方式
// 注意这个暴露的方法,spring在从数据源中获取数据库连接时,通过这个方法确定数据源
DataSource determineTargetDataSource() {
Assert.notNull(.resolvedDataSources, );
Object lookupKey = .determineCurrentLookupKey();
DataSource dataSource = (DataSource).resolvedDataSources.get(lookupKey);
(dataSource == && (.lenientFallback || lookupKey == )) {
dataSource = .resolvedDefaultDataSource;
}
(dataSource == ) {
IllegalStateException(+ lookupKey + );
} {
dataSource;
}
}
// 对外暴露的决定数据源的方法,通过外部设置数据源的key值,
// spring自行从已存储的数据源中查找指定数据源
Object determineCurrentLookupKey();
}
通过分析源码,可以知道我们只需要实现如何动态设置切换数据源的方式即可,可以考虑使用注解的方式在指定的位置添加数据源注解,利用AOP动态指定数据源。
自定义的数据源如下:
com.yao.yz.yaowangdrug.dataSource;
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
DynamicDataSource AbstractRoutingDataSource{
Object determineCurrentLookupKey() {
DataSourceHolder.();
}
}
2、自定义注解,本章节不做过多解释,对于注解有兴趣的同学请自行研究,废话一句:程序员以实际应用为主,研究行的内容请自行学习
com.yao.yz.yaowangdrug.dataSource;
java.lang.annotation.*;
(RetentionPolicy.)
(ElementType.)
@{
String value();
}
3、定义一个数据源切换功能组件,其中数据源存放的方式采用ThreadLocal的形式,我是单独把存放做成一个额外的组件,这个是可以自行决定的,代码如下:
com.yao.yz.yaowangdrug.dataSource;
DataSourceHolder {
ThreadLocal<String> = InheritableThreadLocal<String>();
setDataSourceKey(String dataSource) {
.set(dataSource);
}
Object getDataSourceKey() {
.get();
}
}
com.yao.yz.yaowangdrug.dataSource;
org.apache.log4j.Logger;
org.aspectj.lang.JoinPoint;
org.aspectj.lang.reflect.MethodSignature;
java.lang.reflect.Method;
DynamicAspect {
Logger = Logger.(DynamicAspect.);
switchDataSource(JoinPoint point) NoSuchMethodException
{
Object target = point.getTarget();
String method = point.getSignature().getName();
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
Method m = classz[].getMethod(method, parameterTypes);
(m != && m.isAnnotationPresent(.)) {
data = m
.getAnnotation(.);
DataSourceHolder.(data.value());
}
.info(+ DataSourceHolder.());
}
}
4、在spring配置文件添加多个数据源,并将多个数据源统一纳入自定义数据源的管理,具体的配置信息如下:
<!--装载配置文件,将数据源的配置做成配置文件,方便管理,有兴趣的同学自行参考-->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="Config.properties"/>
</bean>
<!--数据源1-->
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="${dataSource1.jdbc.url}" />
<property name="user" value="${dataSource1.jdbc.username}" />
<property name="password" value="${dataSource1.jdbc.password}" />
<property name="minPoolSize" value="${dataSource1.jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${dataSource1.jdbc.maxPoolSize}" />
</bean>
<!--数据源2-->
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="${dataSource2.jdbc.url}" />
<property name="user" value="${dataSource2.jdbc.username}" />
<property name="password" value="${dataSource2.jdbc.password}" />
<property name="minPoolSize" value="${dataSource2.jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${dataSource2.jdbc.maxPoolSize}" />
</bean>
<!--自定义数据源,将所有的数据源纳入自定义数据源管理-->
<bean id="dataSource" class="com.yao.yz.yaowangdrug.dataSource.DynamicDataSource">
<property name="targetDataSources">
<map>
<!-- 对应spring提供的AbstractRoutingDataSource的Map -->
<entry key="dataSource1" value-ref="dataSource1"/>
<entry key="dataSource2" value-ref="dataSource2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource1"/>
</bean>
5、配置数据源切换的AOP,代码如下:
<!--动态决定数据源-->
<bean id="dataSourceSwitchAspect" class="com.yao.yz.yaowangdrug.dataSource.DynamicAspect"/>
<aop:config>
<aop:aspect id="dynamicAspect" ref="dataSourceSwitchAspect">
<!--数据源切换可以控制为Dao或者service层,请根据实际业务需要自行决定-->
<aop:pointcut id="dynamicPointCut" expression="execution(* com.xx.xx.xx.dao.*.*(..))"/>
<aop:before method="switchDataSource" pointcut-ref="dynamicPointCut"/>
</aop:aspect>
</aop:config>
6、使用spring整合mybatis,和一般的整合却别在于使用的数据源为自定义数据源,代码如下:
classpath:ModelMapper.xml classpath:ModelMapper1.xml classpath:ModelMapper2.xml
注意:目前精力有限,此处为什么可以使用自定义多个数据源的底层原理还没来得及看,有兴趣的同学请自行研究,如果好心的话可以简单的告知我,谢谢你了~~~
7、将自定义数据源纳入spring的事务管理器管理,配置代码如下:
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--申明事务回滚的方法和异常信息-->
<tx:advice id="txAdvic" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="do*" read-only="false" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<!--申明事务范围-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.xx.xx.xx.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvic" pointcut-ref="txPointCut"/>
</aop:config>
Model类:
com.yao.yz.yaowangdrug.model;
DBModel {
String ;
String ;
String getValue() {
;
}
setValue(String value) {
.= value;
}
String getKey() {
;
}
setKey(String key) {
.= key;
}
}
Dao接口:
com.yao.yz.yaowangdrug.dao;
;
com.yao.yz.yaowangdrug.model.DBModel;
DBDao {
()
insert(DBModel dbModel) Exception;
}
Service接口:
com.yao.yz.yaowangdrug.service;
com.yao.yz.yaowangdrug.model.DBModel;
;
DBService {
doInsert() Exception;
doUpdate() Exception;
testSeperateDB() Exception;
}
关于事务的控制,有一下几点说明:
1、采用申明或者注解实现事务控制时时,因为开启了事务控制,所以如果是两个不同的数据源Dao,根据spring的事务传播特性,第二个事务开启将使用已有的事务(即将采用第一个数据源的数据库连接)进行事务操作,所以此时事务控制是失效的(即使切面执行了数据源切换)。结论就是跨数据库的事务是无法通过spring的数据库控制实现的!!!请切记。
2、同一个数据源的事务控制和普通的数据源控制是一致的,没有什么区别。
以上代码都是经过测试通过,可以实现跨库的数据方式,主要的应用场景是mysql的数据库读写分离。如果不正确的地方请告知,谢谢!
标签:
原文地址:http://my.oschina.net/u/1471781/blog/508906