그리고 LazyLoadExceptions를 피하기 위해 어떤 종류의 대체 전략을 사용합니까?
보기에서 열린 세션에 다음과 같은 문제가 있음을 이해합니다.
- 다른 jvm에서 실행되는 계층화 된 애플리케이션
- 트랜잭션은 마지막에만 커밋되며 아마도 이전 결과를 원할 것입니다.
그러나 애플리케이션이 단일 VM에서 실행되고 있다는 것을 알고 있다면보기 전략에서 열린 세션을 사용하여 고통을 덜어 보는 것은 어떨까요?
그리고 LazyLoadExceptions를 피하기 위해 어떤 종류의 대체 전략을 사용합니까?
보기에서 열린 세션에 다음과 같은 문제가 있음을 이해합니다.
그러나 애플리케이션이 단일 VM에서 실행되고 있다는 것을 알고 있다면보기 전략에서 열린 세션을 사용하여 고통을 덜어 보는 것은 어떨까요?
답변:
초기화되지 않은 프록시, 특히 컬렉션을 뷰 레이어에서 보내고 거기에서 최대 절전 모드로드를 트리거하는 것은 성능과 이해의 관점 모두에서 문제가 될 수 있기 때문입니다.
이해 :
OSIV를 사용하면 데이터 액세스 계층과 관련된 문제로 뷰 계층이 '오염'됩니다.
보기 계층은 HibernateException
지연로드시 발생할 수있는를 처리 할 준비가되어 있지 않지만 데이터 액세스 계층은 아마도 그렇습니다.
성능 :
OSIV는 카펫 아래에 적절한 엔티티 로딩을 잡아 당기는 경향이 있습니다. 컬렉션이나 엔티티가 느리게 초기화된다는 것을 알지 못하는 경향이 있습니다 (아마도 N + 1). 더 많은 편리함, 더 적은 제어.
업데이트 : 이 주제에 대한 더 큰 논의 는 OpenSessionInView 안티 패턴 을 참조하십시오 . 저자는 세 가지 중요한 사항을 나열합니다.
- 각 지연 초기화는 각 엔티티에 N + 1 개의 쿼리가 필요함을 의미하는 쿼리를 가져옵니다. 여기서 N은 지연 연결의 수입니다. 화면에 표 형식의 데이터가 표시되는 경우 Hibernate의 로그를 읽는 것은해야 할 일이 아니라는 큰 힌트입니다.
- 이것은 프레젠테이션 레이어에서 DB로 손톱을 더럽 히기 때문에 레이어드 아키텍처를 완전히 무력화합니다. 이것은 개념적인 사기입니다. 그래서 나는 그것과 함께 살 수 있지만 그에 따른 결과가 있습니다.
- 마지막으로, 세션을 가져 오는 동안 예외가 발생하면 페이지를 쓰는 동안 예외가 발생합니다. 사용자에게 깨끗한 오류 페이지를 제공 할 수 없으며 본문에 오류 메시지를 작성하는 것뿐입니다.
더 자세한 설명은 안티 패턴보기에서 내 세션 열기 기사를 읽을 수 있습니다 . 그렇지 않은 경우 다음은보기에서 세션 열기를 사용하지 않아야하는 이유에 대한 요약입니다.
보기에서 세션 열기는 데이터를 가져 오는 데 잘못된 접근 방식을 사용합니다. 비즈니스 계층이 View 계층에 필요한 모든 연결을 가져 오는 것이 가장 좋은 방법을 결정하도록하는 대신 View 계층이 프록시 초기화를 트리거 할 수 있도록 Persistence Context를 강제로 열어 둡니다.
OpenSessionInViewFilter
부르는 openSession
기본 방법을 SessionFactory
새로운를 가져옵니다 Session
.Session
에 바인딩됩니다 TransactionSynchronizationManager
.OpenSessionInViewFilter
부르는 doFilter
의 javax.servlet.FilterChain
오브젝트 레퍼런스 및 요청이 더 처리DispatcherServlet
라고하며 기본에이를 라우팅 HTTP 요청한다 PostController
.PostController
통화량은 PostService
목록을 얻을 수 Post
개체를.PostService
새 트랜잭션을 열고,이 HibernateTransactionManager
같은 재사용 Session
에 의해 열린이 OpenSessionInViewFilter
.PostDAO
목록 가져 오는 Post
어떤 게으른 관계를 초기화하지 않고 개체를.PostService
기본 트랜잭션을 커밋하지만은 Session
그것이 외부에서 열렸다 때문에 닫히지 않았습니다.DispatcherServlet
차례로, lazy 연관을 탐색하고 자신의 초기화를 트리거 UI를 렌더링이 시작됩니다.OpenSessionInViewFilter
를) 닫을 수 있으며 Session
기본 데이터베이스 연결도 해제됩니다.언뜻보기에 이것은 끔찍한 일처럼 보이지 않을 수도 있지만 데이터베이스 관점에서 보면 일련의 결함이 더 분명해지기 시작합니다.
서비스 계층은 데이터베이스 트랜잭션을 열고 닫지 만 이후에는 명시적인 트랜잭션이 진행되지 않습니다. 이러한 이유로 UI 렌더링 단계에서 발행 된 모든 추가 문은 자동 커밋 모드에서 실행됩니다. 자동 커밋은 각 문이 트랜잭션 로그를 디스크로 플러시해야하므로 데이터베이스 측에 많은 I / O 트래픽이 발생하기 때문에 데이터베이스 서버에 부담을줍니다. 한 가지 최적화는를 Connection
읽기 전용으로 표시 하여 데이터베이스 서버가 트랜잭션 로그에 쓰지 않도록하는 것입니다.
문은 서비스 계층과 UI 렌더링 프로세스 모두에서 생성되기 때문에 더 이상 문제가 분리되지 않습니다. 생성되는 문 수를 확인하는 통합 테스트를 작성 하려면 모든 계층 (웹, 서비스, DAO)을 거치면서 웹 컨테이너에 응용 프로그램을 배포해야합니다. 인 메모리 데이터베이스 (예 : HSQLDB) 및 경량 웹 서버 (예 : Jetty)를 사용하는 경우에도 이러한 통합 테스트는 계층이 분리되고 백엔드 통합 테스트가 데이터베이스를 사용하는 경우보다 실행 속도가 느립니다. 프런트 엔드 통합 테스트는 서비스 계층을 모두 조롱했습니다.
UI 계층은 N + 1 쿼리 문제를 유발할 수있는 연결 탐색으로 제한됩니다. Hibernate는 @BatchSize
일괄 적으로 연관을 가져오고이 FetchMode.SUBSELECT
시나리오에 대처하기 위해 제공하지만 , 주석은 기본 가져 오기 계획에 영향을 미치므로 모든 비즈니스 사용 사례에 적용됩니다. 이러한 이유로 데이터 액세스 계층 쿼리는 현재 사용 사례 데이터 가져 오기 요구 사항에 맞게 조정할 수 있기 때문에 훨씬 더 적합합니다.
마지막으로 데이터베이스 연결은 UI 렌더링 단계 (연결 해제 모드에 따라 다름) 동안 유지 될 수 있으며, 이는 연결 임대 시간을 늘리고 데이터베이스 연결 풀의 정체로 인해 전체 트랜잭션 처리량을 제한합니다. 연결이 더 많이 유지 될수록 풀에서 연결을 얻기 위해 더 많은 다른 동시 요청이 대기하게됩니다.
따라서 연결이 너무 오래 유지되거나 단일 HTTP 요청에 대해 여러 연결을 획득 / 해제하여 기본 연결 풀에 압력을 가하고 확장 성을 제한합니다.
불행히도 Open Session in View는 Spring Boot에서 기본적으로 활성화됩니다 .
따라서 application.properties
구성 파일에 다음 항목이 있는지 확인하십시오 .
spring.jpa.open-in-view=false
이렇게하면 OSIV가 비활성화되므로 올바른 방법으로 처리LazyInitializationException
할 수 있습니다 .
트랜잭션은 서비스 계층에서 커밋 될 수 있습니다. 트랜잭션은 OSIV와 관련이 없습니다. Session
트랜잭션이 아닌 열린 상태로 유지 되는 것은 실행 중입니다.
응용 프로그램 계층이 여러 컴퓨터에 분산되어 있으면 OSIV를 사용할 수 없습니다 . 개체를 유선으로 보내기 전에 필요한 모든 것을 초기화해야합니다.
OSIV는 지연로드의 성능 이점을 활용하는 훌륭하고 투명한 (즉, 코드가 발생하는 것을 인식하지 못하는) 방법입니다.
Open Session In View가 나쁜 관행이라고 생각하지 않습니다. 그 인상을주는 것은 무엇입니까?
Open-Session-In-View는 Hibernate로 세션을 처리하는 간단한 접근 방식입니다. 간단하기 때문에 때로는 단순합니다. 요청에 여러 트랜잭션을 포함하는 것과 같이 트랜잭션에 대한 세밀한 제어가 필요한 경우 Open-Session-In-View가 항상 좋은 방법은 아닙니다.
다른 사람들이 지적했듯이 OSIV에는 몇 가지 절충안이 있습니다. 시작하는 트랜잭션을 인식 할 가능성이 적기 때문에 N + 1 문제에 훨씬 더 취약합니다. 동시에, 뷰의 사소한 변경에 적응하기 위해 서비스 계층을 변경할 필요가 없음을 의미합니다.
Spring과 같은 IoC (Inversion of Control) 컨테이너를 사용하는 경우 빈 범위 지정 에 대해 읽을 수 있습니다 . 본질적으로, 나는 Spring에게 Session
전체 요청에 걸친 수명주기를 갖는 Hibernate 객체 를 제공하라고 말하고있다 (즉, HTTP 요청의 시작과 끝에서 생성되고 파괴된다). LazyLoadException
IoC 컨테이너가 나를 위해 관리하므로 세션을 닫거나 s 에 대해 걱정할 필요가 없습니다 .
언급했듯이 N + 1 SELECT 성능 문제에 대해 생각해야합니다. 나중에 항상 Hibernate 엔티티를 구성하여 성능이 문제가되는 곳에서 즉시 조인로드를 수행 할 수 있습니다.
빈 범위 지정 솔루션은 Spring 전용이 아닙니다. 저는 PicoContainer가 동일한 기능을 제공한다는 것을 알고 있으며 다른 성숙한 IoC 컨테이너가 비슷한 기능을 제공한다고 확신합니다.
내 블로그에서 오픈 세션을 언제 사용할 지에 대한 몇 가지 지침에 대한 게시물을 작성했습니다. 관심이 있으시면 확인하십시오.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
나는 Hibernate에서 v. rusty이지만 하나의 Hibernate 세션에서 여러 트랜잭션을 가질 수 있다고 생각합니다. 따라서 트랜잭션 경계는 세션 시작 / 중지 이벤트와 동일하지 않아도됩니다.
OSIV, imo는 요청이 DB에 액세스해야 할 때마다 '지속성 컨텍스트'(일명 세션)를 시작하기위한 코드를 작성하는 것을 피할 수 있기 때문에 주로 유용합니다.
서비스 계층에서 'Required, New Required 등'과 같이 트랜잭션 요구 사항이 다른 메서드를 호출해야 할 것입니다. 이 메서드에 필요한 유일한 것은 누군가 (예 : OSIV 필터)가 지속성 컨텍스트를 시작했기 때문에 그들이 걱정해야 할 유일한 것은 "이 스레드에 대한 최대 절전 세션을 제공합니다 .. 몇 가지를 수행해야합니다. DB 물건 ".
이것은별로 도움이되지 않지만 여기서 내 주제를 확인할 수 있습니다. * Hibernate Cache1 OutOfMemory with OpenSessionInView
OpenSessionInView 및로드 된 많은 엔티티로 인해 일부 OutOfMemory 문제가 있습니다. Hibernate 캐시 레벨 1에 남아 있고 가비지 수집되지 않기 때문입니다 (페이지 당 500 개 항목으로 많은 엔티티를로드하지만 모든 엔티티는 캐시에 남아 있음).