动态数据源路由

时间:2011-09-05 04:57:42

标签: java spring datasource

抱歉我的英语不好。我为AbstractRoutingDataSource编写了实现:

public class DatabaseRoutingDataSource extends AbstractRoutingDataSource{

    @Override
    protected Object determineCurrentLookupKey() {      
        return DatabaseContextHolder.getDatabaseType();
    }

}

我创建了用于在数据库之间切换的新类:

public class DatabaseContextHolder {

    private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<DatabaseType>();

    public static void setDatabaseType(DatabaseType databaseType) {
        contextHolder.set(databaseType);
    }

    public static DatabaseType getDatabaseType() {
        return (DatabaseType) contextHolder.get();
    }

    public static void clearDatabaseType() {
        contextHolder.remove();
    }
}

其中DatabaseType为:

public enum DatabaseType {
    MAIN,
    BACKUP
}

在我的beans.xml中:

<bean id="mainDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:/jdbc/DBMIDS"/>
</bean>
<bean id="backupDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:/jdbc/DBMIDS2"/>
</bean>
<bean id="dataSource" class="DatabaseRoutingDataSource">
    <property name="targetDataSources">
        <map key-type="DatabaseType">
            <entry key="MAIN" value-ref="mainDataSource"/>
            <entry key="BACKUP" value-ref="backupDataSource"/>
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="mainDataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<bean id="databaseTarget" class="DatabaseBean">
    <property name="dataSource" ref="dataSource"/>
</bean>
<bean id="database" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
    <property name="target" ref="databaseTarget"/>
    <property name="proxyInterfaces">
        <value>Database</value>
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="*">PROPAGATION_REQUIRED,-MyException</prop>
        </props>
    </property>
</bean>

现在,当我在DAO中尝试更改数据源时:

public class DatabaseBean extends JdbcDaoSupport implements Database
    public void myMethod() {
       DatabaseContextHolder.setDatabaseType(DatabaseType.MAIN);
       getJdbcTemplate().execute("INSERT INTO test(test) VALUES('test')");      
       DatabaseContextHolder.setDatabaseType(DatabaseType.BACKUP);
       getJdbcTemplate().execute("INSERT INTO test(test) VALUES('test')");        
}

determineCurrentLookupKey()调用一次,首次执行getJdbcTemplate(),并且数据源不切换。

2 个答案:

答案 0 :(得分:5)

Spring的JDBC事务事务管理的一部分是在事务开始时将连接绑定到线程。在事务结束并且连接未绑定之前,同一数据源上的每个持久性操作将使用相同的连接。因为您使用单个数据源来屏蔽其他两个数据源,所以您只能获得一个连接。如果您显式使用了两个单独的数据源,则每个数据源都将被视为一个单独的资源,并且将启动一个单独的连接并将其绑定到每个数据源的线程。请参阅参考指南中的"Synchronizing resources with transactions",了解当您使用JdbcDaoSupport和JdbcTemplate进行事务时,至少有一些暗示。

答案 1 :(得分:1)

您应该在方法调用之前调用DatabaseContextHolder.setDatabaseType(DatabaseType.MAIN)。一旦启动事务,Hibernate就会查找连接。在您的情况下,在DatabaseContextHolder.setDatabaseType(DatabaseType.MAIN)执行之前启动事务。并且一旦事务已经开始,架构就无法更改。您的实现是正确的尝试在方法调用之前,即在事务开始之前设置DatabaseType。