Hibernate API를 사용하여 OR 매핑을 처리하는 예제를 작성해봄으로써 일단 Hibernate에 발을 담군다.

요약

웹 어플리케이션 개발은 곧 데이터베이스에 대한 CRUD 코드의 개발이라고 해도 과언이 아닐 정도로 웹 어플리케이션이 제공하는 기능의 대다수가 데이터베이스와 관련되어 있다. OR 매핑 도구를 사용함으로써 CRUD 작업 처리를 위한 코드를 상당량 줄일 수 있으며, 도메인 영역을 개발하는데에 집중함으로써 웹 어플리케이션의 품질을 높일 수 있다는 장점이 있다. 본 글에서는 OR 매핑을 처리해주는 Hibernate를 어떻게 사용할 수 있는 예제를 통해서 간단하게 살펴보도록 하겠다.

>> 문서제작
최범균 - (madvirus골뱅이madvirus.net)
http://javacan.madvirus.net/

OR 매핑과 Hibernate API 사용 준비#

OR 매핑은 객체와 관계형 DB 테이블 사이의 매핑을 뜻하는 것으로서, 객체의 데이터를 DB 테이블의 레코드로 또는 DB 테이블의 레코드를 객체의 데이터로 이동시켜주는 기법을 의미한다. 웹 어플리케이션 개발시 작성하는 코드의 핵심 부분은 거의 95% 이상이 DB 테이블과의 CRUD 작업과 연관되어 있기 때문에, OR 매핑 도구를 알맞게 사용하면 개발 효율성을 높일 수 있게 된다.(예를 들어, select 쿼리를 작성하고 테스트 하는 소비되는 시간이 없어지고, 대신 도메인 로직을 처리하는 코드를 작성하는 데 더 많은 시간을 투자할 수 있다.)

현재 Sun의 JDO, 자카르타의 OJB, Hibernate 등 다양한 OR 매핑 API가 존재한다.이 중에서, Hibernate API는 기존에 작성한 도메인 영역의 엔티티 객체를 그대로 사용할 수 있기 때문에 기존 코드를 작성하기가 쉬우며, OR 매핑 정보를 설정 파일로 관리하기 때문에 OR 매핑 설정이 쉽다. 또한, SELECT 쿼리 수행시 다양한 검색 조건을 제공하며, DBMS에 특징적인 기능도 지원하고 있다. 또한, 현재 많은 개발자들이 애용하고 있는 API이기도 한다. 이런 이유로 필자는 Hibernate API의 사용하는 방법에 대해서 7~8회에 걸쳐서 살펴보도록 하겠다.

Hibernate API를 사용하기 위한 준비

본 글에서는 Hibernate API를 사용하는 웹 어플리케이션을 작성해볼 것이다. 톰캣 5.0.xx 버전과 JDK 1.4.2 버전을 사용해서 웹 어플리케이션을 실행하였다. 웹 어플리케이션을 위한 폴더를 아래와 같이 생성하였다. (WEB-INF 폴더에 들어갈 web.xml 파일은 알맞게 작성하기 바란다.) 하단에 있는 관련 자료에서 예제 코드를 직접 다운로드 받아서 사용할 수 있다.

  - C:\jakarta-tomcat-5.0.28
      - webapps
          - hibernate
              - WEB-INF
                  - lib
                  - classes

위와 같이 웹 어플리케이션 폴더를 작성했다면, 아래의 순서에 따라 Hibernate API를 사용하면 된다.

  1. Hibernate API를 다운로드 한다.
  2. 필요한 jar 파일을 복사한다.
  3. 매핑될 자바 클래스 파일을 작성한다.
  4. 객체의 데이터를 저장할 테이블을 생성한다.
  5. 자바 객체와 테이블 사이의 매핑 정보 파일을 작성한다.
  6. 자바 코드에서 Hibernate Session을 이용하여 OR 매핑을 처리한다.

