Mapping XML File은 모델 객체와 데이터베이스의 테이블간의 매핑 정보를 담고 있는 설정 파일이다.
Mapping File를 작성할 때는 일정한 규약이 있으며
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd에 맞게 작성을 해야 한다.
다음은 Mapping File 작성 방법, Java Data Type와 DB의 Data Type과의 매핑, 그리고 Hibernate
Generator에 대한 이다.
Data Type의 매핑
Data Type의 매핑
Mapping File에서 프로퍼티 파일에 대한 기본 설정 방법은 위에서 언급한 첫 처럼 다음과 같다.
<property name="countryId" type="string">
<column name="COUNTRY_ID" length="2" not-null="true" />
</property>
위의 설정에서 보면 type는 countryId라는 자바 프로퍼티와 CONUTRY_ID라는 DB컬럼의 매핑을 위해 설정한다. type에 설정 된
매핑 타입을 이용해 자바 프로퍼티와 DB컴럼 사이의 값을 알맞게 변환한다. type에 설정되는 매핑 타입은 Hibernate에서 기본적으로
제공하는 것 외에 개발자가 커스터마이즈할 수 있다.
Java Primitive Mapping Type
다음은 Hibernate에서 제공하는 Java primitive mapping type이다. (참고 : Oracale Data Type는 Oracle 10g에서 테이블 생성 시 설정할 Column Type 이다.)
| Mapping Type |
Java Type |
Standard SQL built-in type |
Oracle Column Type |
| integer |
int or java.lang.Integer |
INTEGER |
NUMBER(10,0) |
| long |
long or java.lang.Long |
BIGINT |
NUMBER(19,0) |
| short |
short or java.lang.Short |
SMALLINT |
NUMBER(5,0) |
| float |
float or java.lang.Float |
FLOAT |
FLOAT |
| double |
double or java.lang.Double |
DOUBLE |
DOUBLE PRECISION |
| big_decimal |
java.math.BigDecimal |
NUMERIC |
NUMBER(19,2) |
| character |
char or java.lang.Character |
CHAR(1) |
CHAR(1 CHAR) |
| string |
java.lang.String |
VARCHAR |
VARCHAR2(255 CHAR) |
| byte |
byte or java.lang.Byte |
TINYINT |
NUMBER(3,0) |
| boolean |
boolean or java.lang.Boolean |
BIT |
NUMBER(1,0) |
| yes_no |
boolean or java.lang.Boolean |
CHAR(1) ( ture : false = Y : N ) |
CHAR(1 CHAR) |
| true_false |
boolean or java.lang.Boolean |
CHAR(1) ( ture : false = T : F ) |
CHAR(1 CHAR) |
위 표를 참고하여 자바 프로퍼티와 DB 컬럼 type에 맞게 설정하면 된다. 다음은 Java primaitive type을 테스트 하기 위해 작성한
JavaDataType.java 파일의 일부이다.
public class JavaDataType {
private int id;
private int intType;
private long longType;
private short shortType;
private float floatType;
private double doubleType;
private java.math.BigDecimal bigDecimalType;
private String stringType;
private char charType;
private byte byteType;
private boolean booleanType;
private boolean yesNoType;
private boolean trueFalseType;
<!-- 중략 -->
아래는 JavaDataType.java에 정의 된 프로퍼티 타입과 DB Coulmn타입 매핑 설정을 위해 작성한
JavaDataType.hbm.xml파일의 일부이다.
<property name="intType" column="INT_TYPE" type="int"/>
<property name="longType" column="LONG_TYPE" type="long"/>
<property name="shortType" column="SHORT_TYPE" type="short"/>
<property name="floatType" column="FLOAT_TYPE" type="float"/>
<property name="doubleType" column="DOUBLE_TYPE" type="double"/>
<property name="bigDecimalType" column="BIGDECIMAL_TYPE" type="big_decimal"/>
<property name="charType" column="CHAR_TYPE" type="character"/>
<property name="stringType" column="STRING_TYPE" type="string"/>
<property name="byteType" column="BYTE_TYPE" type="byte"/>
<property name="booleanType" column="BOOLEAN_TYPE" type="boolean"/>
<property name="yesNoType" column="YES_NO_TYPE" type="yes_no"/>
<property name="trueFalseType" column="TRUE_FALSE_TYPE" type="true_false"/>
Java primitive type과 DB Column type에 대한
테스트 코드 보기Date And Time Mapping Type
아래는 Hibernate에서 제공하는 date와 time에 대한 mapping type이다.
(참고 : Oracle Data Type는 Oracle 10g에서 테이블 생성 시 설정할 Column Type 이다.)
| Mapping Type |
Java Type |
Standard SQL built-in type |
Oracle Column Type |
| date |
java.util.Date or java.sql.Date |
DATE |
DATE |
| time |
java.util.Date or java.sql.Time |
TIME |
DATE |
| timestamp |
java.util.Date or java.sql.TimeStamp |
TIMESTAMP |
TIMESTAMP |
| calendar |
java.util.Calendar |
TIMESTAMP |
TIMESTAMP |
| calendar_date |
java.util.Calendar |
TIMESTAMP |
DATE |
mapping flie작성 시 위 표를 참고 하여 자바 객체의 프로퍼티 타입에 맞게 mapping file을 작성하면 된다.
다음은 time, data type을 테스트 하기 위해 작성한
TimeDateType.java파일의 일부이다.
public class TimeDateType {
private java.sql.Date dateType;
private java.sql.Time timeType;
private java.sql.Timestamp timestampType;
private java.util.Calendar calendarType;
private java.util.Calendar calendarDateType;
<!-- 중략 -->
위의 프로퍼티 타입에 맞게 mapping file를 설정하면 된다. 다음은 TimeDataType.java파일과 DB 테이블의 mapping정보를 설정한
TimeDateType.hbm.xml파일의 일부이다.
<property name="dateType" column="DATE_TYPE" type="date"/>
<property name="timeType" column="TIME_TYPE" type="time"/>
<property name="timestampType" column="TIMESTAMP_TYPE" type="timestamp"/>
<property name="calendarType" column="CALENDAR_TYPE" type="calendar"/>
<property name="calendarDateType" column="CALENDAR_DATE_TYPE" type="calendar_date"/>
Java Date, Time type과 DB Column type에 대한
테스트 코드 보기Binary And Large Object Mapping Type
| Mapping Type |
Java Type |
Standard SQL built-in type |
Oracle Column Type |
| binary |
byte[] |
VARBINARY |
BLOB(자동 생성 시 RAW) |
| text |
java.lang.String |
CLOB |
CLOB |
| clob |
java.sql.Clob |
CLOB |
CLOB |
| blob |
java.sql.Blob |
BLOB |
BLOB |
| serializable |
java.io.Serializable |
VARBINARY |
- |
mapping flie작성 시 위 표를 참고 하여 자바 객체의 프로퍼티 타입에 맞게 mapping file을 작성하면 된다.
다음은 binary, large object type을 테스트 하기 위해 작성한
BlobDataType.java,
ClobDataType.java의 일부분이다.
public class BlobDataType {
private String fileName;
private java.math.BigDecimal fileSize;
private byte[] fileContentByte;
private Blob fileContentBlob;
public class ClobDataType {
private String title;
private String contentString;
private Clob contentClob;
위의 프로퍼티 타입에 맞게 mapping file를 설정하면 된다. 다음은 BlobData.java파일과 DB 테이블의 mapping정보를 설정한
BlobDataType.hbm.xml과
ClobDataType.hbm.xml파일의 일부이다.<property name="fileName" column="FILE_NAME" type="text"/>
<property name="fileSize" column="FILE_SIZE" type="big_decimal"/>
<property name="fileContentByte" column="FILE_CONTENT_BYTE" type="binary" />
<property name="fileContentBlob" column="FILE_CONTENT_BLOB" type="blob"/>
<property name="title" column="TITLE" type="text"/>
<property name="contentString" column="CONTENT_STRING" type="text"/>
<property name="contentClob" column="CONTENT_CLOB" type="clob"/>
Binary, BLOB Type과 DB Column type 매핑에 대한
테스트 코드 보기
CLOB Type과 DB Column type 매핑에 대한
테스트 코드 보기
Hibernate Generator
앞에서 설명한 실별자 필드 매핑에 이용되는 <id>태그안의 <geneator>태그는 객체 저장 시
식별자 값의 생성 방식을 지정한다. 그렇기 때문에 Mapping XML 파일 작성 시 신규 데이터를 추가하기 위해 해당 데이터의 유일한
Id를 할당받기 위한 방법을 선택해야 한다. 생성 방법에는 Hibernate에서 제공하는 기본 Id Generator 이용하는 방법과
직접 생성하는 방법이 있다.
Hibernate 기본 Id Generator
Hibernate에서 제공하는 기본 Id Generator
-
identity : DB2, MySQL, MS SQL Server, Sybase, HypersonicSQL에서 제공하는 identity column을 지원하고
return되는 identifier type는 int, long, short이다.
-
native : DB에 의존하여 Hibernate가 자동으로 신규 Id를
할당한다.
-
hilo : hi/lo 알고리즘을 적용된 특정 테이블의 칼럼값을 이용하여 Id를
생성한다. return되는 identifier type는 int, short, long이다.
-
increment : Hibernate가 값을 1씩 증가시켜 Id를 생성한다.
-
guid : MS SQL과 MySQL에서 생성한 GUID 문자열을 Id로
전달한다.
-
sequence : Oracle, DB2, PostgreSQL, SAP DB, Mckoi에서 사용하는
Sequence를 사용하여 Id를 생성한다. 리턴되는 identifier type는 int, short, long이다.
-
uuid : UUID 알고리즘을 이용하여 128 bit Id를 생성한다. 생성된
문자열은 32 글자의 16진법으로 인코딩되어 표시된다.
-
seqhilo : hilo와 동일하나 주어진 DB의 Sequence로부터 hi
값을 가져온다.
identity
identity는 MySQL, MS SQL Server와 같이 DBMS에서 제공하는 identifier를 제공한다.
identity generator를 이용 해 identifier를 생성하기 위한 설정을 보여주는
ConutryWithIdentity.hbm.xml의 일부분이다.
<class name="anyframe.sample.model.unidirection.generator.CountryWithIdentity"
table="COUNTRY_IDENTITY" lazy="true" schema="PUBLIC">
<id name="countryCode" column="COUNTRY_CODE" type="int">
<generator class="identity" />
</id>
<!-- 중략 -->
아래는 identity generator를 이용 해 COUNTRY 테이블의 primary key인 COUNTRY_CODE를 자동 생성하고 테스트 하는
HibernateIdGeneratorTest.java의 일부분이다.
public void testAddCountryWithIdentityGenerator() throws Exception {
CountryWithIdentity country1 = new CountryWithIdentity();
country1.setCountryId("KR");
country1.setCountryName("Korea");
Integer countryCode = (Integer) session.save(country1);
<!-- 중략 -->
}
위 테스트 케이스를 실행하보면 COUNTRY_CODE에 자동으로 identifier가 생성되 저장되는 것을 확인 할 수 있다.
sequence
Oracle과 같이 Sequence를 사용 할 수 있는 DBMS에서 Sequence를 사용하여 Id를 생성한다.
다음은 sequence generator를 이용 해 identifier를 생성하기 위한 설정파일
CountryWithSequence.hbm.xml의 일부분이다.
<class name="anyframe.sample.model.unidirection.generator.CountryWithSequence"
table="COUNTRY_SEQ" lazy="true" schema="PUBLIC">
<id name="countryCode" type="int">
<column name="COUNTRY_CODE" length="12" />
<generator class="sequence">
<param name="sequence">COUNTRY_ID_SEQ</param>
</generator>
</id>
<!-- 중략 -->
DBMS의 COUNTRY_ID_SEQ이름의 Sequence값으로 identifier를 생성한다.
아래는 sequence generator를 이용해 DBMS의 특정 Sequence으로 primaty key column에 데이터를 저장하고 테스트하는
HibernateIdGeneratorTest.java의 일부분이다.
public void testAddCountryWithSequenceGenerator() throws Exception {
CountryWithSequence country1 = new CountryWithSequence();
country1.setCountryId("KR");
country1.setCountryName("Korea");
Integer countryCode = (Integer) session.save(country1);
<!-- 중략 -->
}
위 테스트 케이스를 실행 해 보면 DBMS에서 COUNTRY_ID_SEQ의 Sequence값이 COUNTRY_CODE에 입력되는 것을 확인 할 수 있다.
hilo
hilo generator는 hi/lo알고리즘을 사용하여 identifier를 생성한다.
다음은 hilo를 이용해 identifier를 생성하도록 설정한
CountryWithHilo.hbm.xml의 일부분이다.
<class name="anyframe.sample.model.unidirection.generator.CountryWithHilo"
table="COUNTRY_HILO" lazy="true" schema="PUBLIC">
<id name="countryCode" column="COUNTRY_CODE" type="int">
<generator class="hilo">
<param name="table">ID_MANAGEMENT</param>
<param name="column">NEXT_VALUE</param>
<param name="max_lo">2</param>
</generator>
</id>
위 Mapping File는 ID_MANAGEMENT 테이블에서 NEXT_VALUE 컬럼에서 identifier를 얻고 다음에 유일한 아이디를 제공하기 위해
NEXT_VALUE컬럼의 값에 1을 더한 값을 업데이트 한다.
max_lo는 hilo generator 실행 시 생성 되는 신규 identifier의 개수이다. 다음은 위 Mapping File로 테스트케이스를 실행했을 때
identifier가 생성되는 query에 대한 로그이다.
select NEXT_VALUE from ID_MANAGEMENT
update ID_MANAGEMENT set NEXT_VALUE = 1 where NEXT_VALUE = 0
ID_MANAGEMENT 테이블에서 NEXT_VALUE을 얻어와서 identifier를 생성한 다음 update하는 query를 볼 수있다.
다음은 hilo generator를 테스트 하기 위한
HibernateIdGeneratorTest.java의 일부분이다.
public void testAddCountryWithHiloGenerator() throws Exception {
CountryWithSeqHilo country1 = new CountryWithSeqHilo();
country1.setCountryId("KR");
country1.setCountryName("Korea");
Integer countryCode1 = (Integer) session.save(country1);
<!-- 중략 -->
CountryWithSeqHilo country2 = new CountryWithSeqHilo();
country2.setCountryId("JP");
country2.setCountryName("Japan");
Integer countryCode2 = (Integer) session.save(country2);
<!--중략 -->
CountryWithSeqHilo country3 = new CountryWithSeqHilo();
country3.setCountryId("US");
country3.setCountryName("U.S.A");
Integer countryCode3 = (Integer) session.save(country3);
}
위 테스트 코드를 디버그 모드로 실행해 보면 country2를 저장 할 때까지는 ID_MANAGEMENT테이블에서 NEXT_VALUE컬럼의 값을
select하는 로그는 한 번만 남을 것이다. 그리고 country3를 저장 할 때 다시 한번 ID_MANAGEMENT테이블에서 NEXT_VALUE컬럼의 값을
select로그가 남는 것을 확인 할 수 있다. 이는 Mapping File에 max_lo값을 2로 세팅 했기 때문에 처음 identifier생성 시 2개를
생성하기 때문이다.
#참고 : table, column을 Mapping File에 세팅하지 않을 경우 기본 table, column은 hibernate_unique_key, next_hi이다.seqhilo
hilo와 동일 하지만 Database의 특정 테이블의 컬럼이 아닌 DBMS의 Sequence로 부터 hi값을 가져와 identifier를 생성한다.
아래는 seqhilo를 이용해 identifier를 생성하기 위한
CountryWithSeqHilo.hbm.xml의 일부분이다.
<class name="anyframe.sample.model.unidirection.generator.CountryWithSeqHilo"
table="COUNTRY_SEQHILO" lazy="true" schema="PUBLIC">
<id name="countryCode" column="COUNTRY_CODE" type="int">
<generator class="seqhilo">
<param name="sequence">COUNTRY_ID_SEQ</param>
<param name="max_lo">2</param>
</generator>
</id>
<!-- 중략 -->
위 Mapping File에서는 Primary Key인 COUNTRY_CODE의 identifier를 생성하기 위해서 DBMS의 COUNTRY_ID_SEQ란 이름의 sequence를
이용해 identifier를 생성한다. 아래는 seqhilo generator를 이용해 identifier를 생성하기 할 때 DBMS에서 값을 얻기 위해 실행되는 query 로그이다.
call next value for COUNTRY_ID_SEQ
다음은 seqhilo generator에 대한 테스트 코드
HibernateIdGeneratorTest.java의 일부분이다.
public void testAddCountryWithSeqHiloGenerator() throws Exception {
CountryWithSeqHilo country1 = new CountryWithSeqHilo();
country1.setCountryId("KR");
country1.setCountryName("Korea");
Integer countryCode1 = (Integer) session.save(country1);
<!-- 중략 -->
CountryWithSeqHilo country2 = new CountryWithSeqHilo();
country2.setCountryId("JP");
country2.setCountryName("Japan");
Integer countryCode2 = (Integer) session.save(country2);
<!-- 중략 -->
CountryWithSeqHilo country3 = new CountryWithSeqHilo();
country3.setCountryId("US");
country3.setCountryName("U.S.A");
Integer countryCode3 = (Integer) session.save(country3);
<!-- 중략 -->
}
위의 테스트 케이스도 hilo와 마찬가지로 max_lo를 2로 설정 했기 때문에 country2를 save할 때까지 DBMS의 sequence를 이용해 identifier를
생성하는 로그가 한번만 남는다. 그리고 country3를 save를 할 때 identifier를 생성하기 위해 DBMS에서 sequence를 얻어 오는 로그가 남는다.
increment
increment generator는 매핑되는 primary key값의 최고값을 얻어와서 Hibernate가 1을 증가시킨 다음에 identifier를 생성한다.
아래는 increment generator를 이용해 identifier를 생성하기 위해 설정한
CountryWithIncrement.hbm.xml의 일부분이다.
<class
name="anyframe.sample.model.unidirection.generator.CountryWithIncrement"
table="COUNTRY_INCREMENT" lazy="true" schema="PUBLIC">
<id name="countryCode" type="int">
<column name="COUNTRY_CODE" length="12" />
<generator class="increment" />
</id>
<!-- 중략 -->
increment generator가 키 생성이 필요할 때 실행되는 query는 아래와 같다.
select max(COUNTRY_CODE) from COUNTRY_INCREMENT
위의 query는 identifier가 필요할 때마다 생성 되는 것이 아니라 처음 실행된 이후 메모리에서 1씩 증가하는 것이기 때문에
분산환경에서 사용 할 경우 제대로 된 identifier를 생성하지 못 할 수도 있다.
다음은 increment generator를 이용해 identifier를 생성하는 테스트 코드
HibernateIdGeneratorTest.java의 일부분이다.
public void testAddCountryWithIncrementGenerator() throws Exception {
CountryWithIncrement country1 = new CountryWithIncrement();
country1.setCountryId("KR");
country1.setCountryName("Korea");
Integer countryCode1 = (Integer) session.save(country1);
<!-- 중략 -->
CountryWithIncrement country2 = new CountryWithIncrement();
country2.setCountryId("JP");
country2.setCountryName("Japan");
Integer countryCode2 = (Integer) session.save(country2);
<!-- 중략 -->
CountryWithIncrement country3 = new CountryWithIncrement();
country3.setCountryId("US");
country3.setCountryName("U.S.A");
Integer countryCode3 = (Integer) session.save(country3);
}
위의 테스트 코드를 실행해 보면 처음 DB에서 최대 키 값을 얻어 온 이후 다시 얻어오는 query는 실행 되지 않는다.
uuid
UUID알고리즘을 사용하여 16진법으로 32글자의 identifier를 생성한다.
아래는 UUID를 사용해 identifier를 생성하기 위해 설정한
CountryWithUUID.hbm.xml의 일부분이다.
<class name="anyframe.sample.model.unidirection.generator.CountryWithUUID"
table="COUNTRY_UUID" lazy="true" schema="PUBLIC">
<id name="countryCode" column="COUNTRY_CODE" type="string">
<generator class="uuid">
<param name="separator">#</param>
</generator>
</id>
<!-- 중략 -->
다음은 UUID generator에 대한 테스트 코드
HibernateIdGeneratorTest.java의 일부분이다.
public void testAddCountryWithUUIDGenerator() throws Exception {
CountryWithUUID country1 = new CountryWithUUID();
country1.setCountryId("KR");
country1.setCountryName("대한민국");
String countryCode = (String) session.save(country1);
}
위의 테스트 코드를 실행 시켰을 때 query 로그는 다음과 같다.
insert into PUBLIC.COUNTRY_UUID (COUNTRY_ID, COUNTRY_NAME, COUNTRY_CODE) values ('KR', '대한민국',
'c687b6dc#1c894fc4#011c#894fc5ef#0001')
Mapping File에 separator에 대한 값을 #으로 했기 때문에 생성되는 identifier값에 구분자로 '#'이 사용 된 것을 확인 할 수 있다.
직접생성
Hibernate에서 제공하는 기본 gernerator를 이용 할 수도 있겠지만 직접 키 값을 생성해서 저장하는 경우도 있다.
'product-00001', 'product-00002'과 같이 identifier를 저장하고 싶을 때는 위에서 언급한
하이버네이트 기본 generator를 사용 할 수 없다.
assigned
<generator>의 class 속성 값을 assigned로 정의한
경우 객체에 저장된 값을 그대로 이용하게 된다. 사용자가 정의한 별도 Id
Generator가 있는 경우 class 속성 값에 해당 클래스를 정의할 수
있다.
다음은 assigned하기 위해 Mappping File에 설정한 샘플 소스이다.
<id name="categoryNo" type="string">
<column name="CATEGORY_NO" length="16" />
<generator class="assigned" />
</id>
generator를 assigned로 설정 했다면 객체를 저장하기 전에 categoryNo에 값을 세팅해야 한다.
다음은 Anyframe의 기술공통 서비스인 IdGenerationService를 이용해 identifier을 얻어서 객체에 세팅하고
저장하는 샘플 소드이다.
category.setCategoryNo(idGenerationService.getNextStringId());
<!-- 중략 -->
session.save(category);
assigned generator는 일반적으로 가장 많이 이용하는 형태로 Anyframe의 IdGenerationService과 함께 사용 시 유용하다.