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

10

127

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

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

  • Layered applications running in different jvm's
  • Transactions are committed only at the end, and most probably you would like the results before.

But, if you know that your application is running on a single vm, why not ease your pain by using an open session in view strategy?

Pothead answered 9/7, 2009 at 11:49 Comment(6)
Is OSIV considered a bad practice? By whom?Glycosuria
And - what are good alternatives?Demeter
This peace of text if from seam developers: There are several problems with this implementation, the most serious being that we can never be sure that a transaction is successful until we commit itbut by the time the "open session in view" transaction is committed, the view is fully rendered, and the rendered response may already have been flushed to the client. How can we notify the user that their transaction was unsuccessful?Sherrill
and here is the link: redhat.com/docs/manuals/jboss/jboss-eap-4.2/doc/seam/…Sherrill
See this blog post for pros and cons and my own experience about it - blog.jhades.org/open-session-in-view-pattern-pros-and-consRazz
I see one problem but not sure OSIV causing the issue, in my application i am getting Lazy Initialization error when using multi Node server with load balancer. The same code is working properly with one server but the time i add one more server it start giving Lazy Initialization error randomlyUncial
D
57

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.

enter image description here

  • 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, by fetching all needed association while the EntityManager is open.

Dossal answered 30/5, 2016 at 13:3 Comment(13)
Using Open Session in View with auto-commit is possible but not the way it was intended by the Hibernate developers. So although Open Session in View does have its drawbacks, auto-commit is not one because you can simply turn it off and still use it.Onset
You're talking about what happens inside a transaction, and that's true. But the Web Layer rendering phase happens outside of Hibernate, hence you get the autocommit mode. Makes sense?Dossal
I think that's a variant that is not the optimal one for Open Session in View. The session and the transaction should remain open until the view has been rendered, then there is no need for autocommit mode.Onset
The session remains open. But the transaction does not. Spanning the transaction over the whole process is not optimal either since it increases its length and locks are held for longer than necessary. Imagine what happens if the view throws a RuntimeException. Will the transaction rollback because the UI rendering failed?Dossal
Thank you very much for very detailed answer! I'd only change the guide in the end since spring boot users likely won't use jpa in that way.Booted
You are very welcome. If Spring Boot users would not use OSIB, this StackOverflow answer would not have more than 100 upvotes.Dossal
@VladMihalcea so did I get it correctly:Servlet filter will get one db.connection,transactions are honored (created on annotated methods based on JPA propagation...),but persistence context is still held even code from trx annotated methods ends?And every lazy loading (which would otherwise results in NotInitializedException) will use autocommit to fetch?So with one connection(which is freed - or returned to pool after request)there can be more autocommits to fetch everything desired? If yes, that would mean that also any changes outside trx on entities in context would be persisted too? ThxTamasha
Any change to those entities will be ignored because the Persistence Context is never flushed. If it were, an exception would be thrown as no JPA transaction is active.Dossal
Although i agree OSIV is not the most ideal solution, your proposed work around negates the benefits of an ORM like hibernate. The point of an ORM is to accelerate the developer experience and asking developers to move back to writing JPA queries when fetching linked properties does the exact opposite. The Spring have got it right here by enabling OSIV by default and including logging to notify the developer that this has been configured.Indre
Well, you got it all wrong. Just because Hibernate can generate CRUD statement, it doesn't mean the application developer shouldn't use queries. In fact, JPA and SQL queries are not the exception, but the rule. Spring is a great framework, but enabling OSIV by default is harmful.Dossal
@VladMihalcea This is a quote from official Hibernate documentation: "Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC". Now, you are saying JPA and SQL queries are not the exception, but the rule. I find these two statements contradictory. BTW, I have nothing against your answer, you have listed the ins and outs quite well. Though, I believe they should correct 95% to something like 70% in the documentation:)Rockling
@VladMihalcea Do you have a good approach to remove OSIV from the project? Is it always a "big change in one step" or is there a step by step approach?Pyrometallurgy
@Pyrometallurgy It's a step-by-step process. When I have to improve a legacy system, I use my Hypersistence Optimizer to discover and fix the remaining services that need to switch to JOIN FETCH before I disable OSIV.Dossal
L
47

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
Leandra answered 9/7, 2009 at 11:52 Comment(6)
Ok, it 'pollutes' the view layer with hibernate exception. But, regarding performance, i think the problem is quite similar than to access a service layer that will return your dto. If you face a performance problem, then you should optimize that specific issue with a smarter query or a more lightweight dto. If you have to develop too many service methods to handle possibilities you could need in the view, you are also 'polluting' the service layer. no?Pothead
One difference is that it delays the closing of the Hibernate session. You will wait for the JSP to be rendered/written/etc, and that keeps the objects in memory longer. That may be a problem especially if you need to write data on session commit.Leandra
It doesn't make sense to say that OSIV hurts Performance. What alternatives are there except for using DTOs? In that case, you will always have lower performance because data used by any view will have to be loaded even for views that don't need it.Glycosuria
I think the pollution works the other way round. If I need to eager load the data, the logic layer (or worse the data access layer) need to know in which way an object is going to be displayed. Change the view and you end up loading stuff you don't need or missing objects you need. A Hibernate Exception is a bug and just as poisoning as any other unexpected exception. But performance is an issue. Performance and scalability issues will force you to put more thought and work in your data access layer, and possibly force the session to be closed earlierSarcomatosis
@JensSchauder "Change the view and you end up loading stuff you don't need or missing objects you need". This is exactly it. If you change the view, it is much better to load stuff you don't need (as you are more likely to be eager fetching them) or figure out missing objects as you would get the Lazy loading exception, than to let the view load it lazily as that will result in the N+1 problem, and you won't even know it is happening. So IMO its better the service layer (and you) know what it is sent out than the view loading lazily and you knowing nothing about it.Alixaliza
There are a few really good counter-arguments on that OSIV anti-pattern link; anybody reading this should read through those as well.Uropygium
O
24
  • transactions can be committed in the service layer - transactions are not related to OSIV. It's the Session that stays open, not a transaction - running.

  • if your application layers are spread across multiple machines, then you pretty much can't use OSIV - you have to initialize everything you need before sending the object over the wire.

  • OSIV is a nice and transparent (i.e. - none of your code is aware that it happens) way to make use of the performance benefits of lazy loading