먼저, Hibernate는 Hibernate 홈페이지에서 다운로드 받을 수 있다. 현재 production 버전은 2.1.7c이며, development 상태의 버전은 3.0beta1이다. 본 글에서는 2.1.7c 버전을 사용할 것이다.

다운로드 받은 hibernate-2.1.7c.zip 파일의 압축을 풀면 여러 가지 jar 파일이 존재하는데, 이들 jar 파일 중에서 다음의 파일들을 WEB-INF\lib 폴더에 복사한다.

  • hibernate2.jar
  • lib/cglib-full-2.0.2.jar
  • lib/commons-collections-2.1.1.jar
  • lib/commons-logging-1.0.4.jar
  • lib/dom4j-1.4.jar
  • lib/ehcache-0.9.jar
  • lib/jta.jar
  • lib/odmg-3.0.jar
  • JDBC 드라이버

JDBC 드라이버는 독자가 테스트를 위해 사용할 DBMS에 알맞은 걸 사용하면 된다. (본 글에서는 오라클9i에 해당하는 classes12.jar 파일을 사용해서 테스트할 것이다.)

필요한 jar 파일을 복사했다면 Hibernate API를 사용하기 위한 준비가 끝난 것이다.

매핑될 자바 클래스 작성과 테이블 생성#

OR 매핑의 핵심 개체는 메모리 상에 존재할 자바 객체와 물리적으로 존재할 DB 테이블이다. Hibernate에 발을 담구는 단계이므로 간단한 객체와 테이블을 사용해볼 것이다. (참고로 여기서 사용하는 자바 클래스와 테이블은 Hibernate 레퍼런스의 퀵스타트에서 사용한 것들이다.)

매핑될 자바 클래스

테이블과 매핑될 자바 클래스는 다음과 같다.

package javacan.hibernate.test;

/**
 * Cat 클래스
 
 @author 최범균
 */
public class Cat {
    private String id;
    private String name;
    private char sex;
    private float weight;
    
    public Cat() {
    }
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public char getSex() {
        return sex;
    }
    public void setSex(char sex) {
        this.sex = sex;
    }
    public float getWeight() {
        return weight;
    }
    public void setWeight(float weight) {
        this.weight = weight;
    }
}

위 코드를 보면 알겠지만 OR 매핑에서 테이블과 매핑될 객체를 위한 클래스는 Hibernate API에 대해서 전혀 연관되어 있지 않다.

테이블 생성

앞서 작성한 Cat 객체의 데이터를 저장할 테이블의 스키마는 아래와 같다.

<table border="1"> <tr><td>컬럼명</td><td>컬럼타입</td><td>NOT NULL</td><td>인덱스</td></tr> <tr><td>CAT_ID</td><td>CHAR(32)</td><td>NOT NULL</td><td>PK</td></tr> <tr><td>NAME</td><td>VARCHAR(16)</td><td>NOT NULL</td><td> </td></tr> <tr><td>SEX</td><td>CHAR(1)</td><td> </td><td> </td></tr> <tr><td>WEIGHT</td><td>REAL</td><td> </td><td> </td></tr> </table>

빠른 이해를 위해 Cat 클래스의 프로퍼티와 CAT 테이블의 필드가 1대 1로 매핑되도록 하였다.

매핑 파일 및 Hibernate 세션 설정 파일 작성#

매핑될 자바 객체와 DB 테이블이 준비되었다면, 그 다음으로 해야 할 작업은 매핑 설정 파일을 작성하는 것이다. 앞서 작성한 Cat 클래스와 CAT 테이블에 대한 매핑 정보를 담고 있는 매핑 설정 파일은 아래와 같다.

파일경로:/WEB-INF/classes/Cat.hbm.xml

<?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.Cat" table="CAT">
        <id name="id" type="string" unsaved-value="null">
            <column name="CAT_ID" sql-type="char(32)" not-null="true" />
            <generator class="uuid.hex" />
        </id>
        
        <property name="name">
            <column name="NAME" length="16" not-null="true" />
        </property>
        
        <property name="sex" />
        
        <property name="weight" />
    </class>
