从单数据源转变到多数据源的环境,应用程序面临的其中一个问题是,不同的模块对应不同的数据源,如何透明地使用不同的数据源,达到程序的最小改动。
Spring框架提供了自定义数据源路由功能,可以根据规则获取不同的数据源。
实现思路:参考Oliver Gierke在*上的回复。
剽窃一张图片说明一下(原文链接),图片中的DynamicDataSource对应下面配置中的dataSource
主要配置:
<!-- 配置两个数据源 --> <bean id="dsa" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driver"> <bean class="com.mysql.jdbc.Driver"></bean> </property> <property name="url" value="jdbc:mysql://localhost:3306/da" /> <property name="username" value="root" /> <property name="password" value="root" /> </bean> <bean id="dsb" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"> <property name="driver"> <bean class="com.mysql.jdbc.Driver"></bean> </property> <property name="url" value="jdbc:mysql://localhost:3306/db" /> <property name="username" value="root" /> <property name="password" value="root" /> </bean>
<!-- 自定义数据源路由规则 --> <bean id="dataSource" class="jpa.data.ds.CustomerRoutingDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="a" value-ref="dsa" /> <entry key="b" value-ref="dsb" /> </map> </property> <property name="defaultTargetDataSource" ref="dsa" /> </bean>
这样所有需要数据源的地方统一跟dataSource对象打交道,由它根据规则返回对应的数据源。
dataSource实现代码:
public class CustomerRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return CustomerContextHolder.getName(); } }
public class CustomerContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setName(String name) { contextHolder.set(name); } public static String getName() { return contextHolder.get(); } }
透明地使用多数据源:
// 采用Spring+JPA方式实现 public void find() { CustomerContextHolder.setName("a"); // 选择dsa数据源 User user = userRepository.findOne(1L); // 获取的是dsa数据源 assertNotNull(user); CustomerContextHolder.setName("b"); // 选择dsb数据源 Photo photo = photoRepository.findOne(1L); // 获取的是dsb数据源 assertNotNull(photo); }
参考: