Динамическая маршрутизация источника данных

Извините за мой плохой английский. Я написал реализацию для 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')");        
}

defineCurrentLookupKey() вызывается один раз, когда впервые выполняется getJdbcTemplate(), а источник данных не переключается.

2 ответа

Решение

Часть управления транзакциями Spring для транзакций JDBC заключается в привязке соединения к потоку при запуске транзакции. До тех пор, пока транзакция не завершится и соединение не будет разорвано, каждая постоянная операция с одним и тем же источником данных будет использовать это же соединение. Поскольку вы используете один источник данных для маскировки двух других, вы получите только одно соединение. Если вы явно используете два отдельных источника данных, каждый будет рассматриваться как отдельный ресурс, и для каждого будет запущено отдельное соединение, которое будет привязано к потоку. См. "Синхронизация ресурсов с транзакциями" в справочном руководстве, чтобы узнать хотя бы подсказку о том, что происходит внутри, когда вы используете транзакции с JdbcDaoSupport и JdbcTemplate.

Вы должны вызывать DatabaseContextHolder.setDatabaseType(DatabaseType.MAIN) перед вызовом метода. Hibernate ищет соединение после запуска транзакции. В вашем случае транзакция запускается до того, как будет выполнен DatabaseContextHolder.setDatabaseType(DatabaseType.MAIN). И после запуска транзакции схема не может быть изменена. Ваша реализация верна, попробуйте установить DatabaseType перед вызовом метода, т.е. перед запуском транзакции.

Другие вопросы по тегам