Spring + Hibernate: совместное использование транзакционных и нетранзакционных источников данных

В моем приложении Spring 2.5.6 + Hibernate мне нужно читать / записывать данные в / из нескольких баз данных с разными схемами. Приложение находится на Tomcat, поэтому на данный момент я бы предпочел не использовать JTA, чтобы мне не приходилось переходить на полноценный сервер приложений.

Поэтому я решил использовать только один источник транзакционных данных. Я могу жить с другими, не ведя транзакций.

Но как-то не могу заставить его работать. Не могли бы вы дать мне понять, что я делаю не так?

Это мой applicationContext.xml:

 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- ..configuration.. -->
</bean>

<bean id="nonTransactionalSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="nonTransactionalDataSource" />
    <!-- ..configuration.. -->
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- ..configuration.. -->
</bean>

<bean id="nonTransactionalDataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <!-- ..configuration.. -->
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="daoHolder" class="com.app.services.TransactionTest$DaoHolder"/>

<tx:annotation-driven transaction-manager="txManager"/>

<bean id="transactionalDao" class="com.app.services.TransactionalDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="nonTransactionalDao" class="com.app.services.NonTransactionalDaoImpl">
        <property name="sessionFactory" ref="nonTransactionalSessionFactory" />
</bean>

Как видите, приведенное выше - это всего лишь определение двух фабрик сеансов, каждая из которых использует свой собственный источник данных. Диспетчер транзакций использует только одну из этих фабрик сеансов, и настроено tx-управление на основе аннотаций.

Вот мой модульный тест, в котором я пытаюсь проверить транзакционное поведение:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/applicationContext.xml"})
public class TransactionTest {

@Autowired
private DaoHolder daoHolder;

@Test
public void testTransactions() throws Exception {

    try {
        daoHolder.runTransactionalMethod();
    } catch (Exception exception) {
        System.out.println("Exception caught");
    }
}

public static class DaoHolder {

    @Autowired
    private TransactionalDao transactionalDao;
    @Autowired
    private NonTransactionalDao nonTransactionalDao;

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor={Exception.class})
    private void runTransactionalMethod() throws Exception {
        transactionalDao.insertRow();
        nonTransactionalDao.insertRow();
        throw new Exception();
    }

}

В результате вышеупомянутого теста я ожидаю, что новая строка будет вставлена ​​в нетранзакционный источник данных, потому что изменения в транзакционном источнике данных должны быть отменены, потому что возникает исключение. Однако после прохождения этого теста я вижу строки, вставленные в оба источника данных.

РЕДАКТИРОВАТЬ: Я двинулся немного дальше. Я превратил DaoHolder в интерфейс, переместил вышеуказанную логику в класс, реализующий интерфейс (DaoHolderImpl), и пометил также класс (а не только метод) с помощью @Transactional. Я также добавил этот класс как Spring Bean. Итак, теперь Spring обрабатывает мои транзакции. Но на этот раз при выдаче исключения новая строка откатывается из обоих источников данных, а не только из транзакционного. Это все еще не то, чего я ожидал: /

Кто-нибудь видит, что я делаю не так? Заранее спасибо,

Питер


person machinery    schedule 28.12.2011    source источник


Ответы (1)


А как насчет того, чтобы поставить точки останова на строку "throw exception", чтобы вы могли видеть, есть ли эта строка в одной или обеих базах данных?

Это не решает вашу странную проблему отката, а не приложение, но может сказать вам, хорошо ли работает ваш нетранзакционный источник данных.

Вы бы предпочли включить ведение журнала Spring / Hibernate, чтобы увидеть, что происходит с транзакциями ...

person Sebastien Lorber    schedule 28.12.2011
comment
Нет проблем, спасибо за попытку :) Я только что дошел до того момента, когда после выброса исключения я получаю откат (пришлось добавить @Transactional ко всему классу DaoHolder), но на этот раз изменения из обоих DAO кажутся откат, потому что ничего не вставлено. Это удивительно для меня, потому что я ожидал отката только транзакционного дао. В любом случае - это все еще не то поведение, которое мне нужно. - person machinery; 28.12.2011
comment
Возможно, вам следует настроить режим очистки вашей нетранзакционной базы данных. Возможно, строка будет вставлена ​​в сеанс, не связанный с транзакциями, но сброса просто не произошло. Вы видите что-нибудь в журналах SQL спящего режима (свойство show_sql)? Как насчет вашей конфигурации БД? Вы установили что-то вроде autocommit = true или что-то в этом роде? - person Sebastien Lorber; 28.12.2011
comment
Что ж, если я не выберу исключение, я увижу обе вставки в журналах гибернации SQL. Если я выброшу Exception, я не увижу никаких вставок. Хм .. в журналах я вижу следующее: Открыл новый сеанс [org.hibernate.impl.SessionImpl@1517997] для транзакции Hibernate только один раз. Итак, я предполагаю, что моя проблема вызвана тем, что на самом деле обе эти операции выполняются в одном сеансе Hibernate. Почему это так и как это изменить? - person machinery; 28.12.2011
comment
Извините, я действительно не знаю. Но обычно Spring связывает сеанс, открытый аннотациями @Transactionnal, с текущим потоком, и поэтому, возможно, если вы используете шаблон поддержки / гибернации spring dao или что-то в этом роде, он видит, что сеанс привязан к потоку, и просто использует его ... В как ваш dao, попробуйте сделать getSession () или что-то еще и проверьте журналы, если это тот же объект - person Sebastien Lorber; 28.12.2011
comment
Отличная идея, спасибо :) Попробую сразу. Знаете ли вы какие-нибудь хорошие ресурсы по управлению весенними транзакциями? Я хотел бы узнать более подробно, что именно происходит, когда вы что-то аннотируете с помощью @Transactional. Документы Spring полезны, но они не предоставили мне достаточно информации ... Еще раз спасибо. - person machinery; 28.12.2011
comment
Проверено - в обоих DAO getSession () возвращает РАЗНЫЕ объекты. Получается, у меня две сессии. Теперь мне нужно убедиться, что один из них не является транзакционным. - person machinery; 28.12.2011
comment
Извините, я не знаю, есть ли хорошо объясненный документ обо всем, что волшебная пружина делает для вас ... когда магия не работает, это всегда боль;) Кстати, было бы хорошо иметь свои реализации dao - person Sebastien Lorber; 28.12.2011
comment
Привет :) Я перефразировал этот вопрос, чтобы уточнить его, и теперь он здесь: stackoverflow.com/questions/8700160/ Вы также можете найти реализации Dao там. Спасибо - person machinery; 03.01.2012