У меня есть веб-приложение с Spring, настроенное для создания моей фабрики сеансов гибернации (singleton), а также сеанса и транзакции (оба имеют область действия запроса), но оно разрушает сеанс и транзакцию в неправильном порядке. Как я могу настроить его так, чтобы транзакция была уничтожена до начала сеанса? Вот мой весенний файл applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
      "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
  <bean id="hibernateSessionFactory" scope="singleton"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="configLocation" value="classpath:hibernate.cfg.xml" />
  </bean>

  <!-- The per-http request hibernate session -->
  <bean id="hibernateSession" factory-bean="hibernateSessionFactory"
    factory-method="openSession" destroy-method="close" scope="request" />

  <!--  The per-http request transaction (i need this to be destroyed BEFORE the session) -->
  <bean id="hibernateTransaction" factory-bean="hibernateSession"
    factory-method="beginTransaction" destroy-method="commit" scope="request" />
</beans>

А вот журнал, в котором видно, что он закрывает сеанс перед закрытием транзакции:

16111 [http-8080-3] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter  - Invoking destroy method 'close' on bean with name 'hibernateSession'
16111 [http-8080-3] DEBUG org.hibernate.jdbc.ConnectionManager  - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
16111 [http-8080-3] DEBUG com.mchange.v2.resourcepool.BasicResourcePool  - trace com.mchange.v2.resourcepool.BasicResourcePool@17e4dee [managed: 4, unused: 3, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@19a8416)
16111 [http-8080-3] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter  - Invoking destroy method 'commit' on bean with name 'hibernateTransaction'
16111 [http-8080-3] DEBUG org.hibernate.transaction.JDBCTransaction  - commit
16111 [http-8080-3] WARN  org.springframework.beans.factory.support.DisposableBeanAdapter  - Invocation of destroy method 'commit' failed on bean with name 'hibernateTransaction'
org.hibernate.SessionException: Session is closed
6
Chris 18 Янв 2010 в 03:42

3 ответа

Лучший ответ

Похоже, что порядок вызовов метода уничтожения для bean-компонентов без одноэлементной области полностью выходит из-под контроля. Из документов (5.1.4 Использование зависимости от):

Атрибут зависимости в определении bean-компонента может указывать как время инициализации, так и время инициализации. зависимости и, только в случае одноэлементных компонентов , соответствующее время уничтожения зависимость

Вы можете создать вспомогательный объект и делегировать ему создание и уничтожение ваших bean-компонентов:

public class HelperObject
{
    private SessionFactory factory;
    private Session session;
    private Transaction tx;

    public void init()
    {
        session = factory.createSession();
        tx = session.beginTransaction();
    }

    public void destroy()
    {
        tx.commit();
        session.close();
    }

    ...
} 

-

<bean id = "helperObject" class = "HelperObject" scope = "request" init-method = "init" destroy-method = "destroy">
    <property name = "factory" ref = "hibernateSessionFactory" />
</bean>

<bean id="hibernateSession" factory-bean="helperObject" 
    factory-method="getSession" scope="request" /> 

<bean id="hibernateTransaction" factory-bean="helperObject" 
    factory-method="getTransaction" scope="request" />

И, в конце концов, возможно, это не лучший способ управлять сеансами и транзакциями Hibernate в Spring. Рассмотрите возможность использования встроенного в Spring Hibernate. и транзакции.

РЕДАКТИРОВАТЬ: Что ж, правильный способ управления транзакциями - это :

  • Вам не нужны компоненты session и transaction с областью действия запроса
  • Вы не должны вызывать createSession в фабрике сеанса, возвращаемой org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean. Вы можете внедрить эту фабрику сеансов в свои bean-компоненты и вызвать getCurrentSession, когда вам понадобится сеанс, и он будет работать нормально.
  • Вы можете использовать декларативное управление транзакциями (аннотации @Transactional к транзакционным методам). Чтобы он заработал, вам следует добавить в свой конфиг:

.

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

<tx:annotation-driven/>
  • Для получения дополнительной информации см. Ссылки выше
