动态数据库路由设置
项目中要用到多个数据库的时候,可能就需要设置一下动态数据库路由,根据对应的名称连接对应的连接。
举例:某公司要使用线下票务oracle库,应用Mysql库,停车场系统sql server 库。
当然,读者可以根据这个进行自行命名,然后改成自己需要的也可。
(说明:该代码是一位名为“Liwei”的已离职同事所写,代码拉下来作为研讨,学习使用。切不可用于商业用途。否则,产生一切商业纠纷,均由使用者承担,本人不承担任何法律责任。)
README(创作者说明,粘贴下来,以供参考,也是路由使用方法):
该包里的类主要实现的功能是将多个数据库连接池包装成一个连接池,设置一个默认的连接池,
在方法调用时如果不指定连接数据库时,使用默认数据库连接。
数据库连接设置方式:
@Router("default")
该注解可用户类或者方法上。当该注解使用在方法上表示该方法执行时获取它指定的数据连接,
如果注解在类声明上,表示该类中所有的方法都使用它声明的数据库连接,如果同时方法上也使
用了该注解,那么优先使用方法上的数据库连接声明。
@Router(value = "parking")
该注解就使用sqlserver 的停车场数据查询。
package com.xxx.dsrouting;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 通过SpringAop方式实现拦截。
* @author liwei
*/
public class RouterAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取方法上的数据库路由设置
Method method = invocation.getMethod();
Router router = method.getAnnotation(Router.class);
if (router == null) {
// 如果方法上的数据库路由设置为空,获取类声明上的设置
Class<?> clazz = invocation.getThis().getClass();
router = clazz.getAnnotation(Router.class);
}
// 如果路由设置为空,执行当前方法不做任何处理
if (router == null) {
return invocation.proceed();
}
// 获取调用当前方法的对象数据路由信息
String outsideRouter = RouterHolder.getRouter();
RouterHolder.clearRouter();
RouterHolder.setRouter(router.value());
try {
return invocation.proceed();
} finally {
RouterHolder.clearRouter();
if (outsideRouter != null) {
RouterHolder.setRouter(outsideRouter);
}
}
}
}
这个@Router注解是自己定义的一个交给Spring管理的注解.这样就可以找到对应的数据库。
package com.xxx.dsrouting;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据库连接路由设置注解。
* @author liwei
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface Router {
/**
* 路由名称
* @return
*/
public String value() default "default";
}
package com.xxx.dsrouting;
/**
* 数据库连接池路由信息Holder。
* @author liwei
*/
public class RouterHolder {
private static final ThreadLocal<String> holder = new InheritableThreadLocal<String>();
/**
* 获取当前线程数据库连接路由名称。
* @return 路由名称
*/
public static String getRouter() {
return holder.get();
}
/**
* 设置当前线程数据库连接路由名称。
* @param router 路由名称
*/
public static void setRouter(String router) {
holder.set(router);
}
/**
* 清除当前线程路由名称。
*/
public static void clearRouter() {
holder.remove();
}
}
package com.xxx.dsrouting;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class RouterNameAdvice implements MethodInterceptor {
// 数据库名称
private String routerName;
public Object invoke(MethodInvocation invocation) throws Throwable {
String outsideRouter = RouterHolder.getRouter();
RouterHolder.setRouter(this.routerName);
try {
return invocation.proceed();
} finally {
RouterHolder.clearRouter();
if (outsideRouter != null) {
RouterHolder.setRouter(outsideRouter);
}
}
}
public void setRouterName(String routerName) {
this.routerName = routerName;
}
}
package com.xxx.dsrouting;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态路由数据库连接池对象。
* @author liwei
*/
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return RouterHolder.getRouter();
}
}
路由配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<description>数据库连接池及事务配置。</description>
<import resource="classpath:spring/jdbc-default.xml" />
<import resource="classpath:spring/jdbc-offline.xml" />
<import resource="classpath:spring/jdbc-parking.xml" />
<!-- 数据库路由配置 -->
<bean id="dataSource" class="com.xxx.dsrouting.DynamicRoutingDataSource">
<!-- 此处Default必须设置为MySQL -->
<property name="defaultTargetDataSource" ref="dataSource_default" />
<property name="targetDataSources">
<map>
<entry key="default" value-ref="dataSource_default" />
<entry key="offline" value-ref="dataSource_offline" />
<entry key="parking" value-ref="dataSource_parking" />
</map>
</property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 注解式事务配置 -->
<tx:annotation-driven transaction-manager="transactionManager" order="3" />
<bean id="routerAdvice" class="com.xxx.dsrouting.RouterAdvice" />
<bean id="productionRouterAdvice" class="com.xxx.dsrouting.RouterNameAdvice">
<property name="routerName" value="usr" />
</bean>
<aop:config>
<aop:pointcut id="routerPointcut" expression="execution(* com.sendinfo.biz.*.service.impl.*.*(..))" />
<aop:advisor advice-ref="routerAdvice" pointcut-ref="routerPointcut" order="1" />
</aop:config>
</beans>
线下ORACLE 的jdbc连接配置:jdbc-offline.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:property-placeholder location="classpath:config/jdbc-offline.properties"
ignore-unresolvable="true" />
<!-- 数据连接池 -->
<bean id="dataSource_offline" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${offline.jdbc.driverClassName}" />
<property name="url" value="${offline.jdbc.url}" />
<property name="username" value="${offline.jdbc.username}" />
<property name="password" value="${offline.jdbc.password}" />
<property name="maxActive" value="300" />
<property name="maxWait" value="1000" />
<property name="poolPreparedStatements" value="true" />
<property name="defaultAutoCommit" value="true" />
<property name="validationQuery" value="${offline.jdbc.validationQuery}" />
<property name="testOnBorrow" value="true" />
</bean>
</beans>
对应的jdbc-offline.properties
offline.jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
offline.jdbc.url=jdbc\:oracle\:thin\:@数据库IP\:1521\:orcl
offline.jdbc.username=xxxx
offline.jdbc.password=XXXX
offline.jdbc.validationQuery=select 1 from dual
offline.jdbc.type=oracle
项目Mysql 的jdbc连接配置:jdbc-default.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:property-placeholder location="classpath:config/jdbc-default.properties"
ignore-unresolvable="true" />
<!-- 数据连接池 -->
<bean id="dataSource_default" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="url" value="${default.jdbc.url} " />
<property name="username" value="${default.jdbc.username}" />
<property name="password" value="${default.jdbc.password}" />
<property name="driverClassName" value="${default.jdbc.driverClassName}"></property>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="1" />
<property name="minIdle" value="1" />
<property name="maxActive" value="300" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="1000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="3000000" />
<property name="validationQuery" value="SELECT 1+1" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<!-- 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。分库分表较多的数据库,建议配置为false。 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
<property name="filters" value="stat" />
</bean>
</beans>
对应的jdbc-default.properties
# [Default JDBC config]
default.jdbc.driverClassName=com.mysql.jdbc.Driver
default.jdbc.url=jdbc:mysql://IP:3306/数据库名?useUnicode=true&characterEncoding=utf-8
default.jdbc.username=xxx
default.jdbc.password=xxx
停车场sql server 的jdbc连接配置:jdbc-parking.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:property-placeholder location="classpath:config/jdbc-parking.properties"
ignore-unresolvable="true" />
<!-- 数据连接池 -->
<bean id="dataSource_parking" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${parking.jdbc.driverClassName}" />
<property name="url" value="${parking.jdbc.url}" />
<property name="username" value="${parking.jdbc.username}" />
<property name="password" value="${parking.jdbc.password}" />
<property name="maxActive" value="300" />
<property name="maxWait" value="1000" />
<property name="poolPreparedStatements" value="true" />
<property name="defaultAutoCommit" value="true" />
<property name="validationQuery" value="${parking.jdbc.validationQuery}" />
<property name="testOnBorrow" value="true" />
</bean>
</beans>
对应的jdbc-parking.properties
parking.jdbc.driverClassName=net.sourceforge.jtds.jdbc.Driver
parking.jdbc.url=jdbc:jtds:sqlserver://IP:1433/数据库名称
parking.jdbc.username=xxx
parking.jdbc.password=xxx
parking.jdbc.validationQuery=select 1
parking.jdbc.type=sqlserver
配置文件所需要的jar包自行下载,不再赘述,然后这个代码本人也不是完全搞清楚了,就不再详细解释。有疑问的可以私信探讨。
PS:如果是同一个oracle数据库,ip:端口都相同的情况下,跨用户查询的时候,可带上数据库名也可查询。