</hibernate-mapping>

OR 매핑 파일을 작성하는 방법에 대해서는 본 시리즈의 4회 글에서 살펴볼 것이며, 본 글에서는 위 설정 파일의 각 태그에 대해서 자세히 설명하지는 않겠다. 간단하게 각 태그 및 속성들은 간단히 설명하면 아래와 같다.

  1. <class> 태그 - OR 매핑에서 사용될 클래스와 테이블을 명시한다. name 속성에는 매핑될 클래스의 완전한 이름을, table 속성에는 매핑딜 DB 테이블의 이름을 명시한다.
  2. <id> 태그 - DB 테이블의 주요키와 매핑되는 객체의 프로퍼티를 설정한다. name 속성은 프로퍼티 이름을, type은 프로퍼티의 타입을 나타낸다.
  3. <property> 태그 - 객체에서 DB 테이블과 매핑될 프로퍼티의 이름을 명시한다.
  4. <column> 태그 - <id> 태그나 <property> 태그로 지정한 객체의 프로퍼티와 매핑되는 DB 테이블의 필드에 대한 정보를 지정한다. name 속성은 필드명을, sql-type은 SQL 타입을 not-null은 필드가 null 값을 허용하는지의 여부를 명시한다.
  5. <generator> 태그 - 키값 생성기를 나타낸다. 객체를 생성하면 <id> 태그에 해당하는 DB 테이블의 필드인 PK 필드에 키값을 삽입해야 하는데, 이때 <generator> 태그가 생성한 값을 키값으로 사용한다. 위 예제코드에서는 32자리의 HEX 코드를 만들어내는 생성기를 사용한다.

<property> 태그가 <column> 태그를 자식으로 갖지 않을 경우에는 프로퍼티 이름과 동일한 이름의 테이블 필드를 참조하게 된다. 예를 들어, weight 프로퍼티의 값을 저장하는 DB 테이블의 필드는 weight가 되는 것이다.

매핑 설정 파일을 작성했다면 그 다음으로 해야 할 일은 Hibernate의 세션 설정 파일을 작성하는 것이다. 세션 설정 파일에서는 다음과 같은 것들을 명시하게 된다.

  • DB 커넥션과 관련된 프로퍼티 지정
  • 세션이 사용할 OR 매핑 설정 파일 정보

설정 파일의 형태는 간단한데, 본 글에서 예젤 사용한 세션 설정 파일은 다음과 같다.

파일경로: /WEB-INF/classes/hibernate.cfg.xml

<?xml version="1.0" encoding="euc-kr" ?>

<!DOCTYPE hibernate-configuration
    PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
           "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
        <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:ORA</property>
        <property name="hibernate.connection.username">scott</property>
        <property name="hibernate.connection.password">tiger</property>
        
        <property name="dialect">net.sf.hibernate.dialect.Oracle9Dialect</property>
        
        <mapping resource="Cat.hbm.xml" />
    </session-factory>
</hibernate-configuration>

위 파일에서 <property> 태그는 DB 세션에 대한 설정을, <mapping> 태그는 이 세션과 관련된 매핑 설정 파일을 명시한다.

위 예제 파일은 Hibernate가 제공하는 커넥션 풀을 사용하도록 설정하고 있는데, 이 외에도 CP30이나 DBCP와 같은 다른 커넥션풀을 사용할 수 있으며, 또한 어플리케이션 서버(WAS)가 제공하는 JNDI로부터 커넥션을 구할 수도 있다. 심지어 프로그램에서 직접 세션에 커넥션을 전달할 수도 있는데 이에 대해서는 다음 글에서 살펴보도록 하겠다.

Hibernate Session을 사용한 OR 매핑 처리 코드 작성#

Hibernate 세션 설정 파일 및 OR 매핑 설정 파일을 작성했다면 이제 남은 일은 Hibernate가 제공하는 OR 매핑 기능을 사용해서 어플리케이션을 개발하는 것이다.

