Spring OpenSessionInViewFilter с аннотацией @Transactional

Это относится к Spring OpenSessionInViewFilter используя аннотацию @Transactional на уровне сервиса.

я просмотрел столько записей на этом, но все еще смущен тем, должен ли я использовать OpenSessionInViewFilter или не избегать LazyInitializationException Было бы очень LazyInitializationException если кто-нибудь поможет мне узнать ответ на ниже запросы.

  • OpenSessionInViewFilter практика использования OpenSessionInViewFilter в приложении, имеющем сложную схему.
  • использование этого фильтра может вызвать проблему N+1
  • если мы используем OpenSessionInViewFilter, значит ли это, что @Transactional не требуется?

Ниже приведен файл конфигурации Spring

<context:component-scan base-package="com.test"/>
<context:annotation-config/>
 <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="resources/messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>
 <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="/WEB-INF/jdbc.properties" />
 <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
        p:password="${jdbc.password}" />
       <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />     
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <property name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${jdbc.dialect}</prop>
                <prop key="hibernate.show_sql">true</prop>
                <!--
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                 -->
            </props>
        </property>
    </bean>
 <tx:annotation-driven /> 
 <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />

  </bean>

Ответ 1

OpenSessionInView - это фильтр сервлетов, чем просто открыть сеанс спящего режима и сохранить его в SessionHolder для потока, обслуживающего запрос. Когда этот сеанс открыт, hibernate может прочитать Lazy инициализированные коллекции и объекты, когда вы используете это на этапе рендеринга запроса. Этот сеанс можно получить, когда вы вызываете SessionFactory.getCurrentSession().

Но OpenSessionInView просто открывает сеанс, и он не запускает никаких транзакций. Открыв сеанс, вы можете читать объекты из базы данных, но если вы хотите что-то сделать в транзакции, вам нужны аннотации @Transactional или другой механизм, чтобы разграничить начало и конец транзакции по вашему желанию.

Затем ответ на вопросы:

Неправильная практика использования OpenSessionInViewFilter в приложении, имеющем сложную схему.

Это хорошая практика, если вам нужно избегать LazyInitializationException, а перегрузка - это просто новый сеанс Hibernate и закрыть его в конце запроса для каждого запроса.

Использование этого фильтра может вызвать проблему N + 1

Я использую этот фильтр во многих проектах и не вызываю никаких проблем.

если мы используем OpenSessionInViewFilter, значит ли это, что @Transactional не требуется?

Нет. У вас только сеанс Hibernate открыт в SessionHolder потока, но если вам нужны транзакции, вам нужно поставить @Transactional.

Ответ 2

Бросив мой 0.02c здесь (и расширяя на Fernando Rincon отличный ответ):

Вы не должны использовать фильтр OpenSessionInView только потому, что вам нужно обойти LazyInitializationException. Его просто добавит еще один уровень замешательства и сложности вашей системы. Вы должны знать из своего системного дизайна, где вам нужно получить доступ к коллекциям на лицевой стороне. Оттуда легко и (по моему опыту) логичнее построить метод контроллера для вызова метода службы для извлечения вашей коллекции.

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

Взвесьте расходы на этот рефактор против стоимости написания методов управления и обслуживания, чтобы определить, следует ли использовать фильтр OpenSessionInView.

Ответ 3

OpenSessionInViewFilter - это фильтр сервлета, который связывает сессию hibernate с HTTP-запросом и для всех операций db, транзакционных и не транзакционных, один и тот же сеанс спящего режима используется для данного HTTP-запроса. Это предоставляет слой db для веб-слоя, который делает его анти-шаблоном.

Мой опыт в том, что это затрудняет отладку кода, когда мы хотим внести изменения в объекты Java и не хотим, чтобы они отражались в базе данных. Поскольку сеанс hibernate всегда открыт, он ожидает сброса данных в базе данных.

Это должно использоваться только тогда, когда службы базового обслуживания JS находятся там, где между ними нет служебного слоя.

Ответ 4

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

Из этого URL-адреса https://developer.jboss.org/wiki/OpenSessionInView:

Проблема
Общей проблемой в типичном веб-приложении является рендеринг представления после завершения основной логики действия, поэтому сеанс Hibernate уже закрыт и транзакция базы данных завершена. Если вы получаете доступ к отдельным объектам, которые были загружены в сеанс внутри вашего JSP (или любой другой механизм рендеринга представлений), вы можете нажать на выгруженную коллекцию или прокси-сервер, который не инициализирован. Исключением является: LazyInitializationException: сеанс закрыт (или очень похожее сообщение). Конечно, этого следует ожидать, ведь вы уже закончили свою работу.

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

В качестве альтернативы рассмотрим загрузку Entity с нужным количеством данных, требуемым вашим представлением. Это можно сделать, используя проекции DTO. В этой статье перечислены некоторые из недостатков использования шаблона Open Session In View: https://vladmihalcea.com/the-open-session-in-view-anti-pattern/