Java 带有@Transactional 注释的 Spring OpenSessionInViewFilter

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23877900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 08:47:34  来源:igfitidea点击:

Spring OpenSessionInViewFilter with @Transactional annotation

javaspringhibernatespring-transactions

提问by Gautam

This is regarding Spring OpenSessionInViewFilterusing with @Transactionalannotation at service layer.

这是关于 Spring在服务层OpenSessionInViewFilter使用 @Transactional注解。

i went through so many stack overflow post on this but still confused about whether i should use OpenSessionInViewFilteror not to avoid LazyInitializationExceptionIt would be great help if somebody help me find out answer to below queries.

我在这方面经历了很多堆栈溢出的帖子,但仍然对我是否应该使用OpenSessionInViewFilter或不应该避免感到困惑LazyInitializationException如果有人帮助我找到以下查询的答案,那将是非常有帮助的。

  • Is it bad practice to use OpenSessionInViewFilterin application having complex schema.
  • using this filter can cause N+1problem
  • if we are using OpenSessionInViewFilterdoes it mean @Transactionalnot required?
  • OpenSessionInViewFilter在具有复杂架构的应用程序中使用是不是不好的做法。
  • 使用此过滤器可能会导致N+1问题
  • 如果我们正在使用OpenSessionInViewFilter它是否意味着@Transactional不需要?

Below is my Spring config file

下面是我的 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>

采纳答案by Fernando Rincon

OpenSessionInViewis a servlet filter than just Open a hibernate session and store it in the SessionHolderfor the thread that is serving the request. With this session opened, hibernate can read the Lazy initialized collections and objects when you use this in the rendering stage of the request. This session can be accessed when you invoke SessionFactory.getCurrentSession().

OpenSessionInView是一个 servlet 过滤器,而不仅仅是打开一个休眠会话并将其存储在SessionHolder为请求提供服务的线程中。打开此会话后,当您在请求的呈现阶段使用它时,hibernate 可以读取 Lazy 初始化的集合和对象。当您调用SessionFactory.getCurrentSession().

But, OpenSessionInView just opens the session and it doesn't begin any transactions. With a session opened you can read objects from database but, if you want to do something in a transaction you need @Transactionalannotations or other mechanism to demarcate the begin and the end of the transaction when you want.

但是,OpenSessionInView 只是打开会话,它不会开始任何事务。打开会话后,您可以从数据库读取对象,但是,如果您想在事务中执行某些操作,则需要@Transactional注释或其他机制来在需要时划分事务的开始和结束。

Then the answer of the questions:

然后是问题的答案:

Is it bad practice to use OpenSessionInViewFilter in application having complex schema.

在具有复杂架构的应用程序中使用 OpenSessionInViewFilter 是不好的做法。

This is a good practice if you need avoid the LazyInitializationException and the overload is just open new Hibernate Session and close it at the end of the request for each request.

如果您需要避免 LazyInitializationException 并且重载只是打开新的 Hibernate Session 并在每个请求的请求结束时关闭它,那么这是一个很好的做法。

Using this filter can cause N+1 problem

使用这个过滤器会导致 N+1 问题

I use this filter in many projects and not cause any problem.

我在很多项目中都使用了这个过滤器,并没有造成任何问题。

if we are using OpenSessionInViewFilter does it mean @Transactional not required?

如果我们使用 OpenSessionInViewFilter 是否意味着不需要@Transactional?

No. You only have a Hibernate Session opened in the SessionHolder of the thread, but if you need Transactions you need put @Transactional.

不。您只在线程的 SessionHolder 中打开了一个 Hibernate Session,但是如果您需要 Transactions,则需要 put @Transactional

回答by JamesENL

