简单的翻译, 也算是一篇笔记.
原文:http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/
在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上, 同时对于不支持事务隔离级别的JTA事务来说, Spring还提供了另外一个类IsolationLevelDataSourceRouter来处理这个问题. 下面的例子将通过context来切换不同的数据源.
首先定义一个Catalog的Dao:
- package blog.datasource;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.List;
- import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
- import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
- public class Catalog extends SimpleJdbcDaoSupport {
- public List<Item> getItems() {
- String query = "select name, price from item";
- return getSimpleJdbcTemplate().query(query, new ParameterizedRowMapper<Item>() {
- public Item mapRow(ResultSet rs, int row) throws SQLException {
- String name = rs.getString(1);
- double price = rs.getDouble(2);
- return new Item(name, price);
- }
- });
- }
- }
然后定义一个Item的JavaBean
- package blog.datasource;
- public class Item {
- private String name;
- private double price;
- public Item(String name, double price) {
- this.name = name;
- this.price = price;
- }
- public String getName() {
- return name;
- }
- public double getPrice() {
- return price;
- }
- public String toString() {
- return name + " (" + price + ")";
- }
- }
接着定义一个枚举类型, 用来表示不同的用户级别, 通过该类型将映射到不同的数据源
- public enum CustomerType {
- BRONZE,
- SILVER,
- GOLD
- }
下面是DataSource定义:
- <bean id="parentDataSource"
- class="org.springframework.jdbc.datasource.DriverManagerDataSource"
- abstract="true">
- <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
- <property name="username" value="sa"/>
- </bean>
- <bean id="goldDataSource" parent="parentDataSource">
- <property name="url" value="jdbc:hsqldb:hsql://localhost:${db.port.gold}/blog"/>
- </bean>
- <bean id="silverDataSource" parent="parentDataSource">
- <property name="url" value="jdbc:hsqldb:hsql://localhost:${db.port.silver}/blog"/>
- </bean>
- <bean id="bronzeDataSource" parent="parentDataSource">
- <property name="url" value="jdbc:hsqldb:hsql://localhost:${db.port.bronze}/blog"/>
- </bean>
- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="location" value="classpath:/blog/datasource/db.properties"/>
- </bean>
AbstractRoutingDataSource 实现类
- package blog.datasource;
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
- public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
- @Override
- protected Object determineCurrentLookupKey() {
- return CustomerContextHolder.getCustomerType();
- }
- }
CustomerContextHolder 是一个和LocalThread绑定的类, 定义如下:
- public class CustomerContextHolder {
- private static final ThreadLocal<CustomerType> contextHolder =
- new ThreadLocal<CustomerType>();
- public static void setCustomerType(CustomerType customerType) {
- Assert.notNull(customerType, "customerType cannot be null");
- contextHolder.set(customerType);
- }
- public static CustomerType getCustomerType() {
- return (CustomerType) contextHolder.get();
- }
- public static void clearCustomerType() {
- contextHolder.remove();
- }
- }
将dao bean和datasource bean结合起来, 至于dao和真正的datasource如何关联这个可以根据需要指定相关的策略和规则来实现:
- <bean id="catalog" class="blog.datasource.Catalog">
- <property name="dataSource" ref="dataSource"/>
- </bean>
- <bean id="dataSource" class="blog.datasource.CustomerRoutingDataSource">
- <property name="targetDataSources">
- <map key-type="blog.datasource.CustomerType">
- <entry key="GOLD" value-ref="goldDataSource"/>
- <entry key="SILVER" value-ref="silverDataSource"/>
- </map>
- </property>
- <property name="defaultTargetDataSource" ref="bronzeDataSource"/>
- </bean>
下面通过一个TestCase来看看如何使用:
- public class CatalogTests extends AbstractDependencyInjectionSpringContextTests {
- private Catalog catalog;
- public void setCatalog(Catalog catalog) {
- this.catalog = catalog;
- }
- public void testDataSourceRouting() {
- CustomerContextHolder.setCustomerType(CustomerType.GOLD);
- List<Item> goldItems = catalog.getItems();
- assertEquals(3, goldItems.size());
- System.out.println("gold items: " + goldItems);
- CustomerContextHolder.setCustomerType(CustomerType.SILVER);
- List<Item> silverItems = catalog.getItems();
- assertEquals(2, silverItems.size());
- System.out.println("silver items: " + silverItems);
- CustomerContextHolder.clearCustomerType();
- List<Item> bronzeItems = catalog.getItems();
- assertEquals(1, bronzeItems.size());
- System.out.println("bronze items: " + bronzeItems);
- }
- protected String[] getConfigLocations() {
- return new String[] {"/blog/datasource/beans.xml"};
- }
- }