spring的AbstractRoutingDataSource实现数据库链接切换。java
能够动态的切换数据源,可是对事务有影响,能够用JTA实现事务一致,可是效率较低。并且咱们项目事务能够单库一致就知足需求。因此采用了这种方式。mysql
下面是具体的实现过程:spring
1)spring的配置文件中配置多个数据源。sql
a:若是有相同的属性值可以下配置数据库
<!-- 数据源相同的内容 --> <bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" /> <property name="username" value="sa" /> <property name="password" value="net2com" /> </bean> <!-- start如下配置各个数据源的特性 --> <bean parent="parentDataSource" id="testDataSource"> <property name="url" value="jdbc:sqlserver://localhost:1433;databaseName=test" /> </bean> <bean parent="parentDataSource" id="UserDataSource"> <property name="url" value="jdbc:sqlserver://localhost:1433;databaseName=User" /> </bean>
b:若是没有,能够以下配置apache
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="${mysql.url}" /> <property name="username" value="${mysql.username}" /> <property name="password" value="${mysql.password}" /> <!-- 链接池启动时的初始值 --> <property name="initialSize" value="1" /> <!-- 最大空闲值.当通过一个高峰时间后,链接池能够慢慢将已经用不到的链接慢慢释放一部分,一直减小到maxIdle为止 --> <property name="maxIdle" value="2" /> <!-- 最小空闲值.当空闲的链接数少于阀值时,链接池就会预申请一些链接,以免洪峰来时再申请而形成的性能开销 --> <property name="minIdle" value="1" /> </bean> <bean id="xiaomi" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="${mysql.url.xiaomi}" /> <property name="username" value="${mysql.username}" /> <property name="password" value="${mysql.password}" /> <!-- 链接池启动时的初始值 --> <property name="initialSize" value="1" /> <!-- 最大空闲值.当通过一个高峰时间后,链接池能够慢慢将已经用不到的链接慢慢释放一部分,一直减小到maxIdle为止 --> <property name="maxIdle" value="2" /> <!-- 最小空闲值.当空闲的链接数少于阀值时,链接池就会预申请一些链接,以免洪峰来时再申请而形成的性能开销 --> <property name="minIdle" value="1" /> </bean>
2)定义动态的数据源app
<bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="testDataSource" key="test"></entry> <entry value-ref="UserDataSource" key="User"></entry> </map> </property> <property name="defaultTargetDataSource" ref="testDataSource" ></property> </bean>
在这个配置中第一个property属性配置目标数据源,<map key-type="Java.lang.String">中的key-type必需要和静态键值对照类DataSourceConst中的值的类型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必需要和静态键值对照类中的值相同,若是有多个值,能够配置多个< entry>标签。第二个property属性配置默认的数据源。ide
java code 以下:sqlserver
/** * 动态配置多数据源 * 数据源的名称常量类 * @author LONGHUI_LUO * */ public class DataSourceConst { public static final String TEST="test"; public static final String USER="User"; }
/** * 得到和设置上下文环境 主要负责改变上下文数据源的名称 * * @author LONGHUI_LUO * */ public class DataSourceContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal(); // 线程本地环境 // 设置数据源类型 public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } // 获取数据源类型 public static String getDataSourceType() { return (String) contextHolder.get(); } // 清除数据源类型 public static void clearDataSourceType() { contextHolder.remove(); } }
3)修改事务管理器的数据源为动态数据源,指定事务注解的排序为2,咱们会指定切换数据源的注解为1,这样在事务以前切换数据源,不然在事务以后切换的的话,无效。性能
<!-- 注解事务处理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource" /> <!-- 启用注解 --> <tx:annotation-driven transaction-manager="transactionManager" order="2"/>
4)定义切换数据库的注解和aop切面,指定排序为1,这里有个疑问,经过切点获取代理方法的注解数据,我用的是反射,可是网上有说能够直接做为参数传入的,我一直没有试验成功,不知道哪里有错,后续哪位大神指导的,能够分享一下。
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface DataSource { String name(); } @Component @Aspect @Order(1) public class DataSourceProxy { @Before(value="@annotation(com.futuren.wzk.common.datasource.DataSource)") public void before(JoinPoint jp) { String methodName = jp.getSignature().getName(); Method[] methods = jp.getTarget().getClass().getMethods(); for(Method method : methods) { if(method.getName().equals(methodName)) { DataSource ds = method.getAnnotation(DataSource.class); DataSourceContextHolder.setDataSourceType(ds.name()); } } } }
在项目中使用
@Override @Transactional @DataSource(name="ucenter") public int addUser(User user) { userMapper.insert(user); return user.getUid(); }
这种方法,只支持单库事务,若是要多库事务,可能要引入JTA,或者是其余自定义实现。或者其余我不知道的技术。欢迎讨论!