본문 바로가기
Homo Faber/Idioms

가벼운 클래스

by javauser 2008. 2. 27.
다음의 패턴은 Hibernate에서 예전이나 지금이나 여전히 보편적이지만, Hibernate3 에서 이 문제를 해결하는 다른 방법이 있다.

다음과 같은 저장 클래스를 가지고 있다고 가정하자.
public class Document implements Node {
   private Long _key;
   private String _name;
   private Calendar _created;
   private Calendar _updated;
   private Folder _folder;
   private Clob _text;
   public String getKey() { return _key; }
   public void setKey(Long key) { _key = key; }
   public String getName() { return _name; }
   public void setName(String name) { _name = name; }
   public Calendar getCreated() { return _created; }
   public void setCreated(Calendar created) { _created = created; }
   public Calendar getUpdated() { return _updated; }
   public void setUpdated(Calendar updated) { _updated = updated; }
   public Folder getFolder() { return _folder; }
   public void setFolder(Folder folder) { _folder = folder; }
   public Clob getText() { return _text; }
   public void setText(Clob text) { _text = text; }
}
이 클래스의 모든 속성들은 DOCUMENTS 테이블의 컬럼에 매핑된다. 성능상의 이유로 문서 리스트를 가지고오거나, 문서의 이름을 변경하거나, 문서를 옮기는 등의 매순간마다 포함되어 있는 CLOB 데이터에 대한 스트림을 열기 원치 않는다. 어떤 상용 OR 매핑 도구는 기본적인 초시화 속성을 세팅하여 처음에 조회를 하도록 한다. 나머지 속성들은 처음 접근될 때 가지고 온다. Hibernate3는 이와 같은 특성을 가지고 있지만, Hibernate2는 그렇지 않다. 하지만, 그러한 방법보다도 더 성능을 잘 내도록 하는 잘쓰는 방법이 있다.

Document 클래스를 "가벼운" 상위 클래스와 "무거운" 하위클래스로 나눈다.
public class DocumentInfo implements Node {
   private Long _key;
   private String _name;
   private Calendar _created;
   private Calendar _updated;
   private Folder _folder;
   public String getKey() { return _key; }
   public void setKey(Long key) { _key = key; }
   public String getName() { return _name; }
   public void setName(String name) { _name = name; }
   public Calendar getCreated() { return _created; }
   public void setCreated(Calendar created) { _created = created; }
   public Calendar getUpdated() { return _updated; }
   public void setUpdated(Calendar updated) { _updated = updated; }
   public Folder getFolder() { return _folder; }
   public void setFolder(Folder folder) { _folder = folder; }
}

public class Document extends DocumentInfo {
   private Clob _text;
   public Clob getText() { return _text; }
   public void setText(Clob text) { _text = text; }
}
다음과 같이 매핑을 한다.
<class name="DocumentInfo" table="DOCUMENTS">
   <id name="key" type="long" column="ID">
       <generator class="native"/>
   </id>
   <property name="name"/>
   <property name="created"/>
   <property name="updated"/>
   <many-to-one name="folder"/>
</class>

<class name="Document" table="DOCUMENTS" polymorphism="explicit">
   <id name="key" type="long" column="ID">
       <generator class="native"/>
   </id>
   <property name="name"/>
   <property name="created"/>
   <property name="updated"/>
   <many-to-one name="folder"/>
   <property name="text"/>
</class>
이제 어플리케이션은 정보만을 원한다면 DocumentInfo의 인스턴스를 조회할 수 있으며, text 속성을 접근할 필요가 있는 경우 Document 인스턴스를 조회할 수 있다. polymorphism="explicit" 를 사용해서 Document를 매핑하고 있기 때문에 다음과 같은 쿼리들은 DocumentInfo를 반환한다.
from DocumentInfo
from Node
from java.lang.Object
다음의 쿼리는 Document 인스턴스만을 반환한다.
from d in class Document

주의 : Hibernate2에서 두개의 서로 다른 객체로 동일한 데이터베이스의 행을 로딩할 수 없다. 그렇기 때문에 Hibernate가 객체를 캐스팅하지 못해서 동일한 Document ID의 Document 다음의 DocumentInfo를 먼저 로딩할 수 없다. 예를 들어, 다음의 코드는 ClassCastException이 발생할 것이다.
DocumentInfo info = (DocumentInfo) session.load(DocumentInfo.class, new Long(1));
Document doc = (Document) session.load(Document.class, new Long(1));

DocumentInfo와 Document로 동일한 Document를 얻어오려면 session.evict() 나 먼저 DocumentInfo를 사용할 필요가 있다. 혹은 클래스 매핑에 polymorphism="explicit"를 추가하면 로더가 동일한 테이블에 매핑되는 서로 다른 클래스에 매핑되는 것을 방해하는 것을 멈추고 동일한 PK를 공유하는 것을 멈추게 한다.

논란의 여지가 있지만 Hibernate는 지연 로딩(lazy loading) 기능이 진정으로 필요하지 않을 수도 있음을 유의하라. 왜냐하면 쿼리가 project을 지원하고 자바 코드를 통해 정확하게 필요한 속성을 끄집어오게끔 하는 것이 가능하기 때문이다.
반응형