Правильный способ настройки транзакций в Spring для разных источников данных?
У меня есть приложение, которое необходимо подключить к нескольким базам данных. Это административное приложение, которое в основном используется для управления записями в разных базах данных - нам не нужно одновременно обращаться к нескольким базам данных, и нам не нужно какое-либо управление распределенными транзакциями.
В основном одна область приложения позволяет создавать гаджеты в базе данных A, а другая область приложения позволяет настраивать аналогичные гаджеты в базе данных B.
У нас уже созданы транзакции и работают отлично, используя только один источник данных. Конфигурация выглядит так:
<aop:config>
<aop:pointcut id="companyServicePoint"
expression="execution(* com.company.service.CompanyService.*(..))" />
<aop:advisor advice-ref="companyServiceTxAdvice"
pointcut-ref="companyServicePoint"/>
</aop:config>
<tx:advice id="companyServiceTxAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- set propogation required on create methods, all others are read-only -->
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
Это устанавливает pointcut при любом выполнении любых методов в CompanyService
и связывает рекомендации по сделкам с pointcut, которые требуют транзакций для любых методов, имя которых начинается с "create". Совет по транзакциям связан с TransactionManager, который привязан к источнику данных.
При добавлении второго (или более) источников данных, как я могу применить один и тот же совет по транзакциям к другим источникам данных? Поскольку совет АОП может быть связан только с одним транзакционным менеджером, который может быть связан только с одним источником данных, мне нужно настроить повторяющиеся рекомендации по сделке?
Если я настрою дубликат рекомендаций по транзакциям на один и тот же pointcut, не означает ли это, что любые вызовы методов в моем интерфейсе CompanyService
потребуют распространения от всех моих данных?
Чтобы сделать мой последний вопрос немного яснее, у меня будет несколько объявленных beans, которые реализуют интерфейс CompanyService
, и каждый из этих beans будет иметь отдельный CompanyDAO
для доступа к их отдельному DataSource. Я боюсь, что этот подход будет означать, что при вызове companyService1
bean совет по транзакциям будет запущен на all
companyService beans/dataSources.
Я иду об этом неправильно?
Обновление: Я действительно протестировал конфигурацию, о которой я говорил выше (прикрепление двух советников к тому же pointcut), и вызов любого метода для каждого отдельного экземпляра реализации CompanyService
выполняется в факт создает новые транзакции на обоих источниках данных, как и ожидалось:
DEBUG company.serviceDataSourceTransactionManager - Creating new transaction with name [com.company.service.CompanyService.createCompany]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG company.serviceDataSourceTransactionManager - Acquired Connection [connection1 string here...] for JDBC transaction
...
DEBUG company.serviceDataSourceTransactionManager - Creating new transaction with name [com.company.service.CompanyService.createCompany]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG company.serviceDataSourceTransactionManager - Acquired Connection [connection2 string here...] for JDBC transaction
...
DEBUG company.serviceDataSourceTransactionManager - Rolling back JDBC transaction on Connection [connection1 string here...]
...
DEBUG company.serviceDataSourceTransactionManager - Rolling back JDBC transaction on Connection [connection2 string here...]
Похоже, что это вызовет проблемы в будущем, поскольку либо экземпляр CompanyService
работает только с одним DataSource.
Есть ли лучший способ настроить то, что я пытаюсь выполнить?
Ответы
Ответ 1
Да, вам нужен двойной совет по сделке. Обратите внимание, что в следующей конфигурации выражение pointcut выбирает конкретный CompanyService bean.
<bean id="companyService1" class="com.company.service.CompanyServiceImpl">
<property name="companyDao">
<bean class="com.company.service.CompanyDAO">
<property name="dataSource" ref="dataSource1"/>
</bean>
</property>
</bean>
<aop:config>
<aop:pointcut
id="companyServicePoint1"
expression="bean(companyService1)"/>
<aop:advisor
advice-ref="companyServiceTxAdvice1"
pointcut-ref="companyServicePoint1"/>
</aop:config>
<tx:advice id="companyServiceTxAdvice1" transaction-manager="txManager1">
<tx:attributes>
<!-- set propogation required on create methods, all others are read-only -->
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<bean
id="txManager1"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
</bean>
Чтобы настроить другой CompanyService bean, вам необходимо продублировать тот же подробный шаблон. Другой способ разграничения транзакций в Spring использует TransactionProxyFactoryBean
. Он немного менее подробный, поскольку он использует родительское определение bean для настройки общих свойств, унаследованных дочерним элементом beans.
<bean
id="baseTransactionProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean id="companyService1" parent="baseTransactionProxy">
<property name="transactionManager" ref="txManager1"/>
<property name="target">
<bean class="com.company.service.CompanyServiceImpl">
<property name="companyDao">
<bean class="com.company.service.CompanyDAO">
<property name="dataSource" ref="dataSource1"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean
id="txManager1"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
</bean>
Ответ 2
Вы пытались использовать JtaTransactionManager?
http://forum.springsource.org/showthread.php?t=10476