271

I saw spring.jpa.open-in-view=true property in Spring Boot documentation for JPA configuration.

  • Is the true default value for this property if it's not provided at all?;
  • What does this really do? I did not find any good explaining for it;
  • Does it make you use SessionFactory instead of EntityManagerFactory? If yes, how can I tell it to allow me to use EntityManagerFactory instead?

Thanks!

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
Carlos Alberto
  • 7,761
  • 13
  • 52
  • 72

4 Answers4

554

The OSIV Anti-Pattern

Instead of letting the business layer decide how it’s best to fetch all the associations that are needed by the View layer, OSIV (Open Session in View) forces the Persistence Context to stay open so that the View layer can trigger the Proxy initialization, as illustrated by the following diagram.

OSIV Anti-Pattern

  • 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 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 transaction issues a commit at end, which can trigger a transaction log flush to disk. 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 webserver (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 to the current use case data fetch requirements.

Last but not least, the database connection is held throughout the UI rendering phase 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.

Spring Boot and OSIV

Unfortunately, OSIV (Open Session in View) is enabled by default in Spring Boot, and OSIV is really a bad idea from a performance and scalability perspective.

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.

Starting with version 2.0, Spring Boot issues a warning when OSIV is enabled by default, so you can discover this problem long before it affects a production system.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • 38
    There's a WARNING being logged nowadays. – Vlad Mihalcea Aug 17 '18 at 15:23
  • Does this apply to Spring in general, or only Spring Boot? Can this be disabled via a @Configuration-annotated class instead of setting a property? – Gordon Nov 23 '19 at 20:55
  • 2
    It isn't an anti-pattern. It does have performance impacts, sometimes negative, a lot of times quite neutral, and in a positive way in a lot of cases: if you actually want a lazy relation to begin with, you don't need to do the query in all cases and can avoid it when needed by using open-in-view. – ymajoros Jan 31 '20 at 08:19
  • 3
    Just in case you decide to add `spring.jpa.open-in-view=false`, make sure to properly restart your spring-boot-application rather than just having it auto-reload. If you do the latter, you will be notified of an already existing open-in-view `@ConditionalOnProperty`. See https://www.yawintutor.com/conditionalonproperty-found-different-open-in-view/. Restarting the server manually helped in my case. – Igor Feb 11 '20 at 19:11
  • @Igor I don't have any view layer in my spring boot application. That means the application has only bunch of rest controllers. And I also checked that the `OpenEntityManagerInViewInterceptor` has never been initialised. Does that mean that the when the application serve the html that time only this bean has created ? – seal May 29 '20 at 22:31
  • @seal since I don't exactly know what you mean, your best bet might be a new dedicated question. I remember solving my issue by restarting the server manually rather than relying on auto-reload. I'm sorry I can't help you more. – Igor May 30 '20 at 11:05
  • 5
    Not convinced, at all. Personally, I don't really like OSIV, but the reasons given here are just false. 1) "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.", no, the RDBMS is not so dumb - this claim is given without any evidence, and from experience I know it doesn't occurr. 2) "There is no separation of concerns anymore", non-sense, since those JDBC statements are generated transparently and implicitly; ... – Rogério Aug 24 '20 at 18:02
  • 1
    ... 3) N+1 problem only occurs if developers don't know what they are doing - usually loading lazy collections from the UI is fine, and can/should be avoided by explicit fetching where needed. 4) "connection lease time ... limits overall transaction throughput", also not a real problem, unless you have an application with more concurrent requests than OSIV can handle (and this often is not the case for business apps), for which there are several solutions. In short, this answer only speculates on potential problems which will rarely, if ever, occur in practice, while providing no alternative. – Rogério Aug 24 '20 at 18:09
  • 9
    1) You're wrong, of course. Every Tx will issue a commit that will be processed by the DB. So, it's an extra overhead since, in 2PL, read-locks must be released while in MVCC, SI snapshots can be discarded. This is DB 101. 2) Again, wrong. JDBC has nothing to do with it. It's about which layer controls the transaction boundaries. 3) Wrong again. N+1 can occur via OSIV or FetchType.EAGER, not just lazy collections. 4) Again, wrong. This one is proven mathematically by the [Universal Scalability Law](http://www.perfdynamics.com/Manifesto/USLscalability.html). You should read it too ;) – Vlad Mihalcea Aug 25 '20 at 05:51
  • 1
    @VladMihalcea About the point 1): If the entity is fetched in a service method annotated with "@Transactional", read-locks will also be released in a Tx. So, with or without OSIV, that will occur. So, I do not get the difference here. Can you please assist? Is it more performant to release multiple read locks in one transaction than releasing individually in separate transactions? – sanemain Jan 12 '21 at 13:47
  • Encountered a connection pool starvation issue because of OSIV: https://github.com/spring-projects/spring-boot/issues/26638 – cdalxndr May 22 '21 at 21:59
  • You copy and paste the same in every question about the OSIV. Why? – Lluis Martinez Oct 13 '22 at 18:54
94

This property will register an OpenEntityManagerInViewInterceptor, which registers an EntityManager to the current thread, so you will have the same EntityManager until the web request is finished. It has nothing to do with a Hibernate SessionFactory etc.

Michael Lihs
  • 7,460
  • 17
  • 52
  • 85
dunni
  • 43,386
  • 10
  • 104
  • 99
  • At the moment I have the filter OpenEntityManagerInViewFilter to control the EntityManager until the web request is finished. This interceptor you meant "OpenEntityManagerInViewInterceptor" is the same that "OpenEntityManagerInViewFilter"? What´s the difference between them? So, I would not have more this filter in my servlet context for Spring Boot? – Carlos Alberto Jun 01 '15 at 14:13
  • 2
    The interceptor only works, when you use the DispatcherServlet in Spring (because the interceptor is a Spring mechanism). The filter can be mapped to all configured servlets (we use it for the FacesServlet in one of our applications). So if you only use the DispatcherServlet, you can add the property and remove the filter, otherwise use the filter. – dunni Jun 01 '15 at 14:21
18

Probably is late, but I was trying to dig more about the implications of turning it OFF vs. ON, and I found this article useful spring-open-session-in-view

Hope this can help somebody...

Rogelio Blanco
  • 1,462
  • 4
  • 19
  • 29
0

The one reason for open-in-view (for OSIV) is for developer productivity as it eliminates the need to explicitly load all their lazily loaded properties.

https://stackoverflow.com/a/76864391/242042 shows a way of doing that explicit load. With open-in-view off to prevent the LazyInitializationException.

However, it makes every controller mapping that does a JPA operation start a transaction and thus may cause exhaustion on the database connections.

Another way (also considered an anti-pattern) is to tell Hibernate to allow lazy loading without a transaction by adding.

spring:
  jpa:
    properties:
      hibernate.enable_lazy_load_no_trans: true

In terms of developer productivity it is the same, but in terms of performance this may be slightly better as it will only start the transaction if the object needs to lazily load after the fact.

Obviously the correct approach would be to do join fetch or @EntityGraph but that adds to the complexity for the developer. But that can be a performance improvement afterwards.

In my opinion use the hibernate.enable_lazy_load_no_trans and turn off open-in-view but find places where the enable_lazy_load_no_trans capability is triggered (ideally via a log) and correct it.

Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265