为什么不使用Spring的OpenEntityManagerInViewFilter [英] Why not to use Spring's OpenEntityManagerInViewFilter

查看:92
本文介绍了为什么不使用Spring的OpenEntityManagerInViewFilter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

虽然在Spring的OpenSession / EntityManagerInViewFilter的主题上写了很多帖子,但我找不到任何提及它的缺陷。根据我的理解,假设使用@Transactional服务层的典型分层Web应用程序体系结构,过滤器的工作原理如下:


  1. 过滤器拦截一个servlet请求

  2. 过滤器打开一个EntityManager并将其绑定到当前线程
  3. Web控制器被调用

  4. Web控制器调用服务

  5. 事务拦截器开始一个新事务,检索线程绑定的EntityManager并将其绑定到事务上。调用EntityManager,然后返回

  6. 事务拦截器刷新EntityManager然后提交事务

  7. Web控制器准备视图,然后返回
  8. li>
  9. 创建View

  10. 过滤器关闭EntityManager并从当前线程中解除绑定

在步骤8和9中,由线程的EntityManager加载的对象仍处于管理状态。因此,如果在这些步骤中接触到惰性关联,则会使用仍然打开的EntityManager从数据库加载它们。据我所知,每个这样的访问都需要数据库打开一个事务。 Spring的事务管理将不会意识到这一点,因此我称它为隐式事务。

我看到2个问题:


  1. 加载多个延迟关联会导致多个数据库事务,这可能会影响性能

  2. 根对象及其惰性关联会加载到不同的数据库事务,所以数据可能是陈旧的(例如,线程1加载的根,线程2更新的根联合,线程1加载的根联合)


    一方面,这两个问题似乎足以拒绝使用此过滤器(性能命中,数据不一致)。另一方面,这个解决方案非常方便,避免编写几行代码,问题1可能不那么明显,问题2可能是纯粹的偏执狂。

    什么做你认为?



    谢谢!

    解决方案

    正如您所说, OpenSessionInView过滤器在Web应用程序中非常方便。关于你提到的限制:

    lockquote
    1)加载几个惰性关联会导致多个数据库事务,这可能会影响性能。


    是的,进入数据库往往会导致性能问题。理想情况下,您想在一次旅行中获取所需的所有数据。考虑使用Hibernate join-fetch来做到这一点。但从数据库中获取太多数据也会很慢。我使用的经验法则是如果每次绘制视图时都需要数据,则使用联接抓取;如果在大多数情况下不需要数据,我会让Hibernate在我需要时懒惰地获取它 - threadlocal打开会话可以帮助那么。


    2)根对象及其惰性关联会加载到不同的数据库事务中,因此数据可能会过时(例如,根由线程1加载,根关联由线程2更新,根关联由线程1加载)。


    想象一下,在JDBC中编写这个应用程序 - 如果应用程序的一致性需求要求根和叶子都应该加载在同一个txn中,请使用联接提取。如果没有,通常情况下,延迟获取不会导致任何一致性问题。



    恕我直言,OpenSessionInView的更重要的缺点是当您想要服务层在非web上下文中重用。从你的描述看,你似乎没有这个问题。


    While a lot of posts have been written on the subject of Spring's OpenSession/EntityManagerInViewFilter, I couldn't find any that mentions its flaws. From what I understand, and assuming a typical layered web application architecture using a @Transactional service layer, the filter works as follows:

    1. The filter intercepts a servlet request
    2. The filter opens an EntityManager and binds it to the current thread
    3. Web controller is called
    4. Web controller calls service
    5. Transaction interceptor begins a new transaction, retrieves the thread-bound EntityManager and binds it to the transaction
    6. Service is called, does some stuff with EntityManager, then returns
    7. Transaction interceptor flushes the EntityManager then commits the transaction
    8. Web controller prepares view, then returns
    9. View is built
    10. Filter closes the EntityManager and unbinds it from current thread

    In steps 8 and 9, objects that were loaded by the thread's EntityManager are still managed. Consequently, if lazy associations are touched in these steps, they will be loaded from the database using the still open EntityManager. From what I understand, each such access will require that the database open a transaction. Spring's transaction management will be unaware of this, hence my calling it "implicit transaction".

    I see 2 problems with this:

    1. Loading several lazy associations will result in multiple database transactions, a possible hit on performance
    2. The root object and its lazy associations are loaded in different database transactions, so the data may possibly be stale (e.g. root loaded by thread 1, root associations updated by thread 2, root associations loaded by thread 1)

    On the one hand, these 2 issues seem enough to reject using this filter (performance hit, data inconsistency). On the other hand, this solution is very convenient, avoids writing several lines of code, problem 1 may not be that noticeable and problem 2 may be pure paranoia.

    What do you think?

    Thanks!

    解决方案

    As you said, the OpenSessionInView filter is very convenient in web applications. Regarding the limitations you mentioned:

    1) Loading several lazy associations will result in multiple database transactions, a possible hit on performance.

    Yes, going to the DB often might lead to performance problems. Ideally you want to fetch all the data you need in one trip. Consider using Hibernate join-fetch for this. But fetching too much data from the DB will also be slow. The rule of thumb I use is to use join fetching if the data is needed every time I paint the view; if the data is not needed in most cases, I let Hibernate lazy fetch it when I need it - the threadlocal open session helps then.

    2) The root object and its lazy associations are loaded in different database transactions, so the data may possibly be stale (e.g. root loaded by thread 1, root associations updated by thread 2, root associations loaded by thread 1).

    Imagine writing this application in JDBC - if the application's consistency requirements demand that the root and leaves both should be loaded in the same txn, use join fetching. If not, which is often the case, lazy fetching won't lead to any consistency problems.

    IMHO, the more important disadvantage with OpenSessionInView is when you want your service layer to be reused in a non-web context. From your description, you don't seem to have that problem.

    这篇关于为什么不使用Spring的OpenEntityManagerInViewFilter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