4
Srini 18 Янв 2019 в 12:59
Привет, у меня было подозрение, что у spring есть встроенные параметры для управления сеансом / txn, однако после прочтения этих двух ссылок я все еще не приблизился к пониманию того, как они будут работать. Думаю, я выберу твой вариант «вспомогательного класса», это отличная идея. Мне жаль, что весна не может контролировать порядок уничтожения, она действительно не так уж много для меня делает.
 – 
Chris
18 Янв 2010 в 05:02
Я посмотрел на эти 2 ссылки, и я не вижу, как я могу использовать диспетчер транзакций Spring (или что-то еще), чтобы предоставить мне сеанс и транзакцию, которые я мог бы ввести в свои действия, это только казалось, что дало мне фабрику сеансов что я мог бы затем вызвать getCurrentSession (), что мне не нравится.
 – 
Chris
18 Янв 2010 в 05:03
Я предполагаю, что я спрашиваю, если это не лучший способ, какой является лучшим способом управления сессиями / txns с помощью Spring?
 – 
Chris
18 Янв 2010 в 05:10
Спасибо за обновления. Итак, идея заключается в том, что я добавляю эти изменения конфигурации, затем добавляю @Transactional ко всем моим классам, которым нужен доступ к спящему режиму, а затем в этих классах у меня есть getCurrentSession ()? Но как тогда эти классы получат SessionFactory?
 – 
Chris
18 Янв 2010 в 07:04
Хорошо, я пробовал то, что вы предложили, но теперь у меня проблема в том, что мой уровень служб загружает мой объект события нормально, но когда позже я пытаюсь получить доступ к полям в возвращенном объекте, у него есть исключение «LazyInitializationException - не удалось инициализировать прокси. - ошибка "нет сеанса", предположительно из-за того, что сеанс был закрыт после того, как мой доступ к уровню служб завершился. Итак, я думаю, что мне действительно действительно все еще нужен сеанс с ограничением по запросу, что бы вы сделали в этой ситуации?
 – 
Chris
18 Янв 2010 в 08:05

Вы можете заявить, что hibernateTransaction зависит от hibernateSession. Поскольку контейнер будет создавать экземпляры bean-компонентов в порядке зависимости (исключая циклические зависимости) и удалять их в обратном порядке зависимости, это должно помочь.

1
meriton 18 Янв 2010 в 04:10
1
Это не работает для bean-компонентов с ограниченным объемом запроса. Согласно документации, зависимость определяет порядок уничтожения только для bean-компонентов с одноэлементной областью действия.
 – 
axtavt
18 Янв 2010 в 04:26
Да, я уже пробовал, зависит от того, не помогло. Однако спасибо за ответ!
 – 
Chris
18 Янв 2010 в 04:50

Транзакции должны быть связаны со службами, если вы следуете идиоме Spring. Сеансы - это объекты веб-уровня, полностью отделенные от уровня служб. Мне кажется, вы совершили ошибку, связав свой веб-уровень с уровнем услуг. Лучше разлучить их; у вас вряд ли возникнет проблема с такой договоренностью.

1
duffymo 18 Янв 2010 в 05:44
Мне потребовалось полдюжины прочтений этого, чтобы понять, что вы имели в виду! Теперь я предполагаю, что когда вы говорите «сервис», вы имеете в виду классы «бизнес-уровня» (например, классы, которые имеют такие вещи, как «FindEventById ()»). Таким образом, услуги - это те, которые заботятся о транзакциях. Думаю, в этом есть смысл.
 – 
Chris
18 Янв 2010 в 06:59
Хорошо, я пробовал то, что вы предложили, но теперь у меня проблема в том, что мой уровень служб загружает мой объект события нормально, но когда позже я пытаюсь получить доступ к полям в возвращенном объекте, у него есть исключение «LazyInitializationException - не удалось инициализировать прокси. - ошибка "нет сеанса", предположительно из-за того, что сеанс был закрыт после того, как мой доступ к уровню служб завершился. Итак, я думаю, что сеанс все еще нужно как-то привязать к веб-запросу, какие-либо предложения, как вы это преодолели?
 – 
Chris
18 Янв 2010 в 08:04
Ага! Все заработало: необходимо настроить OpenSessionInViewFilter в файле web.xml. Уф!
 – 
Chris
18 Янв 2010 в 08:20