Throwing in my 0.02c here (and expanding on Fernando Rincon's excellent answer):

在这里抛出我的 0.02c(并扩展Fernando Rincon 的优秀答案):

You shouldn't be using a OpenSessionInViewfilter justbecause you need to get around a LazyInitializationException. Its just going to add another layer of confusion and complexity to your system. You should know from your system design exactly where you are going to need to access collections on the front end. From there, it's easy and (in my experience) more logical to build a controller method to call a service method to retrieve your collection.

你不应该使用OpenSessionInView过滤器因为你需要得到周围LazyInitializationException。它只会给您的系统增加另一层混乱和复杂性。您应该从您的系统设计中确切地知道您需要在前端访问集合的位置。从那里开始,构建控制器方法来调用服务方法来检索您的集合很容易并且(根据我的经验)更合乎逻辑。

However if you have another problem that using the OpenSessionInViewfilter solves, and as a happy side effect you then have a session open, then I don't see the harm in using it to access your collections. However, I'd say that if you use the OpenSessionInViewto fetch a collection object in one place, you should refactor your code in other places to do the same thing so as the strategy used to fetch collections is standardised across your application.

但是,如果您有使用OpenSessionInView过滤器解决的另一个问题,并且作为一个愉快的副作用,您可以打开一个会话,那么我看不出使用它来访问您的收藏有什么害处。但是,我想说的是,如果您OpenSessionInView在一个地方使用来获取集合对象,那么您应该在其他地方重构您的代码以执行相同的操作,以便用于获取集合的策略在您的应用程序中是标准化的。

Weigh up the costs of this refactor against the cost of writing the controller & service methods to determine if you should be using a OpenSessionInViewfilter.

权衡此重构的成本与编写控制器和服务方法的成本,以确定您是否应该使用OpenSessionInView过滤器。

回答by Kamal Kishore

OpenSessionInViewFilter is a servlet filter that binds a hibernate session to http request and for all db operations, transactional and non transactional, same hibernate session is used for a given http request. This exposes db layer to web layer that makes it anti-pattern.

OpenSessionInViewFilter 是一个 servlet 过滤器,它将休眠会话绑定到 http 请求,并且对于所有数据库操作,事务性和非事务性,相同的休眠会话用于给定的 http 请求。这将 db 层暴露​​给 web 层,使其成为反模式。

My experience is that this makes the code difficult to debug when we want to make changes to java objects and do not want those to get reflected in database. Since the hibernate session is always open, it expects to flush the data in database.

我的经验是,当我们想要对 java 对象进行更改并且不希望这些更改反映在数据库中时,这会使代码难以调试。由于休眠会话始终处于打开状态,因此它希望刷新数据库中的数据。

This should be used only when JS base rest services are there with no service layer in between.

仅当存在 JS 基础休息服务且中间没有服务层时才应使用此选项。

回答by Suketu Bhuta

The typical usage pattern for OpenSessionInViewFilter is that some Entity is lazily loaded but during the view rendering phase the view needs some attribute of this Entity that was not loaded initially thus necessitating the need to fetch this data from the database. Now typically the transaction demarcation is made to happen in the service layer of your web application so by the time the view rendering takes place the view is working with a detached entity which results in a LazyInitializationExceptionwhen accessing the unloaded attribute.

OpenSessionInViewFilter 的典型使用模式是某些实体被延迟加载,但在视图呈现阶段,视图需要该实体的某些最初未加载的属性,因此需要从数据库中获取此数据。现在,事务划分通常发生在您的 Web 应用程序的服务层中,因此当视图呈现发生时,视图正在与分离的实体一起工作,这会导致LazyInitializationException访问未加载的属性时发生。

From this url https://developer.jboss.org/wiki/OpenSessionInView:

从这个网址https://developer.jboss.org/wiki/OpenSessionInView

The problem
A common issue in a typical web-application is the rendering of the view, after the main logic of the action has been completed, and therefore, the Hibernate Session has already been closed and the database transaction has ended. If you access detached objects that have been loaded in the Session inside your JSP (or any other view rendering mechanism), you might hit an unloaded collection or a proxy that isn't initialized. The exception you get is: LazyInitializationException: Session has been closed (or a very similar message). Of course, this is to be expected, after all you already ended your unit of work.

A first solution would be to open another unit of work for rendering the view. This can easily be done but is usually not the right approach. Rendering the view for a completed action is supposed to be inside the first unit of work, not a separate one. The solution, in two-tiered systems, with the action execution, data access through the Session, and the rendering of the view all in the same virtual machine, is to keep the Session open until the view has been rendered.

问题
典型的 web 应用程序中的一个常见问题是视图的呈现,在操作的主要逻辑完成后,因此 Hibernate Session 已经关闭,数据库事务已经结束。如果您访问已在 JSP(或任何其他视图呈现机制)内的 Session 中加载的分离对象,您可能会遇到未加载的集合或未初始化的代理。您得到的异常是: LazyInitializationException: Session has been closed(或非常相似的消息)。当然,这是意料之中的,毕竟您已经结束了您的工作单元。

第一个解决方案是打开另一个工作单元来呈现视图。这很容易做到,但通常不是正确的方法。渲染完成动作的视图应该在第一个工作单元内,而不是一个单独的工作单元。解决方案,在两层系统中,动作执行、通过 Session 的数据访问以及视图的渲染都在同一个虚拟机中,是保持 Session 打开,直到视图被渲染。

As an alternative, consider loading the Entity with just the right amount of data required by your view. This can be accomplished by using DTO projections. This article lists some of the downsides of using the Open Session In View pattern : https://vladmihalcea.com/the-open-session-in-view-anti-pattern/

作为替代方案,请考虑使用视图所需的适量数据加载实体。这可以通过使用 DTO 投影来完成。本文列出了使用 Open Session In View 模式的一些缺点:https: //vladmihalcea.com/the-open-session-in-view-anti-pattern/