반응형
Hibernate에서 객체와 테이블사이의 매핑 설정 파일의 기본 작성법, Hibernate 타입, 키값 생성기에 대해서 살펴본다.
ORM 설정 파일 기본 작성법
본 시리즈의 첫번째 퀵스타트에서 봤듯이, ORM 설정 파일은 XML 파일로 저장된다. ORM 설정 파일에 대해서 살펴보기 전에 먼저 본 글에서 예제로 사용할 자바빈 클래스를 살펴보자. 본 글에서 사용할 자바빈 클래스는 간단한 책 정보를 담고 있는 Book 클래스로서 아래와 같다.
위의 자바빈 객체와 대응되는 테이블은 아래와 같다.
오라클을 사용한다면 테이블 생성 스크립트는 아래와 같을 것이다.
또한, 본 글에서는 오라클을 기준으로 BOOK_ID 컬럼에 삽입될 값을 오라클 시퀀스로부터 가져오도록 예제를 설정할 것이며, 이때 사용할 시퀀스는 아래와 같은 스크립트를 이용해서 생성해주면 된다.
앞서 생성한 Book 클래스와 BOOK 테이블 사이의 매핑을 처리해주는 매핑 설정 파일은 아래와 같이 작성할 수 있을 것이다.
위 매핑 설정 파일은 가장 간단한 1대1로 이뤄진 객체-테이블 매핑을 보여주는 가장 간단한 형태의 매핑 설정 파일의 예이다. OR 매핑을 설정하는 가장 기본적인 형태이므로 Hibernate를 사용할 때 반드시 숙지해야 하는 코드이기도 하다.
위 코드에서 사용된 각 태그는 다음과 같은 의미를 지닌다.
<hibernate-mapping> 태그
hibernate-mapping 태그는 Hibernate가 사용하는 OR 매핑 설정 XML 파일의 루트 태그로서 XML 문서가 Hibernate 매핑 문서임을 나타낸다. 앞서 예제 OR 매핑 파일에서 볼 수 있듯이, hibernate-mapping 태그는 필수 속성을 갖고 있지 않다. hibernate-mapping 태그에서 옵션으로 사용할 수 있는 속성은 아래와 같이 네 개가 존재한다.
schema 속성은 Hibernate가 내부적으로 생성하는 쿼리와 연관되는 데, 예를 들어, schema 속성의 값을 'javacan'이라고 주었다면 아래와 같이 테이블 이름 앞에 데이터베이스 스키마 명이 붙은 쿼리를 내부적으로 사용하게 된다.
<class> 태그
OR 매핑 파일은 다음과 같이 여러 개의 class 태그를 가질 수 있다.
하나의 class 태그는 기본적으로 하나의 클래스와 하나의 테이블에 대한 매핑 정보를 담는데, class 태그를 사용해서 어떤 클래스가 어떤 테이블과 매핑되는 지에 대한 정보 및 기타 CRUD 처리와 관련된 속성 정보를 명시하게 된다. 가장 기본저인 형태는 아래와 같다.
class 태그의 가장 중요한 속성은 name과 table이다. 이 두 속성은 아래와 같다.
이 두 속성 외에 다음 표와 같은 속성을 사용할 수 있다.
이 외에 여러 속성들이 존재하는데, 이들 속성들은 본 시리즈 글을 진행하면서 필요할 때에 추가적으로 설명하겠다.
<id> 태그
DB 테이블로부터 읽어온 데이터를 저장하기 위해서 사용되는 객체는 대부분 PK 값을 저장하기 위해 자바빈 스타일의 프로퍼티를 제공한다. (앞서 작성했던 Book 클래스도 id 프로퍼티를 제공하고 있다.) id 태그는 테이블의 PK 컬럼에 매핑되는 프로퍼티를 명시할 때 사용되며, 구문은 다음과 같다.
각 속성은 다음과 같은 의미를 지닌다.
만약 name 속성을 입력하지 않으면 클래스가 식별자 프로퍼티를 갖고 있지 않다고 가정한다. 이 경우 Hibernate는 읽어온 객체를 변경하거나 삭제할 때 사용할 키값을 별도로 관리하게 된다.
<property> 태그와 <column> 태그
property 태그는 PK 컬럼 이외의 컬럼과 매핑되는 프로퍼티에 대한 정보를 명시할 때 사용된다. property 태그의 일반적인 형태는 아래와 같다.
첫번째 property 태그는 매핑될 컬럼 정보를 자식 태그인 column 태그에 명시하는 방법을 보여주고 있고, 두번째 property 태그는 컬럼 정보를 속성을 사용해서 명시하는 방법을 보여주고 있다. property 태그의 필수 속성은 name 뿐이며 나머지 속성은 hibernate가 기본값을 적용하여 알맞게 처리해준다. property 태그에서 사용가능한 속성을 다음 표와 같다.
type 속성은 타입명을 입력하는데, 타입명에는 다음과 같은 값이 올 수 있다.
<component> 태그를 이용한 값 저장
회원 정보는 다음과 같이 자택 주소와 직장 주소를 포함하는 것이 일반적이다.
위 코드에서 주소 자택 주소에 해당하는 필드와 직장 주소에 해당하는 필드는 동일한 구조를 갖고 있다. 따라서 자바에서는 다음과 같이 Address 객체를 사용해서 주소 정보를 담을 수가 있다.
주소를 Address 클래스를 사용하여 표현할 경우 MEMBER 테이블과 매핑될 Member 클래스는 다음과 같은 코드 형태를 취하게 될 것이다.
위 Member 클래스가 의미하는 것은 MEMBER 테이블의 집주소에 해당하는 세 개의 컬럼, 즉 HOME_ZIPCODE, HOME_ADDRESS1, HOME_ADDRESS2가 homeAddress 프로퍼티(즉, Address 객체에) 매핑된다는 것을 의미한다. 이렇게 테이블의 여러 컬럼이 하나의 프로퍼티에 매핑될 때 사용되는 태그가 component 태그이다. MEMBER 테이블과 Member 클래스 사이의 매핑에서 component 태그는 다음과 같이 사용된다.
component 태그의 name 속성과 class 속성은 다음과 같다.
component 태그가 나타내는 자바 클래스와 테이블 사이의 매핑 정보를 처리하기 위해서는 component 태그에 property 태그를 포함시키면 된다. component 태그에 중첩된 property 태그는 class 태그가 나타내는 클래스가 아닌 component 태그가 나타내는 클래스의 프로퍼티와 테이블 사이의 매핑을 표현하게 된다.
Hibernate의 타입
자바 객체의 프로퍼티 타입과 테이블의 컬럼 타입은 정확하게 일치하지 않는다. 예를 들어, 자바의 java.lang.String 타입의 프로퍼티가 SQL의 VARCHAR로 매핑될 수도 있고, CHAR로 매핑될 수도 있다. Hibernate는 이러한 타입의 불일치 문제를 해결하기 위해서 Hibernate 자체적으로 타입을 제공하고 있으며, 이 타입을 사용해서 자바의 타입과 SQL 타입 사이의 매핑 문제를 해결하고 있다.
Hibernate가 제공하는 매핑 타입은 자바의 기본 데이터 타입, 날짜 및 시간 관련 타입, 대량 데이터 관련 타입 등이 존재한다. 이들 타입은 property 태그나 id 태그 등 프로퍼티와 컬럼 사이의 매핑 정보를 표시하는 부분에서 사용되며, 이 타입 정보를 통해 Hibernate는 OR 매핑을 올바르게 처리할 수 있게 된다.
자바 기본 데이터 타입 매핑
자바의 기본 데이터 타입의 매핑을 처리해주는 Hibernate 타입은 다음과 같다.
날짜 및 시간 타입 매핑
자바의 날짜 및 시간 타입의 매핑을 처리해주는 Hibernate 타입은 다음과 같다.
대량 데이터 관련 매팅 타입
바이너리 데이터와 대량 데이터를 처리할 때 사용되는 Hibernate 타입은 다음과 같다.
JDBC 드라이버중에 clob이나 blob의 경우는 지원하지 않은 경우도 있으므로, clob과 blob을 사용할 때에는 먼저 JDBC 드라이버가 해당 타입을 지원하는지의 여부부터 확인해야 한다.
키 값 생성기
앞서 예로 들었던 OR 매핑 설정 파일의 일부를 다시 한번 살펴보자.
위 코드에서 id 태그는 테이블의 PK 컬럼과 매핑을 처리할 때 사용된다고 했으며, 새롭게 생성한 자바 객체를 테이블에 저장할 때 사용할 키값은 generator 태그에서 명시한 클래스를 통해서 생성된다. generator 태그는 키값을 생성할 클래스와 클래스가 사용할 파라미터 값을 전달하는데 사용되며, 보통 다음과 같은 형태를 갖는다.
클래스 이름에는 net.sf.hibernate.id.IdentifierGenerator 인터페이스를 구현한 클래스의 완전한 이름을 적어주면 된다. Hibernate는 몇가지 형태의 키값 생성기를 기본적으로 제공하고 있으며, 이들 생성기를 짧은 이름을 통해서 사용할 수 있도록 하고 있다. 다음은 Hibernate가 제공하는 기본 키값 생성기의 이름이다.
increment는 DBMS에 상관없이 사용할 수 있는 키값 생성기로서 long, short, int 타입의 식별값을 생성한다. increment 생성기는 파라미터 값을 필요로 하지 않으며, 다음과 같이 사용할 수 있다.
클러스터(cluster)에서는 사용하면 안 된다.
identity 생성기
DB2, MySQL, MS SQL 서버, Sybase 그리고 HypersonicSQL의 식별 컬럼을 지원한다. (예를 들어, MySQL의 auto_increment 컬럼) 리턴된 식별값의 타입은 long, short, int 중 하나이다. increment 생성기와 마찬가지로 파라미터를 필요로 하지 않으며, 다음과 같이 생성기의 이름만 지정해주면 된다.
sequence 생성기
DB2, 오라클, PostgreSQL, SAP DB, McKoi의 시퀀스나 Interbase의 generator를 사용해서 키값을 생성한다. 리턴된 식별자의 타입은 long, short, int 중 하나이다. sequence 생성기는 아래와 같이 sequence 파라미터로부터 사용할 시퀀스의 이름을 전달받는다.
hilo / seqhilo 생성기
hilo 생성기는 hi/lo 알고리즘을 사용한다. 알고리즘에서 hi 값을 읽어올 때 사용할 테이블과 컬럼의 이름을 table 파라미터와 column 파라미터로부터 읽어온다. (테이블과 컬럼의 이름을 명시하지 않을 경우 기본값은 테이블 이름은 'hi_value'이고 컬럼 이름은 'next_value'이다.) 또한 hi/lo 알고리즘에서 사용될 값을 max_lo 파라미터로부터 읽어온다. hilo 생성기는 long, short, int 타입의 식별값을 생성한다. 다음은 hilo 생성기의 사용예이다.
hilo 생성기를 사용할 때 주의할 점은 테이블이 미리 생성되어 있어야 하며, 테이블이 한개의 레코드가 존재해야 한다는 점이다. 또 다른 주의점은, 데이터베이스 Connection을 세션에 직접 제공하거나 또는 JTA를 사용하기 위해서 어플리케이션 서버가 제공하는 DataSource를 사용하는 경우에는 hilo 생성기를 사용할 수 없다는 점이다.
seqhilo 생성기는 hilo 생성기와 비슷하나 알고리즘에서 사용할 값을 테이블이 아닌 시퀀스로부터 읽어온다는 차이점이 있다. 시퀀스의 이름은 sequence 파라미터로부터 전달받는다. 다음은 seqhilo 생성기의 사용예이다.
uuid.string / uuid.hex 생성기
uuid.string 생성기와 uuid.hex 생성기는 128비트 UUID 알고리즘을 사용하여 string 타입의 값을 생성한다. 차이점은 다음과 같다.
native 생성기
native 생성기는 DBMS가 제공하는 기능에 따라 identity, sequence, hilo 생성기를 선택적으로 사용한다.
assigned 생성기
assigned 생성기는 어플리케이션에서 직접 식별값을 입력한다는 것을 의미한다. assigned 생성기를 사용하면 객체를 저장하기 전에(즉, Session.save() 메소드를 호출하기 전에) 식별자에 키값을 입력해주어야 한다.
다음 글에서는
이번 글에서는 테이블과 객체의 매핑에 대해서만 살펴봤는데, 실제로는 객체 사이의 상속 관계와 테이블, 객체 사이의 연관과 테이블 사이의 연관 등 처리해줘야 할 것이 많다. 또한, 여러 컬럼이 PK가 되는 복합키에 대한 처리도 필요하다. 다음 글에서는 이처럼 좀더 복잡한 OR 매핑을 처리하는 방법에 대해서 살펴보도록 하자.
관련링크:
ORM 설정 파일 기본 작성법
본 시리즈의 첫번째 퀵스타트에서 봤듯이, ORM 설정 파일은 XML 파일로 저장된다. ORM 설정 파일에 대해서 살펴보기 전에 먼저 본 글에서 예제로 사용할 자바빈 클래스를 살펴보자. 본 글에서 사용할 자바빈 클래스는 간단한 책 정보를 담고 있는 Book 클래스로서 아래와 같다.
package javacan.hibernate.test;
public class Book {
private Integer id;
private String title;
private String author;
private String publishingCompany;
private int price;
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getPublishingCompany() {
return publishingCompany;
}
public void setPublishingCompany(String publishingCompany) {
this.publishingCompany = publishingCompany;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
public class Book {
private Integer id;
private String title;
private String author;
private String publishingCompany;
private int price;
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getPublishingCompany() {
return publishingCompany;
}
public void setPublishingCompany(String publishingCompany) {
this.publishingCompany = publishingCompany;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
위의 자바빈 객체와 대응되는 테이블은 아래와 같다.
BOOK 테이블 | |||
컬럼 | 타입 | NULL 여부 | 키 |
BOOK_ID | INTEGER | false | PK |
TITLE | VARCHAR(100) | false | |
AUTHOR | VARCHAR(100) | false | |
PUBLISHING | VARCHAR(100) | false | |
PRICE | INTEGER | false |
오라클을 사용한다면 테이블 생성 스크립트는 아래와 같을 것이다.
create table BOOK (
BOOK_ID INTEGER NOT NULL,
TITLE VARCHAR2(100) NOT NULL,
AUTHOR VARCHAR2(100) NOT NULL,
PUBLISHING VARCHAR2(100) NOT NULL,
PRICE INTEGER NOT NULL,
CONSTRAINT PK_BOOK PRIMARY KEY (BOOK_ID)
)
BOOK_ID INTEGER NOT NULL,
TITLE VARCHAR2(100) NOT NULL,
AUTHOR VARCHAR2(100) NOT NULL,
PUBLISHING VARCHAR2(100) NOT NULL,
PRICE INTEGER NOT NULL,
CONSTRAINT PK_BOOK PRIMARY KEY (BOOK_ID)
)
또한, 본 글에서는 오라클을 기준으로 BOOK_ID 컬럼에 삽입될 값을 오라클 시퀀스로부터 가져오도록 예제를 설정할 것이며, 이때 사용할 시퀀스는 아래와 같은 스크립트를 이용해서 생성해주면 된다.
CREATE SEQUENCE BOOK_ID_SEQ
START WITH 0
INCREMENT BY 1
MINVALUE 0
NOCACHE NOCYCLE NOORDER
START WITH 0
INCREMENT BY 1
MINVALUE 0
NOCACHE NOCYCLE NOORDER
앞서 생성한 Book 클래스와 BOOK 테이블 사이의 매핑을 처리해주는 매핑 설정 파일은 아래와 같이 작성할 수 있을 것이다.
<?xml version="1.0" encoding="euc-kr" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="javacan.hibernate.test.Book" table="BOOK">
<id name="id" type="integer" unsaved-value="null">
<column name="BOOK_ID" sql-type="INTEGER" not-null="true" />
<generator class="sequence">
<param name="sequence">BOOK_ID_SEQ</param>
</generator>
</id>
<property name="title" type="string">
<column name="TITLE" length="100" not-null="true" />
</property>
<property name="author" />
<property name="publishingCompany" column="PUBLISHING" />
<property name="price" type="integer" />
</class>
</hibernate-mapping>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="javacan.hibernate.test.Book" table="BOOK">
<id name="id" type="integer" unsaved-value="null">
<column name="BOOK_ID" sql-type="INTEGER" not-null="true" />
<generator class="sequence">
<param name="sequence">BOOK_ID_SEQ</param>
</generator>
</id>
<property name="title" type="string">
<column name="TITLE" length="100" not-null="true" />
</property>
<property name="author" />
<property name="publishingCompany" column="PUBLISHING" />
<property name="price" type="integer" />
</class>
</hibernate-mapping>
위 매핑 설정 파일은 가장 간단한 1대1로 이뤄진 객체-테이블 매핑을 보여주는 가장 간단한 형태의 매핑 설정 파일의 예이다. OR 매핑을 설정하는 가장 기본적인 형태이므로 Hibernate를 사용할 때 반드시 숙지해야 하는 코드이기도 하다.
위 코드에서 사용된 각 태그는 다음과 같은 의미를 지닌다.
- hibernate-mapping : OR 매핑 정보를 담고 있는 XML의 루트 태그
- class : OR 매핑에서 사용되는 자바 클래스에 대한 정보를 표시
- id : 테이블의 PK 컬럼을 저장할 자바빈 프로퍼티에 대한 정보를 표시
- property : 테이블의 컬럼과 매핑될 자바빈 프로퍼티에 대한 정보를 표시
- column : 자바빈 프로퍼티와 매핑되는 테이블의 컬럼에 대한 정보를 표시
- generator : 테이블에 객체를 저장할 때 사용되는 키 값 생성기
<hibernate-mapping> 태그
hibernate-mapping 태그는 Hibernate가 사용하는 OR 매핑 설정 XML 파일의 루트 태그로서 XML 문서가 Hibernate 매핑 문서임을 나타낸다. 앞서 예제 OR 매핑 파일에서 볼 수 있듯이, hibernate-mapping 태그는 필수 속성을 갖고 있지 않다. hibernate-mapping 태그에서 옵션으로 사용할 수 있는 속성은 아래와 같이 네 개가 존재한다.
속성 | 설명 |
schema | 데이터베이스 스키마의 이름을 명시한다. |
default-cascade | 기본적으로 사용할 케스케이딩 전략을 명시한다. 테이블 사이의 연관 관계가 있을 때, 테이블에 자체의 케스케이딩 전략이 없으면 이 속성의 값에서 명시한 전략을 사용한다. 'none'과 'save-update'를 값으로 가질 수 있으며, 기본값은 'none'이다. |
auto-import | 이 매핑 파일에 있는 클래스 설정 중에서 (패키지 이름을 포함한) 완전한 이름을 명시하지 않은 클래스를QL(Query Language)에서 사용할 수 있는지의 여부를 명시한다. true와 false를 값으로 가지며, 기본값은 true이다. |
package | 패키지 이름을 포함하지 않은 클래스 이름의 완전한 이름을 유추할 때 사용할 기본 패키지 이름을 명시한다. |
schema 속성은 Hibernate가 내부적으로 생성하는 쿼리와 연관되는 데, 예를 들어, schema 속성의 값을 'javacan'이라고 주었다면 아래와 같이 테이블 이름 앞에 데이터베이스 스키마 명이 붙은 쿼리를 내부적으로 사용하게 된다.
select ... from javacan.tableName ...
<class> 태그
OR 매핑 파일은 다음과 같이 여러 개의 class 태그를 가질 수 있다.
<?xml version="1.0" encoding="euc-kr" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="javacan.hibernate.test.Book" table="BOOK">
...
</class>
<class name="javacan.hibernate.test.PublishingCompany" table="PUBLISHING">
...
</class>
</hibernate-mapping>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="javacan.hibernate.test.Book" table="BOOK">
...
</class>
<class name="javacan.hibernate.test.PublishingCompany" table="PUBLISHING">
...
</class>
</hibernate-mapping>
하나의 class 태그는 기본적으로 하나의 클래스와 하나의 테이블에 대한 매핑 정보를 담는데, class 태그를 사용해서 어떤 클래스가 어떤 테이블과 매핑되는 지에 대한 정보 및 기타 CRUD 처리와 관련된 속성 정보를 명시하게 된다. 가장 기본저인 형태는 아래와 같다.
<class name="javacan.hibernate.test.Book" table="BOOK">
... <!-- 프로퍼티와 컬럼에 대한 매핑 정보 설정 -->
</class>
... <!-- 프로퍼티와 컬럼에 대한 매핑 정보 설정 -->
</class>
class 태그의 가장 중요한 속성은 name과 table이다. 이 두 속성은 아래와 같다.
- name - 퍼스시턴트 클래스의 완전한 자바 클래스(또는 인터페이스) 이름
- table - 데이터베이스 테이블의 이름
이 두 속성 외에 다음 표와 같은 속성을 사용할 수 있다.
속성 | 설명 |
schema | 테이블을 소유한 스키마를 명시한다. |
dynamic-update | 런타임에, 값이 변경되는 컬럼에 대해서만 SQL UPDATE 쿼리를 동적으로 생성할지의 여부를 명시한다. true 또는 false를 값으로 갖는다. 기본값은 false이다. |
dynamic-insert | 런타임에, 값이 null이 아닌 컬럼에 대해서만 SQL INSERT 쿼리를 동적으로 생성할지의 여부를 명시한다. true 또는 false를 값으로 갖는다. 기본값은 false이다. |
select-before-update | 객체가 실제로 변경되는 것이 아닌 경우 SQL UPDATE 쿼리를 수행하지 않을 지의 여부를 명시한다. 기본값은 false이다. 이 값을 true로 지정할 경우 Hibernate는 Session.update() 메소드를 호출할 때, UPDATE 쿼리를 수행할 필요가 있는 지의 여부를 검사하기 위해 SELECT 쿼리를 수행하게 된다. (성능이 좋지 않으므로 트리거와 같이 DBMS가 지원하는 기능으로 대체하는 것이 좋다.) |
이 외에 여러 속성들이 존재하는데, 이들 속성들은 본 시리즈 글을 진행하면서 필요할 때에 추가적으로 설명하겠다.
<id> 태그
DB 테이블로부터 읽어온 데이터를 저장하기 위해서 사용되는 객체는 대부분 PK 값을 저장하기 위해 자바빈 스타일의 프로퍼티를 제공한다. (앞서 작성했던 Book 클래스도 id 프로퍼티를 제공하고 있다.) id 태그는 테이블의 PK 컬럼에 매핑되는 프로퍼티를 명시할 때 사용되며, 구문은 다음과 같다.
<id name = "프로퍼티이름"
type = "Hibernate타입"
column = "테이블컬럼이름"
unsaved-value = "any|none|null|id_value"
generator class="generatorClass" />
</id>
type = "Hibernate타입"
column = "테이블컬럼이름"
unsaved-value = "any|none|null|id_value"
generator class="generatorClass" />
</id>
각 속성은 다음과 같은 의미를 지닌다.
속성 | 설명 |
name | 식별자로 사용될 프로퍼티의 이름 (옵션) |
type | Hibernate 타입 (옵션) |
column | PK 컬럼의 이름. 입력하지 않은 경우 프로퍼티의 이름을 사용한다. (옵션) |
unsaved-value | 새로 생성한 객체를 아직 저장하지 않았음을 나타낼 때 사용할 식별자 프로퍼티의 값. Hibernate는 식별자 프로퍼티의 값이 이 속성에 명시한 값과 가을 경우, 객체가 DB 테이블에 저장되어 있지 않은 새롭게 생성된 객체라고 가정한다. 기본값은 null이다. (옵션) |
만약 name 속성을 입력하지 않으면 클래스가 식별자 프로퍼티를 갖고 있지 않다고 가정한다. 이 경우 Hibernate는 읽어온 객체를 변경하거나 삭제할 때 사용할 키값을 별도로 관리하게 된다.
<property> 태그와 <column> 태그
property 태그는 PK 컬럼 이외의 컬럼과 매핑되는 프로퍼티에 대한 정보를 명시할 때 사용된다. property 태그의 일반적인 형태는 아래와 같다.
<class name=".." table="..">
<id .... > .. </id>
<property name="title" type="string">
<column name="TITLE" length="100" not-null="true" />
</property>
<property name="author" column="AUTHOR" />
...
</class>
<id .... > .. </id>
<property name="title" type="string">
<column name="TITLE" length="100" not-null="true" />
</property>
<property name="author" column="AUTHOR" />
...
</class>
첫번째 property 태그는 매핑될 컬럼 정보를 자식 태그인 column 태그에 명시하는 방법을 보여주고 있고, 두번째 property 태그는 컬럼 정보를 속성을 사용해서 명시하는 방법을 보여주고 있다. property 태그의 필수 속성은 name 뿐이며 나머지 속성은 hibernate가 기본값을 적용하여 알맞게 처리해준다. property 태그에서 사용가능한 속성을 다음 표와 같다.
속성 | 설명 |
name | 프로퍼티의 이름. 첫번째 글자는 소문자이다. (옵션) |
column | 프로퍼티와 매핑될 테이블의 컬럼 이름. 기본값은 프로퍼티 이름이다. (옵션) |
type | 타입명을 입력한다. (옵션) |
unique | UNIQUE 컬럼과 매핑될 경우 true를 입력한다. 기본값은 false. (옵션) |
update | 매핑될 컬럼이 SQL의 UPDATE 쿼리에 포함될지의 여부를 나타낸다. 기본값은 true. (옵션) |
insert | 매핑될 컬럼이 SQL의 INSERT 쿼리에 포함될지의 여부를 나타낸다. 기본값은 true. (옵션) |
formula | 프로퍼티가 계산에 의해서 구해지는 경우, 프로퍼티의 값을 구할 때 사용되는 SQL 수식을 입력. 계산된 값을 갖는 프로퍼티는 매핑되는 컬럼을 갖지 못한다. (옵션) |
type 속성은 타입명을 입력하는데, 타입명에는 다음과 같은 값이 올 수 있다.
- Hibernate 기본 타입 (예, integer, string, date, binary 등)
- Hibernate 기본 타입과 관련된 자바 클래스 이름 (예, int, java.lang.String, java.util.Date, java.sql.Clob)
- PersistentEnum의 하위 클래스 이름
- 직렬화 가능한 자바 클래스 이름
- 커스텀 타입의 클래스 이름
<component> 태그를 이용한 값 저장
회원 정보는 다음과 같이 자택 주소와 직장 주소를 포함하는 것이 일반적이다.
위 코드에서 주소 자택 주소에 해당하는 필드와 직장 주소에 해당하는 필드는 동일한 구조를 갖고 있다. 따라서 자바에서는 다음과 같이 Address 객체를 사용해서 주소 정보를 담을 수가 있다.
public class Address {
private String zipCode;
private String address1;
private String address2;
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getZipCode() {
return zipCode;
}
.... // 관련 get/set 메소드
}
private String zipCode;
private String address1;
private String address2;
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getZipCode() {
return zipCode;
}
.... // 관련 get/set 메소드
}
주소를 Address 클래스를 사용하여 표현할 경우 MEMBER 테이블과 매핑될 Member 클래스는 다음과 같은 코드 형태를 취하게 될 것이다.
public class Member {
private String id;
private String name;
private String homeAddress;
private String jobAddress;
....
public void setHomeAddress(Address address) {
homeAddress = address;
}
public Address getHomeAddress() {
return homeAddress;
}
public void setJobAddress(Address address) {
jobAddress = address;
}
public Address getJobAddress() {
return jobAddress;
}
}
private String id;
private String name;
private String homeAddress;
private String jobAddress;
....
public void setHomeAddress(Address address) {
homeAddress = address;
}
public Address getHomeAddress() {
return homeAddress;
}
public void setJobAddress(Address address) {
jobAddress = address;
}
public Address getJobAddress() {
return jobAddress;
}
}
위 Member 클래스가 의미하는 것은 MEMBER 테이블의 집주소에 해당하는 세 개의 컬럼, 즉 HOME_ZIPCODE, HOME_ADDRESS1, HOME_ADDRESS2가 homeAddress 프로퍼티(즉, Address 객체에) 매핑된다는 것을 의미한다. 이렇게 테이블의 여러 컬럼이 하나의 프로퍼티에 매핑될 때 사용되는 태그가 component 태그이다. MEMBER 테이블과 Member 클래스 사이의 매핑에서 component 태그는 다음과 같이 사용된다.
<class name="Member" table="MEMBER" >
<id name="id" column="MEMBER_ID">
<generator class="assigned" />
</id>
<property name="name" />
<component name="homeAddress" class="Address">
<property name="zipCode" column="HOME_ZIPCODE" />
<property name="address1" column="HOME_ADDRESS1" />
<property name="address2" column="HOME_ADDRESS2" />
</component>
<component name="jobAddress" class="Address">
<property name="zipCode" column="JOB_ZIPCODE" />
<property name="address1" column="JOB_ADDRESS1" />
<property name="address2" column="JOB_ADDRESS2" />
</component>
...
</class>
<id name="id" column="MEMBER_ID">
<generator class="assigned" />
</id>
<property name="name" />
<component name="homeAddress" class="Address">
<property name="zipCode" column="HOME_ZIPCODE" />
<property name="address1" column="HOME_ADDRESS1" />
<property name="address2" column="HOME_ADDRESS2" />
</component>
<component name="jobAddress" class="Address">
<property name="zipCode" column="JOB_ZIPCODE" />
<property name="address1" column="JOB_ADDRESS1" />
<property name="address2" column="JOB_ADDRESS2" />
</component>
...
</class>
component 태그의 name 속성과 class 속성은 다음과 같다.
- name - component가 속한 클래스의 프로퍼티 이름.
- class - 프로퍼티의 자바 클래스 이름
component 태그가 나타내는 자바 클래스와 테이블 사이의 매핑 정보를 처리하기 위해서는 component 태그에 property 태그를 포함시키면 된다. component 태그에 중첩된 property 태그는 class 태그가 나타내는 클래스가 아닌 component 태그가 나타내는 클래스의 프로퍼티와 테이블 사이의 매핑을 표현하게 된다.
Hibernate의 타입
자바 객체의 프로퍼티 타입과 테이블의 컬럼 타입은 정확하게 일치하지 않는다. 예를 들어, 자바의 java.lang.String 타입의 프로퍼티가 SQL의 VARCHAR로 매핑될 수도 있고, CHAR로 매핑될 수도 있다. Hibernate는 이러한 타입의 불일치 문제를 해결하기 위해서 Hibernate 자체적으로 타입을 제공하고 있으며, 이 타입을 사용해서 자바의 타입과 SQL 타입 사이의 매핑 문제를 해결하고 있다.
Hibernate가 제공하는 매핑 타입은 자바의 기본 데이터 타입, 날짜 및 시간 관련 타입, 대량 데이터 관련 타입 등이 존재한다. 이들 타입은 property 태그나 id 태그 등 프로퍼티와 컬럼 사이의 매핑 정보를 표시하는 부분에서 사용되며, 이 타입 정보를 통해 Hibernate는 OR 매핑을 올바르게 처리할 수 있게 된다.
자바 기본 데이터 타입 매핑
자바의 기본 데이터 타입의 매핑을 처리해주는 Hibernate 타입은 다음과 같다.
매핑 타입 | 자바 타입 | SQL 타입 |
integer | int 또는 java.lang.Integer | INTEGER |
long | long 또는 java.lang.Long | BIGINT |
short | short 또는 java.lang.Short | SMALLINT |
float | float 또는 java.lang.Float | FLOAT |
double | double 또는 java.lang.Double | DOUBLE |
big_decimal | java.math.BigDecimal | NUMERIC |
character | java.lang.String | CHAR(1) |
string | java.lang.String | VARCHAR |
byte | byte 또는 java.lang.Byte | TINYINT |
boolean | boolean 또는 java.lang.Boolean | BIT |
yes_no | boolean 또는 java.lang.Boolean | CHAR(1) ('Y' 또는 'N') |
true_false | boolean 또는 java.lang.Boolean | CHAR(1) ('T' 또는 'F') |
날짜 및 시간 타입 매핑
자바의 날짜 및 시간 타입의 매핑을 처리해주는 Hibernate 타입은 다음과 같다.
매핑 타입 | 자바 타입 | SQL 타입 |
date | java.util.Date 또는 java.sql.Date | DATE |
time | java.util.Time 또는 java.sql.Time | TIME |
timestamp | java.util.Timestamp 또는 java.sql.Timestamp | TIMESTAMP |
calendar | java.util.Calendar | TIMESTAMP |
calendar_date | java.util.Calendar | DATE |
대량 데이터 관련 매팅 타입
바이너리 데이터와 대량 데이터를 처리할 때 사용되는 Hibernate 타입은 다음과 같다.
매핑 타입 | 자바 타입 | SQL 타입 |
binary | byte[] | VARBINARY (또는 BLOB) |
text | java.lang.String | CLOB |
serializable | java.io.Serializable를 구현한 모든 자바 클래스 | VARBINARY (또는 BLOB) |
clob | java.sql.Clob | CLOB |
blob | java.sql.Blob | BLOB |
JDBC 드라이버중에 clob이나 blob의 경우는 지원하지 않은 경우도 있으므로, clob과 blob을 사용할 때에는 먼저 JDBC 드라이버가 해당 타입을 지원하는지의 여부부터 확인해야 한다.
키 값 생성기
앞서 예로 들었던 OR 매핑 설정 파일의 일부를 다시 한번 살펴보자.
<hibernate-mapping>
<class name="javacan.hibernate.test.Book" table="BOOK">
<id name="id" type="integer" unsaved-value="null">
<column name="BOOK_ID" sql-type="INTEGER" not-null="true" />
<generator class="sequence">
<param name="sequence">BOOK_ID_SEQ</param>
</generator>
</id>
<property name="title" type="string">
<column name="TITLE" length="100" not-null="true" />
</property>
...
</class>
</hibernate-mapping>
<class name="javacan.hibernate.test.Book" table="BOOK">
<id name="id" type="integer" unsaved-value="null">
<column name="BOOK_ID" sql-type="INTEGER" not-null="true" />
<generator class="sequence">
<param name="sequence">BOOK_ID_SEQ</param>
</generator>
</id>
<property name="title" type="string">
<column name="TITLE" length="100" not-null="true" />
</property>
...
</class>
</hibernate-mapping>
위 코드에서 id 태그는 테이블의 PK 컬럼과 매핑을 처리할 때 사용된다고 했으며, 새롭게 생성한 자바 객체를 테이블에 저장할 때 사용할 키값은 generator 태그에서 명시한 클래스를 통해서 생성된다. generator 태그는 키값을 생성할 클래스와 클래스가 사용할 파라미터 값을 전달하는데 사용되며, 보통 다음과 같은 형태를 갖는다.
<generator class="클래스이름">
<param name="파라미터이름">파라미터값</param>
<param name="파라미터이름">파라미터값</param>
</generator>
<param name="파라미터이름">파라미터값</param>
<param name="파라미터이름">파라미터값</param>
</generator>
클래스 이름에는 net.sf.hibernate.id.IdentifierGenerator 인터페이스를 구현한 클래스의 완전한 이름을 적어주면 된다. Hibernate는 몇가지 형태의 키값 생성기를 기본적으로 제공하고 있으며, 이들 생성기를 짧은 이름을 통해서 사용할 수 있도록 하고 있다. 다음은 Hibernate가 제공하는 기본 키값 생성기의 이름이다.
- increment : 정수 타입의 키 값을 사용할 경우, 키 값을 1씩 증가시켜 생성한다.
- identity : 테이블의 식별 컬럼을 지원한다. (예, MySQL의 auto_increment 컬럼)
- sequence : 시퀀로부터 키 값을 가져온다. (예, 오라클의 시퀀스)
- hilo / seqhilo : hi/lo 알고리즘을 사용하여 정수값 키값을 생성한다.
- uuid.string / uuid.hex : UUID 알고리즘을 사용하여 키값을 생성한다.
- native : DBMS 및 테이블에 알맞은 키값 생성기를 자동 선택한다.
- assigned : 어플리케이션에 직접 키값을 생성한다.
- foreign : 연관 객체의 식별자를 키값으로 사용한다. <one-to-one> PK 연관에서 주로 사용된다.
increment는 DBMS에 상관없이 사용할 수 있는 키값 생성기로서 long, short, int 타입의 식별값을 생성한다. increment 생성기는 파라미터 값을 필요로 하지 않으며, 다음과 같이 사용할 수 있다.
<id name="id" type="integer" column="BOOK_ID" unsaved-value="null">
<generator class="increment" />
</id>
<generator class="increment" />
</id>
클러스터(cluster)에서는 사용하면 안 된다.
identity 생성기
DB2, MySQL, MS SQL 서버, Sybase 그리고 HypersonicSQL의 식별 컬럼을 지원한다. (예를 들어, MySQL의 auto_increment 컬럼) 리턴된 식별값의 타입은 long, short, int 중 하나이다. increment 생성기와 마찬가지로 파라미터를 필요로 하지 않으며, 다음과 같이 생성기의 이름만 지정해주면 된다.
<id name="id" type="integer" column="BOOK_ID" unsaved-value="null">
<generator class="identity" />
</id>
<generator class="identity" />
</id>
sequence 생성기
DB2, 오라클, PostgreSQL, SAP DB, McKoi의 시퀀스나 Interbase의 generator를 사용해서 키값을 생성한다. 리턴된 식별자의 타입은 long, short, int 중 하나이다. sequence 생성기는 아래와 같이 sequence 파라미터로부터 사용할 시퀀스의 이름을 전달받는다.
<id name="id" type="integer" column="BOOK_ID" unsaved-value="null">
<generator class="sequence">
<param name="sequence">BOOK_ID_SEQ</param>
</generator>
</id>
<generator class="sequence">
<param name="sequence">BOOK_ID_SEQ</param>
</generator>
</id>
hilo / seqhilo 생성기
hilo 생성기는 hi/lo 알고리즘을 사용한다. 알고리즘에서 hi 값을 읽어올 때 사용할 테이블과 컬럼의 이름을 table 파라미터와 column 파라미터로부터 읽어온다. (테이블과 컬럼의 이름을 명시하지 않을 경우 기본값은 테이블 이름은 'hi_value'이고 컬럼 이름은 'next_value'이다.) 또한 hi/lo 알고리즘에서 사용될 값을 max_lo 파라미터로부터 읽어온다. hilo 생성기는 long, short, int 타입의 식별값을 생성한다. 다음은 hilo 생성기의 사용예이다.
<id name="id" type="integer" column="BOOK_ID" unsaved-value="null">
<generator class="hilo">
<param name="table">HILO_VALUE</param>
<param name="column">NEXT_VAL</param>
<param name="max_lo">5</param>
</generator>
</id>
<generator class="hilo">
<param name="table">HILO_VALUE</param>
<param name="column">NEXT_VAL</param>
<param name="max_lo">5</param>
</generator>
</id>
hilo 생성기를 사용할 때 주의할 점은 테이블이 미리 생성되어 있어야 하며, 테이블이 한개의 레코드가 존재해야 한다는 점이다. 또 다른 주의점은, 데이터베이스 Connection을 세션에 직접 제공하거나 또는 JTA를 사용하기 위해서 어플리케이션 서버가 제공하는 DataSource를 사용하는 경우에는 hilo 생성기를 사용할 수 없다는 점이다.
seqhilo 생성기는 hilo 생성기와 비슷하나 알고리즘에서 사용할 값을 테이블이 아닌 시퀀스로부터 읽어온다는 차이점이 있다. 시퀀스의 이름은 sequence 파라미터로부터 전달받는다. 다음은 seqhilo 생성기의 사용예이다.
<id name="id" type="integer" column="BOOK_ID" unsaved-value="null">
<generator class="seqhilo">
<param name="sequence">BOOK_ID_SEQ</param>
<param name="max_lo">5</param>
</generator>
</id>
<generator class="seqhilo">
<param name="sequence">BOOK_ID_SEQ</param>
<param name="max_lo">5</param>
</generator>
</id>
uuid.string / uuid.hex 생성기
uuid.string 생성기와 uuid.hex 생성기는 128비트 UUID 알고리즘을 사용하여 string 타입의 값을 생성한다. 차이점은 다음과 같다.
- uuid.string - 16 개의 ASCII 글자로 구성된 식별값을 생성한다. PostgreSQL에서는 사용해서는 안 된다
- uuid.hex - 32 개의 16진수 숫자로 구성된 식별값을 생성한다.
<id name="id" type="string" column="CAT_ID" unsaved-value="null">
<generator class="uuid.string" />
</id>
<generator class="uuid.string" />
</id>
native 생성기
native 생성기는 DBMS가 제공하는 기능에 따라 identity, sequence, hilo 생성기를 선택적으로 사용한다.
assigned 생성기
assigned 생성기는 어플리케이션에서 직접 식별값을 입력한다는 것을 의미한다. assigned 생성기를 사용하면 객체를 저장하기 전에(즉, Session.save() 메소드를 호출하기 전에) 식별자에 키값을 입력해주어야 한다.
다음 글에서는
이번 글에서는 테이블과 객체의 매핑에 대해서만 살펴봤는데, 실제로는 객체 사이의 상속 관계와 테이블, 객체 사이의 연관과 테이블 사이의 연관 등 처리해줘야 할 것이 많다. 또한, 여러 컬럼이 PK가 되는 복합키에 대한 처리도 필요하다. 다음 글에서는 이처럼 좀더 복잡한 OR 매핑을 처리하는 방법에 대해서 살펴보도록 하자.
관련링크: