본문 바로가기
연재/NHibernate

[챕터4] XML 매핑 클래스에 대한 테이블 스키마를 실제 DB에 생성해보자.

by 그저그런보통사람 2011. 8. 7.



이번 쳅터에서는 실제로 xml 메타데이터로 설정된 도메인 모델을 DB 테이블로 생성해보겠습니다.
실제로는 모든 도메인 모델이 구성되면 한 번에 처리하고자 했으나, 하이버네이트가 제공하는 도구의 편의성도 살펴볼 겸 간단하게 진행하겠습니다.

우선 솔루션에 새 프로젝트를 추가합니다. 이 프로젝트는 실제 DB에 스키마를 주입/생성하는 간단한 역할을 하는 프로젝트임으로 콘솔 프로젝트로 생성합니다.


프로젝트명은 DbSchemaGenerator 로 하겠습니다.


프로젝트를 생성 후 아래와 같이 두 프로젝트 참조를 추가합니다.



그리고 NHibernate 라이브러리 참조를 추가하고, hibernate.cfg.xml 파일과 NHibernateUtil.cs 파일을 추가합니다.



hibernate.cfg.xml 는 하이버네이트 설정 파일입니다. 이 파일은 어떠한 DB를 사용할 것이며, 연결 문자열은 무엇이고, 배치 사이즈 및 캐시 등등 전반적인 구성 요소를 정의하는 파일입니다.
하이버네이트가 구동되면 가장 먼저 하는 작업이 이 설정 파일을 읽고 정의된 내용에 기반한 구성 객체를 생성하는 일입니다.

hibernate.cfg.xml 파일명은 구성 객체를 생성할 때 별도의 위치 지정이 없으면 하이버네이트가 자동으로 찾는 파일명입니다.

이제 hibernate.cfg.xml 에 구성요소를 등록해봅시다.

=> 그전에 더보기 한번~

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
  <session-factory name="NHibernate.Test">
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">
      Server=(local)\sqlexpress;initial catalog=nhibernate;Integrated Security=SSPI
    </property>
    <property name="adonet.batch_size">10</property>
    
    <!-- 이 항목은 개발에서만 사용하고 운영시에는 반드시 제거합니다. 로그 및 디버깅용. -->
    <property name="show_sql">true</property>
    <!-- 이 항목도 마찬가지. -->
    <property name="format_sql">true</property>
    
    <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
    <property name="command_timeout">60</property>
    <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
 
    <property name="generate_statistics">true</property>
    
  </session-factory>
</hibernate-configuration>

약간은 복잡해 보이는 듯하니만 실제론 매우 간단합니다.

<
hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >

hibernate-configuration 엘리먼트로 시작하며, 두 번째 session-factory 엘리먼트가 핵심적인 요소입니다.
<session-factory name="NHibernate.Test">

이 엘리먼트의 자식 엘리먼트로 필요한 구성 요소를 추가하면 됩니다.

property 엘리먼트로 각각의 구성 요소를 추가해 줍니다.
첫번째 요소인 connection.driver_class 는 데이터베이스 연결 공급자 드라이버를 지정하는 요소입니다.
NHibernate.Driver.SqlClientDriver 라고 지정되어 있습니다. 이것은 MS-SQL 데이터베이스에 연결하기 위해 제공되는 드라이버입니다. 만약 오라클용으로 사용하고 싶다면 NHibernate.Driver.OracleClientDriver 로 입력하면 됩니다.

* 현재 하이버네이트가 지원하는 플랫폼은 MS-SQL, Oracle, MySql, DB2, FireBird, PostgreSQL, Sybase, SQLite 등등 거의 대부분의 관계형 데이터베이스를 지원하고 있으며, 각 플랫폼이 업그레이드 (ex: oralce 9i, 10g, 11g 나 SQL Server 2005, 2008 등) 할 때마다 발빠르게  대체하고 있습니다.

connection.connection_string 은 연결문자열입니다. 위에서는 로컬 sql express 를 사용할 것이기 때문에 별도의 아이디와 암호가 설정되어 있지 않지만, 실무에서 사용해 왔던 것처럼 입력하면 됩니다.

adonet.batch_size 는 일괄 처리 작업 사이즈를 의미합니다. 대용량 SQL 작업시 유용합니다. 
좀 더 쉽게 이해를 돕고자 부연 예를 들자면 은행에 100만원을 입금하려고 하는데 은행에서 입금하는 기준 금액이 1만원이라고 가정해 봅시다.
batch를 사용하지 않으면 1만원씩 입금해서 통장에 한번씩 찍어냅니다. 이 작업을 100번 해야하니 통장에는 100개의 입금 내역이 찍히겠죠? batch size 를 50으로 정하면 50만원씩 두 번만 작업합니다. 50번(50만원, 입금 기준은 1만원)의 입금을 준비하고 50만원이 만족하면 은행에 입금 처리하는 식입니다. 통장에는 두 번만 찍히겠죠.
이는 성능에 상당한 영향을 미칩니다. 너무 작아도 문제지만 너무 크게 잡아도 문제입니다. 이는 업무에 따라 적절하게 테스트를 하면서 정하는게 좋습니다.