Ophite answered 21/9, 2010 at 12:44 Comment(2)
Regarding the first bullet point, this is at least not true for the original OSIV from the JBoss wiki, it also handles transaction demarcation around the request.Calorific
@PascalThivent Which part made you think so?Nash
C
14

I wouldn't say that Open Session In View is considered a bad practice; what gives you that impression?

Open-Session-In-View is a simple approach to handling sessions with Hibernate. Because it's simple, it's sometimes simplistic. If you need fine-grained control over your transactions, such as having multiple transactions in a request, Open-Session-In-View is not always a good approach.

As others have pointed out, there are some trade-offs to OSIV -- you're much more prone to the N+1 problem because you're less likely to realize what transactions you're kicking off. At the same time, it means you don't need to change your service layer to adapt to minor changes in your view.

Chemarin answered 21/7, 2009 at 15:57 Comment(0)
L
5

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.

Load answered 20/7, 2009 at 11:44 Comment(1)
Do you have a pointer to an actual implementation of Hibernate sessions being made available in the view via request scoped beans?Unpremeditated
C
4

In my own experience, OSIV is not so bad. The only arrangement I made is using two different transactions: - the first, opened in "service layer", where I have the "business logic" - the second opened just before the view rendering

Coheir answered 13/10, 2010 at 14:40 Comment(0)
M
3

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/

Minna answered 4/4, 2010 at 1:12 Comment(2)
As a general SO rule of thumb, if you are providing an answer, it's best to do more than just link elsewhere. Perhaps provide one or two sentences or listed items giving the gist. It's okay to link, but you want to provide a little extra value. Otherwise, you might want merely to comment and put the link there.Oria
the link in this answer is worth reading, it provides a good guidance on when to use OSIV and notAgatha
C
1

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".

Capitol answered 2/11, 2009 at 18:25 Comment(0)
D
1

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)

Deeprooted answered 21/1, 2011 at 13:58 Comment(1)
If you're loading that much stuff into the L1 cache, your problem is not OSIV, its that you designed something dumb.Breeden
T
1

TL; DR

Previous answer was inconvenience for my understanding about real practical reasons to avoid OSIV

OSIV is an Anti pattern, practical common reasons ?

The OSIV anti-pattern arises when the database session is kept open for the entire duration of the request-response cycle (which in most Contexts people using the Thread-per Request Model)which means that the session remains open during the request lifecycle. The intention behind OSIV is to allow lazy loading of entity associations within the view and using the session on demand and when it needed to access lazy loaded entities.

  • Concurrency: When the session remains open, it can lead to potential concurrency issues. If multiple requests are handled simultaneously, they may end up sharing the same session, leading to unpredictable behavior and data integrity problems.
  • Database Performance Impacts: Keeping the database session open for a longer duration than necessary can lead to increased resource usage and decreased performance.
  • Transaction Lifecycle management: With OSIV, the transaction boundaries are often getting lost, as the session remains open throughout the entire request-response cycle. This make it difficult to manage transactions effectively and can result in issues like uncommitted or long-running transactions so it can affect datasource connections limits and eventually all transaction can be in 'idle in transaction" state
Tantalous answered 7/8, 2023 at 10:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.