Reputation: 8585
I saw spring.jpa.open-in-view=true
property in Spring Boot documentation for JPA configuration.
true
default value for this property if it's not provided at all?;SessionFactory
instead of EntityManagerFactory
? If yes, how can I tell it to allow me to use EntityManagerFactory
instead?Thanks!
Upvotes: 316
Views: 321516
Reputation: 101
If you leave the spring.jpa.open-in-view=true property at its default (true), the database connection remains open until the controller returns a response. This can be unnecessary and might lead to performance issues. Unless you have a specific reason, it's recommended to set this property to false. By doing so, the connection will close right after the query execution.
Upvotes: 0
Reputation: 41220
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.
Upvotes: 5
Reputation: 153690
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.
OpenSessionInViewFilter
calls the openSession
method of the underlying SessionFactory
and obtains a new Session
.Session
is bound to the TransactionSynchronizationManager
.OpenSessionInViewFilter
calls the doFilter
of the javax.servlet.FilterChain
object reference and the request is further processedDispatcherServlet
is called, and it routes the HTTP request to the underlying PostController
.PostController
calls the PostService
to get a list of Post
entities.PostService
opens a new transaction, and the HibernateTransactionManager
reuses the same Session
that was opened by the OpenSessionInViewFilter
.PostDAO
fetches the list of Post
entities without initializing any lazy association.PostService
commits the underlying transaction, but the Session
is not closed because it was opened externally.DispatcherServlet
starts rendering the UI, which, in turn, navigates the lazy associations and triggers their initialization.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.
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.
Upvotes: 612
Reputation: 44515
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.
Upvotes: 107