개발 등/HIBERNATE

lazy 방식

darkhorizon 2009. 4. 14. 13:39
728x90
반응형
원문 출처 : http://javacan.tistory.com/entry/105#comment2347957
작성자     : 최범균

실제로 사용할 때 연관 객체를 읽어오는 걸, lazy 방식이라고 한다. Hibernate는 lazy 방식으로 연관 객체를 읽어올 때 다음과 같이 프록시 객체를 사용한다.



lazy 방식으로 지정된 경우, Hibernate는 위 그림에서 볼 수 있듯이 연관된 객체를 곧바로 읽어오지 않는다. 대신 연관된 객체와 연결될 수 있는 프록시 객체를 생성한다. 연관된 객체가 필요할 때 실제 연관될 객체가 생성되서 프록시와 연결된다.

many-to-one 관계에서 lazy 방식을 적용하기 위해서는 다음과 같이 연관될 클래스에 대한 정보를 담고 있는 class 태그의 lazy 속성값을 true로 지정해주어야 한다. (Bid-Item의 관계가 many-to-one)

      <class name="Item" table="ITEM" lazy="true">
            ...
      </class>

      <class name="Bid" table="BID">
            ...
            
            <many-to-one
                         name="item"
                         column="ITEM_ID"
                         class="Item"
                         not-null="true" />
            ...
      </class>

위와 같이 lazy 속성을 true로 지정하게 되면 Hibernate는 Item 클래스를 위한 프록시 클래스를 자동으로 생성해서 프록시로 사용한다.

one-to-one 관계에서도 마찬가지로 lazy 속성을 사용해서 lazy 방식을 적용할 수 있다. 예를 들어, 아래와 같이 one-to-one 관계를 맺을 때, 양쪽 모두 lazy 방식으로 읽어오도록 설정할 수 있다.

      <class name="Item" table="ITEM" lazy="true">
            ...
            <one-to-one name="detail"
                        class="ItemDetail" 
                        cascade="save-update" />            
            ...
            
      </class>

      <class name="ItemDetail" table="ITEM_DETAIL" lazy="true">
            ...
            <one-to-one name="item"
                        class="Item"
                        constrained="true" />
      </class>

콜렉션을 읽어올 때에도 lazy 방식을 사용할 수 있다. 예를 들어, set 태그에 다음과 같이 lazy 속성을 추가함으로써 lazy 방식으로 콜렉션을 읽어오도록 명시할 수 있다.

    <class name="javacan.hibernate.test.Item" table="ITEM" .. >
        ...

        <set name="bids" inverse="true" cascade="all-delete-orphan" lazy="true">
            <key column="ITEM_ID" />
            <one-to-many class="javacan.hibernate.test.Bid" />
        </set>
    </class>

lazy 방식을 사용할 때 주의할 점은 객체가 영속 상태일 때에만, 즉 Session과 연결되어 있는 경우에만 lazy 방식으로 데이터를 읽어올 수 있다는 것이다. 예를 들어, 아래의 코드처럼 lazy 방식으로 읽어온 프로퍼티를 준영속 상태에서 읽어올 수 없으며, 이런 경우 예외가 발생하게 된다.

      Session session = sessions.openSession();
      Transaction tx = session.beginTransaction();
      
      // Item의 bids 프로퍼티와 관련 객체를 lazy 방식으로 로딩
      Item item = (Item)session.get(Item.class, someId); 
      
      tx.commit();
      session.close();
      
      // 준영속 상태에서 lazy 방식의 프로퍼티에 접근 -> 예외 발생
      Iterator iter = item.getBids().iterator();

따라서, lazy 방식으로 읽어온 프로퍼티를 준영속 상태에서 사용해야 하는 경우에는 사전에 미리 lazy 방식으로 읽어온 프로퍼티를 초기화해야 한다. 이럴 때 사용할 수 있는 메소드가 Hibernate.initialize()이다. 위와 같은 경우 다음과 같이 Session을 종료하기 전에 Hibernate.initialize() 메소드를 호출해주면 된다.

      Session session = sessions.openSession();
      Transaction tx = session.beginTransaction();
      
      // Item의 bids 프로퍼티와 관련 객체를 lazy 방식으로 로딩
      Item item = (Item)session.get(Item.class, someId); 
      Hibernate.initialize(item.getBids());      
      tx.commit();
      session.close();
      
      // 사전에 초기화 되었으므로 예외 발생안함
      Iterator iter = item.getBids().iter();

outer-join 속성을 사용한 연관 객체 로딩

lazy 방식은 연관된 객체가 실제로 사용되기 전까지 최대한 늦게 객체를 로딩하는 반면에 outer-join 방식은 최대한 빨리 연관된 객체를 로딩한다. outer-join 방식은 외부 조인을 사용해서 한번의 SQL 쿼리로 연관 객체와 관련된 모든 데이터를 읽어오므로 데이터베이스 조회 회수를 줄이면서 동시에 연관 객체를 읽어오게 된다.

outer-join 속성값을 사용해서 outer-join 방식을 설정할 수 있는데, outer-join 속성에 사용할 수 있는 값을 다음과 같이 세가지가 존재한다.

  • auto - 기본값으로서 프록시가 가능한 경우 lazy 방식으로 읽어오고, 그렇지 않은 경우 outer-join 방식으로 읽어온다.
  • true - 프록시가 가능한 경우에도, 항상 outer-join 방식으로 읽어온다.
  • false - 프로시를 사용하지 않더라도, 항상 outer-join 방식을 사용하지 않는다.
예를 들어, Bid 객체를 읽어올 때 many-to-one의 관계에 있는 Item을 항상 outer-join 방식으로 읽어오고 싶다면 다음과 같이 하면 된다.

      <many-to-one name="item" class="Item" outer-join="true">

728x90