View의 Hibernate Open Session이 나쁜 습관으로 간주되는 이유는 무엇입니까?


108

그리고 LazyLoadExceptions를 피하기 위해 어떤 종류의 대체 전략을 사용합니까?

보기에서 열린 세션에 다음과 같은 문제가 있음을 이해합니다.

  • 다른 jvm에서 실행되는 계층화 된 애플리케이션
  • 트랜잭션은 마지막에만 커밋되며 아마도 이전 결과를 원할 것입니다.

그러나 애플리케이션이 단일 VM에서 실행되고 있다는 것을 알고 있다면보기 전략에서 열린 세션을 사용하여 고통을 덜어 보는 것은 어떨까요?


12
OSIV는 나쁜 습관으로 간주됩니까? 누구에 의해?
Johannes Brodwall

4
그리고-좋은 대안은 무엇입니까?
David Rabinowitz

7
Seam 개발자의 경우이 텍스트의 평화 :이 구현에는 몇 가지 문제가 있습니다. 가장 심각한 문제는 트랜잭션을 커밋하기 전까지는 트랜잭션의 성공 여부를 확인할 수 없다는 것입니다. 그러나 "Open session in view"트랜잭션이 커밋 될 때까지 뷰가 완전히 렌더링되고 렌더링 된 응답이 이미 클라이언트에 플러시되었을 수 있습니다. 거래가 실패했음을 사용자에게 어떻게 알릴 수 있습니까?
darpet


2
-이 블로그의 장점과 단점에 대한 포스트 그것에 대해 내 자신의 경험을 참조하십시오 blog.jhades.org/open-session-in-view-pattern-pros-and-cons
각도 대학

답변:


46

초기화되지 않은 프록시, 특히 컬렉션을 뷰 레이어에서 보내고 거기에서 최대 절전 모드로드를 트리거하는 것은 성능과 이해의 관점 모두에서 문제가 될 수 있기 때문입니다.

이해 :

OSIV를 사용하면 데이터 액세스 계층과 관련된 문제로 뷰 계층이 '오염'됩니다.

보기 계층은 HibernateException지연로드시 발생할 수있는를 처리 할 준비가되어 있지 않지만 데이터 액세스 계층은 아마도 그렇습니다.

성능 :

OSIV는 카펫 아래에 적절한 엔티티 로딩을 잡아 당기는 경향이 있습니다. 컬렉션이나 엔티티가 느리게 초기화된다는 것을 알지 못하는 경향이 있습니다 (아마도 N + 1). 더 많은 편리함, 더 적은 제어.


업데이트 : 이 주제에 대한 더 큰 논의 는 OpenSessionInView 안티 패턴 을 참조하십시오 . 저자는 세 가지 중요한 사항을 나열합니다.

  1. 각 지연 초기화는 각 엔티티에 N + 1 개의 쿼리가 필요함을 의미하는 쿼리를 가져옵니다. 여기서 N은 지연 연결의 수입니다. 화면에 표 형식의 데이터가 표시되는 경우 Hibernate의 로그를 읽는 것은해야 할 일이 아니라는 큰 힌트입니다.
  2. 이것은 프레젠테이션 레이어에서 DB로 손톱을 더럽 히기 때문에 레이어드 아키텍처를 완전히 무력화합니다. 이것은 개념적인 사기입니다. 그래서 나는 그것과 함께 살 수 있지만 그에 따른 결과가 있습니다.
  3. 마지막으로, 세션을 가져 오는 동안 예외가 발생하면 페이지를 쓰는 동안 예외가 발생합니다. 사용자에게 깨끗한 오류 페이지를 제공 할 수 없으며 본문에 오류 메시지를 작성하는 것뿐입니다.

13
좋아, 그것은 최대 절전 모드 예외로 뷰 레이어를 '오염'합니다. 그러나 성능과 관련하여 문제는 dto를 반환하는 서비스 계층에 액세스하는 것과 매우 유사하다고 생각합니다. 성능 문제가 발생하면 더 스마트 한 쿼리 또는 더 가벼운 dto로 특정 문제를 최적화해야합니다. 뷰에서 필요할 수있는 가능성을 처리하기 위해 너무 많은 서비스 방법을 개발해야하는 경우 서비스 계층도 '오염'됩니다. 아니?
HeDinges

1
한 가지 차이점은 Hibernate 세션의 종료를 지연 시킨다는 것입니다. JSP가 렌더링 / 작성 / 기다릴 때까지 기다릴 것입니다. 그러면 객체가 메모리에 더 오래 유지됩니다. 특히 세션 커밋에 데이터를 써야하는 경우 문제가 될 수 있습니다.
Robert Munteanu

8
OSIV가 성능을 저하 시킨다고 말하는 것은 말이되지 않습니다. DTO를 사용하는 것 외에 어떤 대안이 있습니까? 이 경우 모든 뷰에서 사용하는 데이터는 필요하지 않은 뷰에 대해서도로드되어야하므로 항상 성능이 저하됩니다.
Johannes Brodwall

11
나는 오염이 반대 방향으로 작용한다고 생각합니다. 데이터를 열심히로드해야하는 경우 논리 계층 (또는 더 나쁜 데이터 액세스 계층)은 개체가 표시되는 방식을 알아야합니다. 보기를 변경하면 필요하지 않은 항목이로드되거나 필요한 개체가 누락됩니다. Hibernate Exception은 버그이며 다른 예기치 않은 예외와 마찬가지로 중독됩니다. 그러나 성능이 문제입니다. 성능 및 확장 성 문제는 이전에 폐쇄 될 데이터 액세스 계층에서 더 많은 생각과 작업을 넣어, 그리고 아마도 세션을 강제로 당신을 강제로
옌스 Schauder

1
@JensSchauder "보기를 변경하면 필요하지 않은 항목이로드되거나 필요한 개체가 누락됩니다." 이것이 바로 그것입니다. 뷰를 변경하면 뷰를로드하는 것보다 필요하지 않은 것을로드하거나 (열심히 가져올 가능성이 높기 때문에) 누락 된 객체를 파악하는 것이 훨씬 낫습니다. 그것은 N + 1 문제를 초래할 것이기 때문에 게으르고 그것이 일어나고 있는지조차 알지 못할 것입니다. 따라서 IMO는 서비스 계층 (및 사용자)이보기가 느리게로드되고 그것에 대해 아무것도 모르는 것보다 전송되는 내용을 더 잘 알고 있습니다.
여수 룬

40

더 자세한 설명은 안티 패턴보기에서세션 열기 기사를 읽을 수 있습니다 . 그렇지 않은 경우 다음은보기에서 세션 열기를 사용하지 않아야하는 이유에 대한 요약입니다.

보기에서 세션 열기는 데이터를 가져 오는 데 잘못된 접근 방식을 사용합니다. 비즈니스 계층이 View 계층에 필요한 모든 연결을 가져 오는 것이 가장 좋은 방법을 결정하도록하는 대신 View 계층이 프록시 초기화를 트리거 할 수 있도록 Persistence Context를 강제로 열어 둡니다.

여기에 이미지 설명 입력

  • OpenSessionInViewFilter부르는 openSession기본 방법을 SessionFactory새로운를 가져옵니다 Session.
  • Session에 바인딩됩니다 TransactionSynchronizationManager.
  • OpenSessionInViewFilter부르는 doFilterjavax.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 할 수 있습니다 .


3
자동 커밋과 함께 View에서 Open Session을 사용하는 것은 가능하지만 Hibernate 개발자가 의도 한 방식은 아닙니다. 따라서 View의 Open Session에는 단점이 있지만 자동 커밋은 단순히 끄고 계속 사용할 수 있기 때문에 하나가 아닙니다.
stefan.m

거래 내에서 일어나는 일에 대해 이야기하고 있으며, 사실입니다. 그러나 웹 레이어 렌더링 단계는 Hibernate 외부에서 발생하므로 자동 커밋 모드가됩니다. 말이된다?
Vlad Mihalcea

저는 이것이 Open Session in View에 최적이 아닌 변형이라고 생각합니다. 세션과 트랜잭션은 뷰가 렌더링 될 때까지 열려 있어야하며 자동 커밋 모드가 필요하지 않습니다.
stefan.m

2
세션은 계속 열려 있습니다. 그러나 거래는 그렇지 않습니다. 트랜잭션을 전체 프로세스에 걸쳐 확장하는 것은 길이가 늘어나고 잠금이 필요 이상으로 오래 유지되기 때문에 최적이 아닙니다. 뷰가 RuntimeException을 던지면 어떤 일이 발생하는지 상상해보십시오. UI 렌더링이 실패하여 트랜잭션이 롤백됩니까?
Vlad Mihalcea

매우 상세한 답변에 대단히 감사합니다! 스프링 부트 사용자는 그런 식으로 jpa를 사용하지 않을 가능성이 높기 때문에 결국 가이드를 변경할 것입니다.
Skeeve

24
  • 트랜잭션은 서비스 계층에서 커밋 될 수 있습니다. 트랜잭션은 OSIV와 관련이 없습니다. Session트랜잭션이 아닌 열린 상태로 유지 되는 것은 실행 중입니다.

  • 응용 프로그램 계층이 여러 컴퓨터에 분산되어 있으면 OSIV를 사용할 수 없습니다 . 개체를 유선으로 보내기 전에 필요한 모든 것을 초기화해야합니다.

  • OSIV는 지연로드의 성능 이점을 활용하는 훌륭하고 투명한 (즉, 코드가 발생하는 것을 인식하지 못하는) 방법입니다.


2
첫 번째 글 머리 기호와 관련하여 이것은 JBoss 위키 의 원래 OSIV에 대해서는 사실이 아니며 요청에 대한 트랜잭션 경계도 처리합니다.
Pascal Thivent

