View의 Hibernate Open Session이 나쁜 습관으로 간주되는 이유는 무엇입니까?
그리고 LazyLoadExceptions를 피하기 위해 어떤 종류의 대체 전략을 사용합니까?
보기에서 열린 세션에 다음과 같은 문제가 있음을 이해합니다.
- 다른 jvm에서 실행되는 계층화 된 애플리케이션
- 트랜잭션은 마지막에만 커밋되며 아마도 이전 결과를 원할 것입니다.
그러나 애플리케이션이 단일 VM에서 실행되고 있다는 것을 알고 있다면 뷰 전략에서 열린 세션을 사용하여 고통을 덜어 보는 것은 어떻습니까?
초기화되지 않은 프록시, 특히 컬렉션을 뷰 레이어에서 보내고 거기에서 최대 절전 모드로드를 트리거하는 것은 성능과 이해의 관점에서 문제가 될 수 있기 때문입니다.
이해 :
OSIV를 사용하면 데이터 액세스 계층과 관련된 문제로 뷰 계층이 '오염'됩니다.
보기 계층은 HibernateException
지연로드시 발생할 수있는를 처리 할 준비가되어 있지 않지만 데이터 액세스 계층은 아마도 그렇습니다.
성능 :
OSIV는 카펫 아래에 적절한 엔티티 로딩을 잡아 당기는 경향이 있습니다. 컬렉션이나 엔티티가 느리게 초기화된다는 것을 알지 못하는 경향이 있습니다 (아마도 N + 1). 더 많은 편리함, 더 적은 제어.
업데이트 : 이 주제에 대한 더 큰 논의 는 OpenSessionInView 안티 패턴 을 참조하십시오 . 저자는 세 가지 중요한 사항을 나열합니다.
- 각 지연 초기화는 각 엔티티에 N + 1 개의 쿼리가 필요함을 의미하는 쿼리를 가져옵니다. 여기서 N은 지연 연결의 수입니다. 화면에 표 형식의 데이터가 표시되는 경우 Hibernate의 로그를 읽는 것은해야 할 일이 아니라는 큰 힌트입니다.
- 이것은 프레젠테이션 레이어에서 DB로 손톱을 더럽 히기 때문에 레이어드 아키텍처를 완전히 무력화합니다. 이것은 개념적인 사기입니다. 그래서 나는 그것과 함께 살 수 있지만 그에 따른 결과가 있습니다.
- 마지막으로, 세션을 가져 오는 동안 예외가 발생하면 페이지를 쓰는 동안 예외가 발생합니다. 사용자에게 깨끗한 오류 페이지를 제공 할 수 없으며 본문에 오류 메시지를 작성하는 것뿐입니다.
더 자세한 설명은 Anti-Pattern보기에서 내 세션 열기 문서를 참조하세요. 그렇지 않은 경우 다음은보기에서 세션 열기를 사용하지 않아야하는 이유에 대한 요약입니다.
보기에서 세션 열기는 데이터를 가져 오는 데 잘못된 접근 방식을 사용합니다. 비즈니스 계층이 View 계층에 필요한 모든 연결을 가져 오는 것이 가장 좋은 방법을 결정하도록하는 대신 View 계층이 프록시 초기화를 트리거 할 수 있도록 Persistence Context를 강제로 열어 둡니다.
- 가
OpenSessionInViewFilter
부르는openSession
기본 방법을SessionFactory
새로운를 가져옵니다Session
. - 는
Session
에 바인딩됩니다TransactionSynchronizationManager
. - 는
OpenSessionInViewFilter
부르는doFilter
의javax.servlet.FilterChain
오브젝트 레퍼런스 및 요청이 더 처리 - 이
DispatcherServlet
라고하며 기본에이를 라우팅 HTTP 요청한다PostController
. PostController
통화량은PostService
목록을 얻을 수Post
개체를.- The
PostService
opens a new transaction, and theHibernateTransactionManager
reuses the sameSession
that was opened by theOpenSessionInViewFilter
. - The
PostDAO
fetches the list ofPost
entities without initializing any lazy association. - The
PostService
commits the underlying transaction, but theSession
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 theSession
, 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.
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
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.
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 LazyLoadException
s 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.
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
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/
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".
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)
'programing' 카테고리의 다른 글
Git pull / push-HTTPS에 액세스 할 수 없음, SSL 루틴이 다운 된 것 같습니다. (0) | 2020.08.15 |
---|---|
firefox 동일 출처 정책 비활성화 (0) | 2020.08.15 |
풀에서 JDBC 연결 닫기 (0) | 2020.08.15 |
C에서 배열과 배열 포인터를 함수에 전달하는 것의 차이점 (0) | 2020.08.15 |
UnicodeDecodeError : 'ascii'코덱이 위치 1의 바이트 0xef를 디코딩 할 수 없습니다. (0) | 2020.08.15 |