Why is Hibernate Open Session in View considered a bad practice?

后端 未结 9 2076
轮回少年
轮回少年 2020-11-22 17:08

And what kind of alternative strategies do you use for avoiding LazyLoadExceptions?

I do understand that open session in view has issues with:

  • Layered
相关标签:
9条回答
  • 2020-11-22 17:32

    I am v. rusty on Hibernate.. but I think its possible to have multiple transactions in one Hibernate session. So your transaction boundaries do not have to be the same as session start/stop events.

    OSIV, imo, primarily is useful because we can avoid writing code for starting a 'persistence context' (a.k.a. session) every time the request needs to make a DB access.

    In your service layer, you will probably need to make calls to methods which have different transaction needs, such as 'Required, New Required, etc.' The only thing these methods need is that somebody (i.e the OSIV filter) has started up the persistence context, so that only thing they have to worry about is - "hey give me the hibernate session for this thread.. I need to do some DB stuff".

    0 讨论(0)
  • 2020-11-22 17:33

    For a longer description, you can read my Open Session In View Anti-Pattern article. Otherwise, here's a summary for why you shouldn't use Open Session In View.

    Open Session In View takes a bad approach to fetching data. Instead of letting the business layer decide how it’s best to fetch all the associations that are needed by the View layer, it forces the Persistence Context to stay open so that the View layer can trigger the Proxy initialization.

    • The OpenSessionInViewFilter calls the openSession method of the underlying SessionFactory and obtains a new Session.
    • The Session is bound to the TransactionSynchronizationManager.
    • The OpenSessionInViewFilter calls the doFilter of the javax.servlet.FilterChain object reference and the request is further processed
    • The DispatcherServlet is called, and it routes the HTTP request to the underlying PostController.
    • The PostController calls the PostService to get a list of Post entities.
    • The PostService opens a new transaction, and the HibernateTransactionManager reuses the same Session that was opened by the OpenSessionInViewFilter.
    • The PostDAO fetches the list of Post entities without initializing any lazy association.
    • The PostService commits the underlying transaction, but the Session is not closed because it was opened externally.
    • The DispatcherServlet starts rendering the UI, which, in turn, navigates the lazy associations and triggers their initialization.
    • The OpenSessionInViewFilter can close the Session, and the underlying database connection is released as well.

    At a first glance, this might not look like a terrible thing to do, but, once you view it from a database perspective, a series of flaws start to become more obvious.

    The service layer opens and closes a database transaction, but afterward, there is no explicit transaction going on. For this reason, every additional statement issued from the UI rendering phase is executed in auto-commit mode. Auto-commit puts pressure on the database server because each statement must flush the transaction log to disk, therefore causing a lot of I/O traffic on the database side. One optimization would be to mark the Connection as read-only which would allow the database server to avoid writing to the transaction log.

    There is no separation of concerns anymore because statements are generated both by the service layer and by the UI rendering process. Writing integration tests that assert the number of statements being generated requires going through all layers (web, service, DAO), while having the application deployed on a web container. Even when using an in-memory database (e.g. HSQLDB) and a lightweight web server (e.g. Jetty), these integration tests are going to be slower to execute than if layers were separated and the back-end integration tests used the database, while the front-end integration tests were mocking the service layer altogether.

    The UI layer is limited to navigating associations which can, in turn, trigger N+1 query problems. Although Hibernate offers @BatchSize for fetching associations in batches, and FetchMode.SUBSELECT to cope with this scenario, the annotations are affecting the default fetch plan, so they get applied to every business use case. For this reason, a data access layer query is much more suitable because it can be tailored for the current use case data fetch requirements.

    Last but not least, the database connection could be held throughout the UI rendering phase(depending on your connection release mode) which increases connection lease time and limits the overall transaction throughput due to congestion on the database connection pool. The more the connection is held, the more other concurrent requests are going to wait to get a connection from the pool.

    So, either you get the connection held for too long, either you acquire/release multiple connections for a single HTTP request, therefore putting pressure on the underlying connection pool and limiting scalability.

    Spring Boot

    Unfortunately, Open Session in View is enabled by default in Spring Boot.

    So, make sure that in the application.properties configuration file, you have the following entry:

    spring.jpa.open-in-view=false
    

    This will disable OSIV, so that you can handle the LazyInitializationException the right way.

    0 讨论(0)
  • 2020-11-22 17:35

    Because sending possibly uninitialised Proxies, especially collections, in the view layer and triggering hibernate loading from there can be troubling from both a performance and understanding point of view.

    Understanding:

    Using OSIV 'pollutes' the view layer with concerns related to the data access layer.

    The view layer is not prepare to handle a HibernateException which may happen when lazy loading, but presumably the data access layer is.

    Performance:

    OSIV tends to tug proper entity loading under the carpet - you tend not to notice that your collections or entities are lazily initialised ( perhaps N+1 ). More convenience, less control.


    Update: see The OpenSessionInView antipattern for a larger discussion regarding this subject. The author lists three important points:

    1. each lazy initialization will get you a query meaning each entity will need N + 1 queries, where N is the number of lazy associations. If your screen presents tabular data, reading Hibernate’s log is a big hint that you do not do as you should
    2. this completely defeats layered architecture, since you sully your nails with DB in the presentation layer. This is a conceptual con, so I could live with it but there is a corollary
    3. last but not least, if an exception occurs while fetching the session, it will occur during the writing of the page: you cannot present a clean error page to the user and the only thing you can do is write an error message in the body
    0 讨论(0)
  • 2020-11-22 17:35

    I just did a post on some guidelines as to when to use open session in view in my blog. Check it out if your interested.

    http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/

    0 讨论(0)
  • 2020-11-22 17:38

    This won't help too much but you can check my topic here: * Hibernate Cache1 OutOfMemory with OpenSessionInView

    I have some OutOfMemory issues because of OpenSessionInView and a lot of entities loaded, because they stay in Hibernate cache level1 and are not garbage collected (i load a lot of entities with 500 items per page, but all entities stay in cache)

    0 讨论(0)
  • 2020-11-22 17:45

    If you're using an Inversion of Control (IoC) container such as Spring, you may want to read up on bean scoping. Essentially, I'm telling Spring to give me a Hibernate Session object whose life cycle spans the entire request (i.e., it gets created and destroyed at the start and end of the HTTP request). I don't have to worry about LazyLoadExceptions nor closing the session since the IoC container manages that for me.

    As mentioned, you will have to think about N+1 SELECT performance issues. You can always configure your Hibernate entity afterwards to do eager join loading in places where performance is an issue.

    The bean scoping solution is not a Spring-specific. I know PicoContainer offers the same capability and I'm sure other mature IoC containers offer something similar.

    0 讨论(0)
提交回复
热议问题