최대 절전 모드 지연로드 애플리케이션 설계


87

저는 Spring 프레임 워크와 함께 Hibernate 를 사용하는 경향이 있으며 선언적 트랜잭션 경계 기능 (예 : @Transactional )입니다.

우리 모두가 알고 있듯이, 최대 절전 모드는 가능한 한 비 침습적 이고 투명 하도록 노력 하지만 관계를 사용할 때 조금 더 도전적lazy-loaded 입니다.


투명도 수준이 다른 여러 디자인 대안이 있습니다.

  1. 지연로드되지 않는 관계 만들기 (예 : fetchType=FetchType.EAGER)
    • 이것은 지연 로딩의 전체 아이디어를 위반합니다.
  2. 다음을 사용하여 컬렉션 초기화 Hibernate.initialize(proxyObj);
    • 이것은 DAO에 상대적으로 높은 결합을 의미합니다.
    • 를 사용하여 인터페이스를 정의 할 수 있지만 initialize다른 구현에서는 동등한 기능을 제공하지 않을 수도 있습니다.
  3. 영구 Model객체 자체에 트랜잭션 동작 추가 ( 동적 프록시 또는 사용 @Transactional)
    • @Transactional이 영구 개체 자체에서 작업하는 것 같지는 않았지만 동적 프록시 접근 방식을 시도하지 않았습니다. 아마도 최대 절전 모드로 인해 프록시에서 작동합니다.
    • 거래가 실제로 발생할 때 통제력 상실
  4. 모두 게으른 / 비 지연 API를 제공, 예를 들어, loadData()loadDataWithDeps()
    • 응용 프로그램이 어떤 루틴을 사용할지 알도록합니다.
    • 메서드 오버플로 loadDataWithA(),, ....,loadDataWithX()
  5. 예를 들어 byId()오퍼레이션 만 제공하여 종속성에 대한 강제 조회
    • 비 객체 지향 루틴, 예를 들어, 많이 필요 findZzzById(zid)하고 getYyyIds(zid)대신z.getY()
    • 트랜잭션간에 처리 오버 헤드가 큰 경우 컬렉션의 각 개체를 하나씩 가져 오는 것이 유용 할 수 있습니다.
  6. DAO 대신 @Transactional 응용 프로그램의 일부를 만듭니다.
    • 중첩 된 트랜잭션의 가능한 고려 사항
    • 트랜잭션 관리에 적합한 루틴이 필요합니다 (예 : 충분히 작음)
    • 프로그래밍 방식의 영향은 적지 만 대규모 트랜잭션이 발생할 수 있음
  7. DAO에 동적 가져 오기 프로필을 제공합니다 . 예 :loadData(id, fetchProfile);
    • 응용 프로그램은 언제 사용할 프로필을 알아야합니다.
  8. AoP 유형의 트랜잭션 (예 : 작업을 가로 채고 필요한 경우 트랜잭션 수행)
    • 바이트 코드 조작 또는 프록시 사용 필요
    • 거래가 수행 될 때 통제력 상실
    • 언제나처럼 흑 마법 :)

내가 놓친 옵션이 있습니까?


lazy-loaded애플리케이션 설계에서 관계 의 영향을 최소화하려고 할 때 선호하는 접근 방식은 무엇입니까?

(오, WoT 죄송합니다 )


옵션 2 및 5의 예 : m-hewedy.blogspot.ch/2010/03/…
Adrien Be

옵션 4의 예를 들어 주시겠습니까?
degreeightdc 2014-06-08

답변:


26

우리 모두가 알고 있듯이, 최대 절전 모드는 가능한 한 비 침습적이고 투명하도록 노력합니다.

나는 초기 가정이 틀렸다고 말할 것입니다. 애플리케이션은 항상 엔티티 수명주기와로드되는 개체 그래프의 크기를 관리해야하기 때문에 투명 지속성은 신화입니다.

Hibernate는 생각을 읽을 수 없으므로 특정 작업에 대한 특정 종속성 세트가 필요하다는 것을 알고 있다면 어떻게 든 Hibernate에 대한 의도를 표현해야합니다.

이러한 관점에서 이러한 의도를 명시 적으로 표현하는 솔루션 (즉, 2, 4 및 7)은 합리적으로 보이며 투명성이 부족하지 않습니다.