@PascalThivent 어느 부분이 그렇게 생각하게 만들었습니까?
이상현

13

Open Session In View가 나쁜 관행이라고 생각하지 않습니다. 그 인상을주는 것은 무엇입니까?

Open-Session-In-View는 Hibernate로 세션을 처리하는 간단한 접근 방식입니다. 간단하기 때문에 때로는 단순합니다. 요청에 여러 트랜잭션을 포함하는 것과 같이 트랜잭션에 대한 세밀한 제어가 필요한 경우 Open-Session-In-View가 항상 좋은 방법은 아닙니다.

다른 사람들이 지적했듯이 OSIV에는 몇 가지 절충안이 있습니다. 시작하는 트랜잭션을 인식 할 가능성이 적기 때문에 N + 1 문제에 훨씬 더 취약합니다. 동시에, 뷰의 사소한 변경에 적응하기 위해 서비스 계층을 변경할 필요가 없음을 의미합니다.


5

Spring과 같은 IoC (Inversion of Control) 컨테이너를 사용하는 경우 빈 범위 지정 에 대해 읽을 수 있습니다 . 본질적으로, 나는 Spring에게 Session전체 요청에 걸친 수명주기를 갖는 Hibernate 객체 를 제공하라고 말하고있다 (즉, HTTP 요청의 시작과 끝에서 생성되고 파괴된다). LazyLoadExceptionIoC 컨테이너가 나를 위해 관리하므로 세션을 닫거나 s 에 대해 걱정할 필요가 없습니다 .

언급했듯이 N + 1 SELECT 성능 문제에 대해 생각해야합니다. 나중에 항상 Hibernate 엔티티를 구성하여 성능이 문제가되는 곳에서 즉시 조인로드를 수행 할 수 있습니다.

빈 범위 지정 솔루션은 Spring 전용이 아닙니다. 저는 PicoContainer가 동일한 기능을 제공한다는 것을 알고 있으며 다른 성숙한 IoC 컨테이너가 비슷한 기능을 제공한다고 확신합니다.


1
요청 범위 빈을 통해 뷰에서 사용 가능한 Hibernate 세션의 실제 구현에 대한 포인터가 있습니까?
Marvo

4

내 경험상 OSIV는 그렇게 나쁘지 않습니다. 내가 만든 유일한 배열은 두 가지 다른 트랜잭션을 사용하는 것입니다.-첫 번째는 "서비스 계층"에서 열리고 "비즈니스 논리"가있는 경우-두 번째는 뷰 렌더링 직전에 열렸습니다.


3

내 블로그에서 오픈 세션을 언제 사용할 지에 대한 몇 가지 지침에 대한 게시물을 작성했습니다. 관심이 있으시면 확인하십시오.

http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/


1
일반적인 SO 경험 법칙으로 답변을 제공하는 경우 다른 곳에서 링크하는 것 이상을 수행하는 것이 가장 좋습니다. 요점을 제공하는 한두 개의 문장 또는 나열된 항목을 제공 할 수 있습니다. 링크하는 것은 괜찮지 만 약간의 추가 가치를 제공하고 싶습니다. 그렇지 않으면 단순히 주석을 달고 링크를 넣을 수 있습니다.
DWright dec.

이 답변의 링크는 읽을만한 가치가 있습니다. OSIV를 사용해야 할 때에 대한 좋은 지침을 제공합니다
ams

1

나는 Hibernate에서 v. rusty이지만 하나의 Hibernate 세션에서 여러 트랜잭션을 가질 수 있다고 생각합니다. 따라서 트랜잭션 경계는 세션 시작 / 중지 이벤트와 동일하지 않아도됩니다.

OSIV, imo는 요청이 DB에 액세스해야 할 때마다 '지속성 컨텍스트'(일명 세션)를 시작하기위한 코드를 작성하는 것을 피할 수 있기 때문에 주로 유용합니다.

서비스 계층에서 'Required, New Required 등'과 같이 트랜잭션 요구 사항이 다른 메서드를 호출해야 할 것입니다. 이 메서드에 필요한 유일한 것은 누군가 (예 : OSIV 필터)가 지속성 컨텍스트를 시작했기 때문에 그들이 걱정해야 할 유일한 것은 "이 스레드에 대한 최대 절전 세션을 제공합니다 .. 몇 가지를 수행해야합니다. DB 물건 ".


1

이것은별로 도움이되지 않지만 여기서 내 주제를 확인할 수 있습니다. * Hibernate Cache1 OutOfMemory with OpenSessionInView

OpenSessionInView 및로드 된 많은 엔티티로 인해 일부 OutOfMemory 문제가 있습니다. Hibernate 캐시 레벨 1에 남아 있고 가비지 수집되지 않기 때문입니다 (페이지 당 500 개 항목으로 많은 엔티티를로드하지만 모든 엔티티는 캐시에 남아 있음).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.