1.配置多个数据源
<bean id="dataSource1" class="org.logicalcobwebs.proxool.ProxoolDataSource" >
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="driverUrl" value="jdbc:oracle:thin:@xx.xx.xx.xx:1521:ORCL" />
<property name="user" value="xx" />
<property name="password" value="xx" />
<!--数据源的别名 -->
<property name="alias" value="dataSource1" />
<!-- 空闲连接个数 默认为0 -->
<property name="prototypeCount" value="4" />
<!--最小连接数(默认5个) -->
<property name="minimumConnectionCount" value="1" />
<!--最大连接数(默认15个),超过了这个连接数,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定 -->
<property name="maximumConnectionCount" value="30" />
<!-- 如果侦察线程发现闲置连接,则会使用这个SQL语句来对这些连接进行检查 -->
<property name="houseKeepingTestSql" value="select CURRENT_DATE" />
</bean>
<bean id="dataSource2" class="org.logicalcobwebs.proxool.ProxoolDataSource" >
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="driverUrl" value="jdbc:oracle:thin:@xx.xx.xx.xx:1521:ORCL" />
<property name="user" value="xx" />
<property name="password" value="xx" />
<!--数据源的别名 -->
<property name="alias" value="dataSource2" />
<!-- 空闲连接个数 默认为0 -->
<property name="prototypeCount" value="4" />
<!--最小连接数(默认5个) -->
<property name="minimumConnectionCount" value="1" />
<!--最大连接数(默认15个),超过了这个连接数,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定 -->
<property name="maximumConnectionCount" value="30" />
<!-- 如果侦察线程发现闲置连接,则会使用这个SQL语句来对这些连接进行检查 -->
<property name="houseKeepingTestSql" value="select CURRENT_DATE" />
</bean>
2.扩展Spring的AbstractRoutingDataSource抽象类,实现动态数据源。
AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心.这里
对该方法进行Override。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getCustomerType();
}
}
上下文DbContextHolder为一线程安全的ThreadLocal,具体代码如下:
public class DBContextHolder {
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();
}
}
3.配置动态数据源
将DynamicDataSourceBean加入到Spring的上下文xml配置文件中去,同时配置DynamicDataSource的targetDataSources(多数据源目标)属性的Map映射。
<!-- ========配置动态数据源 ======== -->
<bean id="dynamicDataSource" class="com.incon.framework.dataSource.DynamicDataSource" >
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSource1" key="dataSource1"></entry>
<entry value-ref="dataSource2" key="dataSource12"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource1"></property><!-- 默认使用dataSource的数据源 -->
</bean>
4.使用动态数据源
例子中DynamicDataSource是继承与AbstractRoutingDataSource,而AbstractRoutingDataSource又是继承于org.springframework.jdbc.datasource.AbstractDataSource,AbstractDataSource实现了统一的DataSource接口,所以DynamicDataSource同样可以当一个DataSource使用。
在ORM框架ibatis中的使用配置示例:
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocations" >
<list>
<value>classpath:config/ibatis/ibatis-config.xml</value>
<!-- <value>classpath:sqlmap/xk/*SqlMap.xml</value> -->
</list>
</property>
<property name="lobHandler">
<ref local="oracleLobHandler" />
</property>
<property name="dataSource" ref="dynamicDataSource"/>
</bean>
5.事务管理
使用动态数据源的时候,可以看出和使用单数据源的时候相比,在使用配置上几乎没有差别,在进行性事务管理配置的时候也没有差别:
使用ibatis时的事务管理配置示例:
<!-- 数据连接事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource" />
</bean>
6.动态数据源的管理控制
如何选择控制每个业务中需要的具体数据源,可是使用手动控制:
public class DynamicDataSourceTest {
public static void main(String[]args){
DynamicDataSourceTest ds = new DynamicDataSourceTest();
ds.loginUser();
}
public void loginUser(){
String[] locationPath={"config/spring/spring-common.xml"
,"config/spring/spring-pool.xml"
,"config/spring/spring-securitycode.xml"};
ApplicationContext context =new ClassPathXmlApplicationContext(locationPath);
DemoService dao = (DemoService) context.getBean("demoService", DemoService.class);
DBContextHolder.setCustomerType(TargetDataSourceEnum.dataSource1);
HashMap mappar = new HashMap();
mappar.put("dm", "dm1");
mappar.put("mc", "mc1");
DemoJsonPage demoJsonPage = new DemoJsonPage();
System.out.println("-------------"+dao.queryNjb().size());
}
}
也可以采用AOP的控制方式:
public enum TargetDataSourceEnum {
dataSource1,dataSource2
}
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 在方法上使用,用于指定使用哪个数据源
*
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
TargetDataSourceEnum name();
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 切换数据源Advice
* @author
*/
@Aspect
@Order(-1)// 保证该AOP在@Transactional之前执行
@Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(ds)")
public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {
String dsId = ds.name().name();
logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());
DBContextHolder.setCustomerType(ds.name().name());
}
@After("@annotation(ds)")
public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
DBContextHolder.clearCustomerType();
}
}
使用方法
@TargetDataSource(name=TargetDataSourceEnum.dataSource1)
@Override
public List queryNjb(){
return dbDao.query("demo.queryNjb");
}