Как настроить нескольких менеджеров транзакций с весны + д. Банкрофт + с JUnit


В двух словах

Мое Java-приложение командной строки копирует данные из одного источника данных в другой без использования XA. Я настроил два отдельных источника данных и хотел бы получить тест JUnit, который может откатывать данные на обоих источниках данных. Я использую DBUnit для загрузки данных в базу данных" source", но не могу заставить ее откатиться. Я могу заставить" целевой " источник данных откатиться.

Мой Код

Учитывая эту конфигурацию...

<tx:annotation-driven />

<!-- note the default transactionManager name on this one -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource"     ref="dataSourceA" />
</bean>

<bean id="transactionManagerTarget" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource"     ref="dataSourceB" />
</bean>

И это код...

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:resources/spring-context.xml",
                                "classpath:resources/spring-db.xml"})  
@Transactional
@TransactionConfiguration(transactionManager = "transactionManagerTarget", defaultRollback = true) 
public class MyIntegrationTest {

    @Autowired
    private MyService service;

    @Autowired
    @Qualifier("dataSourceA")
    private DataSource dataSourceA;

    private IDataSet loadedDataSet;

    /**
     * Required by DbUnit
     */
    @Before
    public void setUp() throws Exception {
        SybaseInsertIdentityOperation.TRUNCATE_TABLE.execute(getConnection(), getDataSet());
        SybaseInsertIdentityOperation.INSERT.execute(getConnection(), getDataSet());
    }

    /**
     * Required by DbUnit
     */
    protected IDataSet getDataSet() throws Exception {
        loadedDataSet = DbUnitHelper.getDataSetFromFile(getConnection(), "TestData.xml");
        return loadedDataSet;
    }

    /**
     * Required by DbUnit
     */
    protected IDatabaseConnection getConnection() throws Exception{
        return new DatabaseConnection(dataSourceA.getConnection());
    }   

    @Test
    public void testSomething() {

        // service.doCopyStuff();

    }

}
Проблема, как я ее вижу, заключается в том, что @TransactionConfiguration указывает только целевой источник данных для включения отката. DBUnit передается dataSourceA явно и собирает диспетчер транзакций по умолчанию с именем transactionManager (я не уверен, Как), который не был уведомлен об откате.

Вопрос

Как я могу сказать обоим менеджерам транзакций откатиться?

Могу ли я использовать один диспетчер транзакций, если мои источники данных не поддерживают XA сделки?

Примечание: приложение не требует менеджера транзакций на dataSourceA при запуске в рабочей среде, так как оно будет доступно только для чтения. Эта проблема касается только моих классов тестов.

3 5

3 ответа:

Возможным обходным путем было бы ввести вспомогательный боб с аннотацией @Transactional("transactionManagerTarget") и оставить ваш тест с аннотацией @Transactional("transactionManager"), настроив оба с defaultRollback = true. Затем тест должен будет вызвать вспомогательный компонент, который, в свою очередь, вызовет тестируемый компонент службы. Это должно привести к откату транзакции вокруг вашего сервиса, а затем транзакции вокруг DBUnit.

Это немного грязно, хотя.

Другие возможные подходы:

  • использование базы данных в памяти, такой как H2 вместо вашей производственной базы данных-вы можете настроить это, чтобы удалить все свои данные, когда это необходимо.
  • разрешите DBUnit совершить коммит и выполните компенсирующую транзакцию в своем методе разборки, чтобы очистить данные.

Используйте элемент <qualifier> в определении диспетчера транзакций.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSourceA" />
    <qualifier value="transactionManager" />
</bean>

<bean id="transactionManagerTarget" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSourceB" />
    <qualifier value="transactionManagerTarget" />
</bean>

Затем вы можете указать, какой из них вы хотите использовать непосредственно в аннотации @Transactional, т. е.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:resources/spring-context.xml",
                                "classpath:resources/spring-db.xml"})  
@Transactional("transactionManagerTarget")
@TransactionConfiguration(defaultRollback = true) 
public class MyIntegrationTest {
...

Я использовал транзакции XA и откаты в тестах JUnit с открытым исходным кодом TM Atomikos. Одной из приятных особенностей является то, что Atomikos позволяет использовать источники данных, не включенные в XA, для участия в транзакциях XA. Проверьте эту ссылку для примера: http://www.atomikos.com/Documentation/NonXaDataSource

С другой стороны, если XA является достойным решением для ваших проблем JUnit-это другая история. Ваши тесты фокусируются много на реализации базы данных (Sybase) или это больше о Java логика? Обычно я настраиваю встроенные базы данных, такие как Apache Derby или HQSQL, для тестов JUnit. Тогда мне не нужно много заботиться о чистоте, так как GC справится с этим :)