제공 : 한빛 네트워크
저자 : John Ferguson Smart
역자 : 이대엽
원문 : An Introduction to Hibernate 3 Annotations
지난 몇 년간 하이버네이트(Hibernate)는 자바 데이터베이스 퍼시스턴스 진영에서 사실상의 업계 표준에 가깝게 되었다. 하이버네이트는 강력하고, 유연하며 그리고 탁월한 성능을 자랑한다. 이 기사에서는 자바 5 어노테이션이 어떻게 여러분의 하이버네이트 코드를 단순하게 하고 여러분의 퍼시스턴스 계층에 대한 코딩을 훨씬 더 쉽게 만드는 데 사용될 수 있는지 살펴볼 것이다.
전통적으로 하이버네이트는 하이버네이트 설정에 외부 XML 파일을 사용하는데, 데이터베이스 맵핑은 일련의 XML 맵핑 파일에 정의되며 시작 시점에 로딩된다. 이러한 맵핑을 생성하는 데에는 여러 가지 방법이 있는데 자동적으로 하는 것은 물론이거니와 기존의 데이터베이스 스키마나 자바 클래스 모델 혹은 수작업으로도 가능하다. 어떠한 경우이든 간에 여러분은 상당한 양의 하이버네이트 맵핑 파일을 만들게 될 것이다. 비록 그 대안으로 빌드 과정에 별도의 단계를 추가하기는 해야 하지만 여러분은 툴을 사용하여 javadoc 스타일의 어노테이션으로부터 맵핑 파일을 생성해 낼 수도 있다.
하이버네이트의 최신 버전에서 자바 5 어노테이션에 기반한 좀 더 우아한 접근법이 나타났다. 새로운 하이버네이트 어노테이션 라이브러리(Hibernate Annotation library)를 사용하면 굳이 여러분의 구식 맵핑 파일을 사용하지 않아도 되며, 여러분도 짐작하고 있겠지만 모든 것들을 여러분의 자바 클래스 안에 직접적으로 들어있는 어노테이션으로 정의할 수 있다. 어노테이션은 퍼시스턴스 맵핑을 선언하는 데 있어 강력하고 유연한 방법을 제공하는 것으로 입증되고 있으며, 또한 어노테이션은 코드 자동 완성(automatic code completion)과 문법 강조(syntax highlighting)을 비롯하여 최신 자바 통합 개발환경(IDE)에서도 잘 지원되고 있다.
또한 하이버네이트 어노테이션은 새로운 EJB 3 퍼시스턴스 명세(specification)도 지원하고 있다. 이러한 명세는 표준화된 자바 퍼시스턴스 메커니즘을 제공하는 것을 목표로 삼고 있다. 하이버네이트 3도 마찬가지로 몇 가지 확장기능을 제공하기는 하지만 여러분은 EJB 3 프로그래밍 모델을 이용하여도 손쉽게 표준을 준수하고 여러분의 하이버네이트 퍼시스턴스 계층을 코딩할 수 있다.
지금부터 하이버네이트 어노테이션의 위력을 체험해 보도록 하자.
하이버네이트 어노테이션(Hibernate Annotation) 설치
하이버네이트 어노테이션(Hibernate Annotation)을 사용하려면 자바 5는 물론이거니와 최소한 하이버네이트 3.2가 필요하다. 여러분은 하이버네이트 3.2와 하이버네이트 어노테이션 라이브러리 둘 모두 하이버네이트 웹 사이트에서 다운로드 할 수 있다. 표준 하이버네이트 JAR 파일과 의존 라이브러리(dependencies)에 추가적으로 하이버네이트 어노테이션 .jar 파일(hibernate-annotations.jar)과 자바 퍼시스턴스 API 파일(lib/ejb3-persistence.jar)도 필요할 것이다. 만약 여러분이 Maven을 사용하고 있을 경우 아래에 나타나 있는 것과 같이 적절한 의존 라이브러리를 여러분의 POM 파일에 추가하기만 하면 된다.
...
org.hibernate
hibernate
3.2.1.ga
org.hibernate
hibernate-annotations
3.2.0.ga
javax.persistence
persistence-api
1.0
...
다음 단계는 하이버네이트 세션 팩토리(Hibernate session factory)를 획득하는 것이다. 여러분은 하이버네이트 어노테이션을 사용하여 조금 다르게 할 수 있는데, 많이 바꿔야 하는 것은 아니다. 여러분은 단지 여러분의 세션 팩토리를 초기화하는데 AnnotationConfiguration 클래스를 사용하도록 하기만 하면 된다.
sessionFactory = new AnnotationConfiguration().buildSessionFactory();
일반적으로 여러분이 퍼시스턴스 클래스를 선언하는데
엘리먼트를 사용하고 있을지라도 하이버네이트 설정 파일(보통 hibernate.cfg.xml 파일)에 여러분의 퍼시스턴스 클래스를 선언할 필요는 있다:
근래의 자바 프로젝트 중 상당수는 스프링(Spring)과 같은 경량 애플리케이션 프레임워크를 사용하고 있다. 만약 여러분이 스프링 프레임워크를 사용하고 있다면 아래에 나타나 있는 것처럼 AnnotationSessionFactoryBean 클래스를 사용하여 손쉽게 어노테이션 기반의 하이버네이트 세션 팩토리를 초기화할 수 있다.
org.hibernate.dialect.DerbyDialect
create
...
com.onjava.modelplanes.domain.PlaneType
com.onjava.modelplanes.domain.ModelPlane
...
첫 번째 퍼시스턴스 클래스
이제 어노테이션 기반의 하이버네이트 세션을 획득하는 방법을 알았으니 어노테이션 퍼시스턴스 클래스가 어떤 모습을 띠고 있는지 알아보기로 하자.
어노테이션이 적용된 퍼시스턴스 클래스는 다른 하이버네이트 애플리케이션에서 볼 수 있는 일반 POJO이다. 여러분의 자바 퍼시스턴스 API(javax.persistence.*)에는 분명 의존 라이브러리를 추가해야 할 필요가 있을 것이며 만약 여러분이 하이버네이트에 특화된 확장기능을 사용하고 있을 경우 하이버네이트 어노테이션 패키지(org.hibernate.annotations.*)에도 어쩌면 추가해야 할지도 모르겠지만 그것들은 보통의 퍼시스턴스 관련 어노테이션이 추가된 POJO에 지나지 않는다. 아래에 간단한 예제가 나타나 있다:
@Entity
public class ModelPlane {
private Long id;
private String name;
@Id
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
앞서 언급했듯이 상당히 간단하다. @Entity 어노테이션은 퍼시스턴스화될 클래스를 선언한다. @Id 어노테이션은 여러분이 이 클래스의 어느 속성이 유일한 식별자인지를 가리키도록 해준다. 사실 여러분은 필드(어노테이션을 적용한 멤버 변수)나 속성(어노테이션을 적용한 접근자 메소드), 어느 쪽도 퍼시스턴스화 할 수 있다. 이 기사의 나머지 부분에서는 속성에 기반한 어노테이션을 사용할 것이다. 어노테이션 기반 퍼시스턴스화의 한 가지 멋진 점은 기본값(default value)을 집중적으로 활용(“설정보다는 관습”의 유행에 따라)할 수 있다는 것이다. 예를 들어 여러분은 굳이 모든 속성을 퍼시스턴스화되도록 선언할 필요가 없는데, 여러분이 직접 @Transient 어노테이션을 사용하여 퍼시스턴스화하지 않도록 명시하지 않으면 모든 속성은 퍼시스턴스화될 것으로 간주될 것이기 때문이다. 이는 여러분의 코드를 단순하게 만들어주며 또한 고전적인 XML 맵핑 파일보다 훨씬 더 타이핑을 덜 할 수 있도록 해준다.
주키(Primary Keys) 생성하기
한 가지 하이버네이트가 좋은 점은 하이버네이트가 자동적으로 주키를 생성해 준다는 것이다. 하이버네이트/EJB 3 어노테이션 또한 다양한 전략을 구사할 수 있도록 해줌으로써 자동 키 생성에 관한 풍부한 지원을 제공해 준다. 다음 예제는 하이버네이트가 기반 데이터베이스에 의존하여 적절한 키 생성 전략을 결정하는데 자주 사용되는 접근 방법을 보여준다.
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
테이블과 필드 맵핑 조정하기
기본적으로 하이버네이트는 일치하는 이름을 가진 테이블과 필드에 퍼시스턴스 클래스를 맵핑시킨다. 예를 들어 위의 클래스는 다음 줄에 나오는 테이블에 맵핑될 것이다.
CREATE TABLE MODELPLANE
(
ID long,
NAME varchar
)
여러분이 직접 데이터베이스를 생성하고 관리할 경우에는 이렇게 하는 것이 적당하고 또 여러분의 코드를 갖고 나갈 수 있을 경우에도 훨씬 더 유지보수하기가 용이하다. 그렇다고 해서 그것이 모든 사람의 요구를 만족시키는 것은 아니다. 어떤 애플리케이션은 외부 데이터베이스에 접근할 필요도 있을 수 있고, 또 다른 어떤 것은 회사 데이터베이스 명명 규칙을 검사 맡아야 할 필요도 있을 지 모른다. 필요하면 여러분은 @Table과 @Column 어노테이션을 사용하여 여러분의 퍼시스턴스 맵핑을 아래에 나타나 있는 것과 같이 조정할 수도 있다.
@Entity
@Table(name="T_MODEL_PLANE")
public class ModelPlane {
private Long id;
private String name;
@Id
@Column(name="PLANE_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name="PLANE_NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
이는 아래에 나와 있는 테이블에 맵핑될 것이다:
CREATE TABLE T_MODEL_PLANE
(
PLANE_ID long,
PLANE_NAME varchar
)
또한 여러분은 다른 테이블과 컬럼 속성을 사용하여 여러분의 맵핑을 조정할 수도 있다. 이는 여러분이 컬럼 길이, 널 값 금지 제약과 같은 세부사항을 지정할 수 있도록 해준다. 하이버네이트는 이러한 어노테이션들에 대하여 수많은 속성들을 지원해 준다. 아래에 그것들 중 몇 가지가 나타나 있다.
...
@Column(name="PLANE_ID", length=80, nullable=true)
public String getName() {
return name;
}
...
관계 맵핑하기
자바 퍼시스턴스 맵핑의 여러 부분들 중에서 가장 중요하고 복잡한 것 중의 하나가 바로 테이블간의 관계를 맵핑하는 방법을 결정하는 것이다. 여느 것과 마찬가지로 하이버네이트 또한 이 영역에서 상당한 유연성을 제공해 주는데, 어느 정도의 복잡성을 감수해야 한다는 논쟁의 여지는 있다. 우리는 몇 가지 일반적인 사례를 들어 어노테이션을 사용하여 이것이 어떻게 이루어지는지에 대해 알아보기로 할 것이다.
가장 일반적으로 사용되는 관계 중 하나가 바로 다 대 일(many-to-one)의 관계이다. 위의 예제에서 각각의 ModelPlane이 다 대 일 관계(바꾸어 말하자면 비록 주어진 비행기 유형이 여러 개의 모형 비행기와 연관될 수 있기는 하지만 각각의 모형 비행기는 정확히 하나의 비행기 유형에 연관된다고 하자)로 PlaneType과 연관되어 있다고 가정해 보자. 여러분은 다음과 같이 맵핑시킬 수 있을 것이다:
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
public PlaneType getPlaneType() {
return planeType;
}
CascadeType 값은 하이버네이트가 cascading 연산을 어떻게 처리할지를 나타낸다.
일반적으로 사용되는 또 다른 관계는 위의 것과 정 반대이며 컬렉션(Collection)으로도 알려져 있는 일 대 다(one-to-many)의 관계이다. 컬렉션은 우리가 이전 형식의 하이버네이트 맵핑을 사용할 때와 어노테이션을 사용할 때 모두 복잡한 녀석이므로 여기에서는 표면의 장막만 걷어내어 여러분에게 어떻게 처리하는지만 알려주도록 하겠다. 예를 들어 위 예제에서 각각의 PlaneType 객체는 ModelPlanes의 컬렉션을 포함할 수도 있다. 그러면 여러분은 다음과 같이 맵핑할 수 있다:
@OneToMany(mappedBy="planeType",
cascade=CascadeType.ALL,
fetch=FetchType.EAGER)
@OrderBy("name")
public List getModelPlanes() {
return modelPlanes;
}
네임드 쿼리(Named Queries)
하이버네이트의 멋진 기능 중 하나는 여러분의 맵핑 파일 내에서 네임드 쿼리를 선언할 수 있도록 해주는 것이다. 이러한 쿼리들은 코드 내에서 이름을 통해 호출될 수 있으며 이는 여러분이 쿼리를 한데 모아 애플리케이션 도처에 SQL이나 HQL 코드가 흩어지는 것을 피할 수 있도록 해준다.
여러분은 이것 또한 어노테이션을 이용하여 할 수 있는데, 아래에 나타나 있는 것과 같이 @NamedQueries와 @NamedQuery 어노테이션을 사용한다:
@NamedQueries(
{
@NamedQuery(
name="planeType.findById",
query="select p from PlaneType p left join fetch p.modelPlanes where id=:id"
),
@NamedQuery(
name="planeType.findAll",
query="select p from PlaneType p"
),
@NamedQuery(
name="planeType.delete",
query="delete from PlaneType where id=:id"
)
}
)
한번 정의해 놓기만 하면 여러분은 다른 어떠한 네임드 쿼리도 언제든지 호출할 수 있다.
결론
하이버네이트 3 어노테이션은 강력하고 세련된 API를 제공하여 여러분의 자바 데이터베이스 퍼시스턴스 코드를 단순화시켜 주는데, 사실 여기에서 해본 것들은 겉핥기에 지나지 않는다. 여러분이 표준 기술을 고집하여 자바 퍼시스턴스 API(Java Persistence API)를 사용하려 마음먹을 수도 있고, 어느 정도 이식성을 희생하는 대가로 좀 더 강력하고 유연성을 제공해 주는 하이버네이트에 특화된 확장기능만을 이용할 수도 있다. 어느 경우이든지 간에 XML 맵핑 파일에 대한 필요성을 제거하여 하이버네이트 어노테이션을 사용하는 것은 여러분의 애플리케이션에 대한 유지보수가 단순하게 하는 것과 여러분에게 EJB 3 세계로 자연스럽게 입문할 수 있는 부수적인 이득도 함께 얻을 수 있다. 확인해 보시라!
리소스