show_sql 은 하이버네이트가 각각의 플랫폼에 따른 SQL문을 생성할 시 내부 콘솔에 출력할지를 결정하는 속성입니다.
DDL과 DML을 콘솔에 출력하기 때문에 단위 테스트나 디버깅시에 유용하게 사용될 수 있습니다.

format_sql 은 show_sql 설정으로 활성화된 SQL문이 좀 더 시각적으로 보기 좋게 줄바꿈 등을 해주는 옵션입니다.

dialect (방언) 는 가장 중요한 옵션 중 하나로 대상 데이터베이스 플랫폼에 맞는 최적화된 쿼리 생성을 위해 반드시 올바르게 정의해야 합니다. 플랫폼이 가진 특성을 최대한 살리기 위해 MS-SQL 이라도 MsSql2005, MsSql2008 등등 따로 제공됩니다.

command_timeout 은 하이버네이트에 의해 생성된 명령 수행 객체 (IDbCommands)의 시간(초) 제한을 지정하는 속성입니다.

마지막으로 query.substitutions 은 하이버네이트 쿼리로 부터 SQL 토큰으로 매핑하는 토큰을 의미합니다.
이 옵션은 비즈니스 조건식이 참/거짓으로 구분 된다면 코드에서는 조건 타입 = (true|false) 로 식을 정의하지만 이를 SQL 쿼리로 변환하면 1 과 0 과 같은 비트 혹은 정수 타입으로 변환되어 수행되어집니다.
sql query ex: select .. from ... where 조건컬럼 = (1 or 0)


다음은 NHibernateUtil.cs 파일을 작성해봅시다.
아래와 같이 작성합니다.



이제 실제로 스키마를 주입하여 생성하기 위해 Program.cs에 호출 코드를 작성해 봅시다.
namespace DbSchemaGenerator
{
    class Program
    {
        static void Main(string[] args)
        {
            NHibernateUtil.CreateSchema();
        }
    }
}
이제 실제로 생성되는지 실행을 해보죠.



실행이 정상적으로 되었나요?!
전 안타깝게도 예외가 발생했습니다. 저와 같이 챕터를 따라 왔다면 반드시! 예외가 발생했어야 합니다.
잘 수행되신 분이라면 이미 이유를 알고 수정을 한겁니다. ㅎㅎ;

아래와 같은 예외가 발생했습니다. 


예외 설명을 보니 "다음(우리가 정의한 도메인 클래스의 속성)의 타입들이 프록시(proxies)로 사용할 수 없는거 같다." 라고 하네요.

프록시?! 

혹시 제가 처음에 썼던 글 중에 Lazy Loading(지연 로딩)을 언급한 내용을 기억하시나요?!

"호출 코드에서 즉시(eager) 가져오는 방식이 아닌 실제 데이터가 사용되는 시점에 호출하는 방식!!!"
성능 영향을 가장 미치는 DB I/O를 최대한 지연 수행하여 전체 시스템의 성능을 향상 시키는 기술입니다.
Lazy Loading의 장점이 꼭 DB에 국한되는 것은 아니지만, 대부분 가장 성능에 영향을 미치는 구간이 DB다보니... ㅎㅎㅎ;

하이버네이트는 별도의 설정이 없으면 기본적으로 Lazy Loading을 사용합니다
그리고 Lazy Loading을 위해서 프록시를 생성해서 Lazy Loading을 실현합니다. 프록시를 생성하려면 메소드나 속성이 가상(virtual) 이어야 합니다. 
위 예외의 부가 설명에서도 잘 나와 있네요. 'public/protected virtual' 이나 protected ineternal virtual'로 정의 되어야 한다는 군요... 그런데 속성(Property)이라고 안하고 method set_Id, set_UserId ... 로 나왔냐하면... 우리가 작성한 코드가 컴파일러에 의해 IL 코드로 변환되면 속성으로 정의된 코드는 모두 getter, setter 의 메소드로 치환되기 때문입니다.


자, 예외에서 설명해준데로 도메인 클래스에 정의한 속성을 모두 가상(Virtual)로 변경하러 가지요~



다시 실행해 봅시다.



위와 같이 나오면 정상적으로 수행된 것입니다. DDL 쿼리구문이 보이네요. ^^
xml 메타데이터로 정의했던 column 속성대로 정확하게 컬럼이 생성되고, length 길이대로 사이즈가 생성되었습니다.
그리고 그전에 친절하게도 동일한 기존 테이블이 있는지 검사해서 깔끔하게 제거도 해줍니다.


실제로 생성되었는지 SQL Management Studio 를 실행시켜 확인해봅시다.


자~~알 생성 되었습니다. 나이스~~ ㅎㅎ;


만약 Lazy 기법을 사용하지 않고 즉시(Eager) 호출하는 방법을 사용하고자 한다면 xml 메타데이터 의 class 엘리먼트 속성으로Lazy="false" 를 추가해 주면됩니다.
<class name="Member" table="Members" lazy="false">

이렇게 설정하면 클래스 속성을 Virtual로 정의할 필요가 없어집니다. 다만, 대부분 (특히나 엔터프라이즈 시스템에서, 저는 경험이 없답니다 ^^;; 그렇다고 하더라구요...)의 경우 Lazy Loading 사용을 권장합니다. 




다음 챕터에서는 도메인 간 연관관계를 구성해보도록 하겠습니다
Coming Sooooooooooooooon~~~~!!!