당연히 당신 말이 맞습니다 . 가능한 한 투명한 것은 지금까지만 작동합니다. 그것들은 당신이 갔던 좋은 선택입니다.
Johan Sjöberg

IMHO : 완벽하게 정답입니다. 사실 그것은 신화입니다. BTW : 내 투표는 옵션 4와 7에 대한 것입니다 (또는 ORM에서 완전히
멀어짐

7

어떤 문제 (게으름으로 인한)를 암시하고 있는지 잘 모르겠지만, 저에게 가장 큰 고통은 내 애플리케이션 캐시에서 세션 컨텍스트를 잃지 않는 것입니다. 일반적인 경우 :

  • 객체 foo가로드되고지도에 배치됩니다.
  • 다른 스레드는지도에서이 객체를 가져와 호출합니다 foo.getBar()(이전에 호출 된 적이없고 지연 평가 된 것).
  • 팔!

따라서이를 해결하기 위해 다음과 같은 여러 규칙이 있습니다.

  • 세션을 가능한 한 투명하게 래핑합니다 (예 : OpenSessionInViewFilter웹앱의 경우).
  • 스레드 / 스레드 풀에 대한 공통 API를 가지고 있으며, 여기서 db 세션 바인딩 / 바인딩 해제가 계층 구조의 상위 어딘가에서 수행 try/finally되므로 하위 클래스가 이에 대해 생각할 필요가 없습니다.
  • 스레드간에 개체를 전달할 때 개체 자체 대신 ID를 전달합니다. 수신 스레드는 필요한 경우 객체를로드 할 수 있습니다.
  • 개체를 캐시 할 때 개체를 캐시하지 않고 해당 ID 만 캐시합니다. DAO 또는 관리자 클래스에 추상 메서드를 사용하여 ID를 알고있을 때 2 단계 Hibernate 캐시에서 개체를로드합니다. 2 단계 Hibernate 캐시에서 객체를 검색하는 비용은 여전히 ​​DB로가는 것보다 훨씬 저렴합니다.

보시다시피 이것은 실제로 비 침습적이며 투명하지 않습니다 . 그러나 그 비용은 내가 eager loading에 지불해야하는 가격과 비교할 때 여전히 견딜 수 있습니다. 후자의 문제는 엔티티 컬렉션은 말할 것도없고 단일 참조 객체를로드 할 때 가끔 나비 효과로 이어진다는 것입니다. 메모리 소비, CPU 사용량 및 지연 시간도 훨씬 더 나쁘기 때문에 함께 살 수 있다고 생각합니다.


답장을 보내 주셔서 감사합니다. 의 손실은 transparency응용 프로그램이 지연된 개체를로드하는 데 신경을 쓰도록하는 것입니다. 모든 것을 열심히 가져 오면 애플리케이션 객체가 데이터베이스에 유지되는지 여부를 완전히 인식하지 못할 수 있습니다 Foo.getBar(). 항상 성공하기 때문 입니다. > when passing objects between threads, pass IDs, 예, 이것은 # 5에 해당합니다.
Johan Sjöberg

3

매우 일반적인 패턴은 웹 애플리케이션을 빌드하는 경우 OpenEntityManagerInViewFilter 를 사용 하는 것입니다.

서비스를 빌드하는 경우 메서드가 여러 엔터티를 가져 오거나 업데이트해야하는 경우가 많으므로 DAO가 아닌 서비스의 공용 메서드에서 TX를 엽니 다.

이렇게하면 "Lazy Load 예외"가 해결됩니다. 성능 조정을 위해 더 고급이 필요한 경우 프로필 가져 오기가 좋은 방법이라고 생각합니다.


1
나는 당신이 말하고 싶어 같아요 아주 일반적인 안티 패턴을 ... . 서비스 수준에서 TX를 여는 데 동의하지만 사용 OSIV은 여전히 ​​반 패턴이며 예외를 정상적으로 처리 할 수 ​​없거나 성능 저하와 같은 매우 심각한 문제로 이어집니다. 요약 하자면 IMHO OSIV는 쉬운 솔루션이지만 장난감 프로젝트에만 적합합니다.
G. Demecki
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.