세션을 구하기 위한 유틸리티 클래스 작성

Hibernate가 제공하는 세션을 구하는 코드는 아래와 같은 형태를 지닌다.

SessionFactory sessionFactory = null;
sessionFactory = new Configuration().configure().buildSessionFactory();

Session s = sessionFactory.openSession()// Session을 통해서 CRUD 작업을 수행하게 된다.

...

s.close();

위 코드에서 Configuration.configure() 메소드를 호출하는 부분이 있는데, 이 코드가 실행되면서 설정 파일로부터 세션 정보를 읽어오게 된다.

SessionFactory는 초기화를 위해서 한번만 생성하면 되므로 보통은 아래와 같은 형태의 유틸리티 클래스를 작성해서 Hibernate 세션을 사용한다.

package javacan.hibernate.test;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Hibernate의 Session을 open/close 해주는 유틸리티 클래스.
 
 @author 최범균
 */
public class HibernateUtil {
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    
    private static final SessionFactory sessionFactory;
    
    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        catch(Throwable ex) {
            log.error("Initial SessionFactory creation failed.", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    
    public static final ThreadLocal local = new ThreadLocal();
    
    public static Session currentSession() throws HibernateException {
        Session s = (Sessionlocal.get();
        if (s == null) {
            s = sessionFactory.openSession();
            local.set(s);
        }
        return s;
    }
    public static void closeSession() throws HibernateException {
        Session s = (Sessionlocal.get();
        local.set(null);
        if (s != null) {
            s.close();
        }
    }
}

위와 같이 유틸리티 클래스를 만들면 Hibernate 세션을 구하는 코드는 아래와 같이 간단해진다.

Session s = HibernateUtil.currentSession();

...

HibernateUtil.closeSession();

Hibernate를 이용한 CRUD 처리

Hibernate 세션을 구해주는 유틸리티 클래스도 작성했으므로, Hibernate를 이용해서 CRUD 처리를 어떻게 처리하는 지 대략적으로 살펴보도록 하자. 먼저 Hibernate를 사용하는 전형적인 코드는 아래와 같다.

    Session hbSession = null;
    Transaction tx = null;
    
    try {
        hbSession = HibernateUtil.currentSession();
        tx = hbSession.beginTransaction();
        
        // Hibernate 세션을 이용한 CRUD 처리 코드
        
        tx.commit();
    catch(Exception ex) {
        if (tx != nulltx.rollback();
    finally {
        HibernateUtil.closeSession();
    }

Transaction은 트랜잭션을 처리해주며, tx.commit()을 호출하면 트랜잭션이 완료되고, tx.rollback()을 호출하면 트랜잭션이 롤백된다. 트랜잭션은 Session.beginTransaction() 메소드를 호출하면 시작된다.

hbSession은 트랜잭션 처리를 위한 save(), update(), delete() 등의 메소드를 제공한다. 예를 들어, 앞서 작성했던 Cat 클래스의 객체를 생성한 뒤 프로퍼티 값을 알맞게 지정하고 DB 테이블에 저장하는 코드는 아래와 같이 save() 메소드를 사용해서 작성할 수 있다.

관련코드: /makeCat.jsp

Session hbSession = null;
Transaction tx = null;

try {
    hbSession = HibernateUtil.currentSession();
    tx = hbSession.beginTransaction();
    
    Cat cat = new Cat();
    cat.setName("나비");
    cat.setSex('F');
    cat.setWeight(2.0f);
    
    hbSession.save(cat);
    
    tx.commit();
catch(Exception ex) {
    if (tx != nulltx.rollback();
finally {
    HibernateUtil.closeSession();
}

만약 위의 코드를 JDBC API 만으로 작성한다면 아래와 같은 형태를 취할 것이다.

    Cat cat = new Cat();
    cat.setId(생성한ID값);
    ...

    Connection conn = null;
    PreparedStatement pstmt = null;
    
    try {
        conn = DButil.getConnection();
        pstmt = conn.prepareStatement("insert into cat values (?, ?, ?, ?)");
        pstmt.setString(1, cat.setId());
        ...
        pstmt.setFloat(4, cat.getWeight());
        pstmt.executeUpdate();
        
    finally {
        if (pstmt != nulltry pstmt.close()catch(SQLException ex) {}
        if (conn != nulltry conn.close()catch(SQLException ex) {}
    }

앞서 Hibernate를 사용한 코드와 위의 JDBC를 사용한 코드를 비교해보면 Hibernate를 사용한 코드가 훨씬 이해가 빠르며, 코드도 짧은 것을 알수가 있다. 처리해야 할 필드의 개수가 많으면 많아질 수록 코딩량의 차이는 더욱 늘어날 것이다.

테이블에서 데이터를 읽어오는 부분은 가장 기본적인 형태는 아래와 같다.

관련코드: /listCat.jsp

    Session hbSession = null;
    Transaction tx = null;
    
    try {
        hbSession = HibernateUtil.currentSession();
        tx = hbSession.beginTransaction();
        
        Query query = hbSession.createQuery("select c from Cat as c where c.sex = :sex");
        query.setCharacter("sex", request.getParameter("sex").charAt(0));
        for (Iterator iter = query.iterate() ; iter.hasNext() ) {
            Cat c = (Catiter.next();
        }
        tx.commit();
    catch(Exception ex) {
        if (tx != nulltx.rollback();
    finally {
        HibernateUtil.closeSession();
    }

Query 객체는 SQL 쿼리와 비슷한 HQL 정보를 담고 있는 객체로서, 위 코드에서는 select 쿼리를 위한 정보를 담고 있다. 위 코드에서 사용된 select 쿼리는 일반적인 SQL 쿼리와 비슷하지만 다른 형태를 취하고 있는 것을 알 수 있는데, 이 쿼리는 Hibernate가 자체적으로 제공하는 HQL이다. HQL은 SQL과 비슷하기 때문에 쉽게 배울 수 있는데, 이에 대해서는 본 시리즈의 6번째 글에서 살펴볼 것이다.

예제 실행 방법#

글 하단에 위치한 관련 링크에서 본 글에서 사용한 예제 코드를 다운로드 받을 수 있다. 예제 파일은 hibernate.war인데, 이 파일을 톰캣이나 레신과 같은 웹 콘테이너에 알맞게 복사한다. 톰캣의 경우 톰캣/webapps 폴더에 복사한다. 알맞게 복사한 뒤 실행을 하면 hibernate.war 파일이 자동으로 배치될 것이다.

압축이 풀리면 /WEB-INF/classes/hibernate.cfg.xml 파일을 열어서 아래의 부분을 테스트하려는 DBMS에 알맞게 수정한다.

    <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
    <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:ORA</property>
    <property name="hibernate.connection.username">scott</property>
    <property name="hibernate.connection.password">tiger</property>

윗 부분을 알맞게 수정하고, DBMS에 앞서 보여줬던 CAT 테이블을 생성하고, DBMS에 알맞은 JDBC 드라이버를 /WEB-INF/lib 폴더에 복사한 뒤, WAS를 다시 시작하면 예제를 실행할 수 있다.

예제는 다음과 같은 3개의 JSP 파일이 있다.

  • makeForm.jsp - 고양이 정보를 입력하는 폼.
  • makeCat.jsp - makeForm.jsp로부터 전달받은 고양이 정보를 Cat 객체에 저장한 뒤, Hibernate를 사용해서 CAT 테이블에 저장
  • listCat.jsp - Hibernate를 사용해서 CAT 테이블에 저장된 정보를 읽어와 Cat 객체에 저장한 뒤 출력

makeForm.jsp를 실행해서 고양이 정보를 입력한 뒤, listCat.jsp를 통해서 DB 테이블에 저장되는 지의 여부를 확인할 수 있을 것이다.


예제소스

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-1) was last changed on 06-Apr-2006 09:45 by UnknownAuthor