site_link1 http://wiki.javajigi.net/pages/viewpage.action?pageId=297 
site_link2  
site_link3  

 

Hibernate 프레임워크를 이용한 효율적인 개발 전략

Table of Contents

Introduction

  • 이 문서는 Christian Bauer/Gavin King 이 쓴 Hibernate in Action의 원문을 참조하여 작성 되었습니다.

Working with object-oriented software and a relational database can be cumbersome and time consuming in today's enterprise environments.
Hibernate is an object/relational mapping tool for Java environments.
The term ''object/relational mapping (ORM)'' refers to the technique of mapping a data representation from an object model to a relational data model with a SQL-based schema. 원문

Hibernate의 정의를 원문에서 살펴보면 하이버 네이트는 자바환경 하에서 ORM을 하는 도구로 정의된다.
여기서 ORM(Object/Relation Mapping)이라는 말이 나오는데 하이버 네이트를 이해하기 위해서는 먼저 이 ORM이란 용어에 대하여 알아야 하며 ORM의 확실한 이해가 있어야 Hibernate를 사용하는 이유와 필요성을 이해 할 수 있으리라 본다. ORM의 설명은 아래의 ORM - Obejct Relation Mappging에서 다루도록 하겠다.

Hibernate Framework를 이해하고 사용하기 위해서는 먼저 Hibernate reference Documentation을 참조 하기 바란다. 그리고 친절하게도 openframework에서 한글로 번역된 reference도 있으니 굳이 영어가 싫으신분은 Hibernate reference Documentation을 참조 하면 Hibernate의 기본에 대하여 이해 할수 있을 것이다.
먼저 기본적으로 Hibernate의 Architecture에 대한 문서를 참조하여 Hibernate가 어떻게 구성되어 있는지를 이해하고 각각의 구성요소들의 역활도 한번 살펴 보기바란다.

Hibernate를 사용하는 목적

항상 그렇지만 거의 모든 개발자들이 새로운 프레임워크를 접할때 마다 생각 하는것이 과연 이것을 사용함으로써 얼마나 많은 장점을 내가 사용 할 수 있는 냐? 하는 의문이다. 자바라는 언어의 특성상 오픈소스로 제공되는 그 수많은 프레임워크를 일일이 다 파악하고 적절히 사용하는 사람이란 흔치 않을것이다. 그래서 대부분의 개발자들은 보다 많은 사람들이 사용하고 이슈화되고있는 프래임 워크를 선택 하게 되는데 그럴때 마다 모두 한번쯤은 생각 해봤을 것이다. 과연 이 프레임워크가 확실히 도움이 될 것인가? 하는 것을 말이다.
자 그럼 여기서 Hibernate를 사용하고 또 사용해야될 이유와 Hibernate를 사용함으로써 얻어지는 장점은 무엇일까?.
이건 스터디 때 한번 논의 해 보았으면 한다. 사람들 각자마다의 생각이 다 다를 것이고 필자가 결론을 지을 수 있는 것도 그럴 능력도 없을 것이라 생각 되기 때문이다. 스터디 멤버들은 회의 참석전 이 의문에 대한 생각을 한번씩 하고 이야기 해보았으면 한다.

스터디에서 논의된 내용을 추후 정리하여 문서화로 작성 할 것입니다.

ORM - Obejct Relation Mappging

Hibernate에서 ORM 이란 자바 어플리케이션 내의 객체들을 RDB에 있는 테이블로의 자동화된 영속화이다. 라고 정의를 내리고 있는데.
쉽게 설명 하자면 우리가 예전 부터 사용해오던 ResultSet 객체를 그 사용 목적에 맞는 형태의 객체로 변환 하는 작업이라고 생각 하면되겠다. 이러한 작업들은 아마도 필자가 생각 하기로는 기존에 어느정도 OOP 프로잭트를 진행하였던 개발자들이라면 각자 자기만의 Bean을 생성 하여 작업을 하였으리라 생각이된다. 결국 ORM이란 이런 기존의 우리가 행하였던 작업들에 대하여 소프트웨어 공학적으로 정의 해 놓은 것이라고 생각 하면 될듯 한다.
즉, ORM 이란 가상의 Object DB를 효과적으로 만들어 RDB를 OOP 언어의 개념으로 연계하는 프로그램 기술이다. Hibernate는 이런 ORM을 해주는 톨의 하나라고 할수 있다. 이런 ORM을 사용하는 가장 좋은 방법은 당연히 OODBMS를 사용하는 것이며 아직까지 완변하게 구현된 상용 DBMS는 나와있지는 않다.

Mapping Concept (Persistent class와 table사이의 mapping방법)

  • one-to-one : 테이블의 모든 컬럼에 대하여 persistent class의 모든 변수가 표현되며 하나의 비지니스 클래스는 테이블의 할 row에 해당 한다.
  • SUBSET Mapping : 하나의 테이블을 하나이상의 class로 mapping 하며 그 세부적으로 vertical mapping, horizontal mapping, filtered mapping이 있을 수 있다.
    • 간단 한 예로 각각의 설명을 해보겠다. 어떤 매장의 고객과 종업원을 관리하는 업무라고 할때 고객과 종업원은 아래와 Class Diagram으로 객체가 구성되었다면.
    • Vertical mapping: 하나의 테이블당 하나의 객체로 구현 되는 방식으로 아래와 같은 DB구조를 가지게 된다.
      Person Table Employee Table Customer Table
      PersonID ID ID
      LastName PersonID PersonID
      FirstName Since Terms
      Title PayRate Owes
    • Horizontal mapping : 하나의 테이블을 하나이상의 객체로 표현 하는 방식으로 위의 아래와 같은 DB구조를 위의 그림과 같은 객체로 mapping하는 방식이다.
      Employee Table Customer Table
      ID ID
      LastName LastName
      FirstName FirstName
      Title Title
      Terms Since
      Owes PayRate
    • Filter mapping : Horizontal 방식과 동일한 계념이나 어떤 data의 값을 가지고 filtering 하여 객체를 mapping하는 방식으로 아래와 같이 Type 컬럼의 값을 가지고서 고객과 종업원을 구분 하는 형식이 될 수 있다.
      Person Table
      ID
      Type
      LastName
      FirstName
      Title
      Since
      PayRate_Owes
      Terms
  • SUPERSET Mapping : 여러 테이블을 묶어서 하나의 class로 mapping 하는 방식으로 view의 편의성을 위하여 많이 구현 된다.
    • 간략하게 덧붙이자면 위의 경우에서 고객의 주소, 전화번호 같은 정보를 담고 있는 테이블 PersonInfo table이 존재할 경우 Person Table과 PersonInfo Table의 colum값을 모두 표현한는 객체로 mapping하는 방식이라 할 수 있는데 DB구조와 Class는 아래와 같은 구조로 보일 것이다.
    • 객체 구조
      Person
      LastName,FirstName,Address,Phone,Age
    • DB구조
      Person Table PersonInfo Table
      ID ID
      LastName Address
      FirstName Phone
      Title Age
      Since LastVisit
      PayRate_Owes  
      Terms  

       

      RelationShips 모델링

  • mapping object의 relation에 따른 분류로 1:1, 1:N, N:N, ownerShip mapping 방식등이 있다.

    Join 테이블에 대한 모델링

  • Transactional Joins, Transparent Joins, Self-Join Tables 방식등이 있다.
    상세한 이야기를 한다면 그 분량이 너무 방대해지기 때문에 ORM에 관한 이야기들은 인터넷에 많은 자료들을 참조 하기 바란다.

Hibernate이의외 ORM 툴로 널리 사용되고 있는것은 Ibatis, Oracle 의 Toplink, OJB가 널리 사용되어지고 있다

Hibernate

  • 자 이제 드디어 긴 사설을 끝내고 본격적으로 Hibernate란 놈에 대하여 연구를 하여 보자.
  • 일단 Hibernate의 기본 개념과 내용들은 앞부분의 내용과 관련 link를 참조 하도록 하자 (솔직히 필자가 정리 하여 문서화 하려 하였지만 이미 여러 싸이트와 단체에서 작성해 놓은 문서들이 충분히 훌륭하다고 생각이 된다 또한 가장 바람직 한것은 Hibernate 페이지에 있는 문서가 가장 기본적이고 바람직 할 것이라 생각 한다)
  • 이 문서의 제목과 같이 프로젝트에서 어떻게 Hibernate를 효율적으로 사용 할 수 있는지에 목적을 두도록하자.

Hibernate 개요

Hiberante의 기본적인 설치와 설정은 HibernateQuickstart를 참조 하도록하자. 필자가 사용한 환경은 tomcat 5.0.xx, JDK 1.4.3, Hibernate3 버젼이다. 현제 Hibernate3으로 release되었으며 많은 부분 괄목할만한 update가 있어 pakage명부터 시작하여 2.x버젼과의 많이 다르다. 그렇기 때문에 지금 Hibernate를 시작하는 사람들은 Hibernate3을 사용하기를 바란다. 필자는 비록 hibernate2를 사용하여 개발한 경험이 있으나 이번 내용에서 Hibernate3을 사용하여 예제를 만들어 보도록 하겠다. 필자 또한 Hibernate3의 모든 변화된 기능을 잘 모르니 설명의 오류나 새로운 기술의 도입에서 미진한 부분이 있을 수 있다는것을 양지 바란다.

Dictionary
  • SessionFactory (net.sf.hibernate.SessionFactory)
    컴파일된 매핑들의 캐쉬로서 상수처럼 변하지 않으므로 쓰레드에 대해 안전한다. Session의 팩토리이며, ConnectionProvider의 클라이언트.
    트랜잭션사이에서 재사용할 수 있는 데이타들을 캐슁할 수도 있다.
  • _Session (net.sf.hibernate.Session)_
    어플리케이션과 영구저장 사이를 맺어주는 단일쓰레드, 짧은 생명주기의객체이다. JDBC Connection의 Wrapper이며, Transaction에 대한 팩토리이다.
    Persistent 객체들을 캐슁한다.
  • _Persistent Objects and Collections_
    Persistant 상태와 Business 로직을 담는 단일쓰레드, 짧은생명주기의 객체들. 일반적인 JavaBean으로서 정확하게 하나의 Session과 연관된다.
  • _Transient Objects and Collections_
    현재 어떤 Session과도 연관되지 않은 Persistent 클래스의 인스턴스들. 어플리케이션에 의해 생성된 후 아직 저장되지 않았거나, 이미 단힌 Session에 의해 생성된 객체들.
  • _Transaction (net.sf.hibernate.Transaction)_
    작업의 원자성을 명시하기 위해 어플리케이션이 선택적으로 사용하는 단일쓰레드, 짧은생명주기의 객체이다. 어플리케이션을 JDBC, JTA 또는 CORBA 트랜잭션에서 추상화한다. 하나의 Session이 여러개의 Transaction을 가질 수 있다.
  • _ConnectionProvider (net.sf.hibernate.connection.ConnectionProvider)_
    JDBC Connection(또는 Pool)애 대한 선택적인 팩토리로서 어플리케이션에 대해 Datasource 또는 DriverManager를 추상화한다.
  • _TransactionFactory (net.sf.hibernate.TransactionFactory)_
    Transaction객채들에 대한 선택적인 팩토리.

Architecture

application 서비스를 제공하기 위한 persistance 객체를 지원하는 데이터 베이스와 configration을 사용하는 구조도

Runtime Architecture 1 (Lite Architecture)

Runtime Architecture 2 (Full Cream Architecture)

Object Life Cycle

  • 이 단원에서는 Hibernate에서 DB로 부터 가져온 객체의 상태를 이야기 해본다. Hibernate에서는 DB와 객체사이를 Session을 통하여 연결 한다. 그러므로 Session이 open, close때 마다 그 가져온 객체나 생성된 객체는 상태가 Transient, Persistent, Detached의 세가지로 변하게 되며 각각의 상태는 아래와 같다.
    • Transient : 객체의 인스턴스가 임의의 HibernateContext와 연관되지 않은상태이며 인스턴스의 프라이머 키 값을 가지지 않으며 데이터 베이스의 행과는 관계가 없다.
    • Persistent : 인스턴스가 Context와 연관되어 있는 상태이며 인스턴스의 프라이머 키 값을 가지며 영속 Context의 프라미어 키 값과의 관계를 Hibernate가 보증 한며 데이터 베이스 내에 대응하는 행을 가지고 있다.
    • detached : 인스턴스가 Context와 연관이 되었었지만 지금 현제 Context가 닫혔거나, 다른 프로세스로 직렬화 된 상태이며 인스턴스의 프라이머 키 값이 영속 Context의 프라이머 키 값과의 관계를 보증 하지 않으며 데잍너 베이스 내에 대응하는 행을 가지고 있으나 그 데이터의 관계는 다를 수 있다.

Hibernate Framework를 이용하여 프로그래밍 하기위해서는 이런 객체의 상태변화를 확실히 이해 하여야 각 상태에 따른 활용도를 높일 수 있으며 전체적인 application구조의 설계에도 반영을 할 수 있다. 각 상태에 대한 내용은 아래의 링크를 참조하여 이해하도록 하자. 참조

  • Hibernate객체의 상태변화를 이해하였다면 이제 우리의 application에서의 객체의 Life cycle을 어떻게 잡아야 할지를 살펴보자. 먼저 Web application에서 Hibernate의 session으로 DB의 데이터를 처리할 수 있는 방법은 직접사용시 마다, 어떤 써비스별, 사용자 Request에 따라 session을 가지고 처리 하는 방법이 있을 수 있다. 즉,
    • 사용시 마다 : DB로 부터 객체를 얻어 오거나 CRUD를 하기 위해 그 작업 전후에 session을 가져오고 닫는 방식.
    • service 별 : 하나의 CRUD에 따라 session을 가져오는 것이 아니라 어떤 한 service별로 session을 이용하는 방식이다.
    • Request 별 : 웹에서 사용자의 요청에 따라 하나의 session을 가져오고 그 요청이 끝났을때 session을 닫는 방식이다.
  • Web application에서 가장 추천 할 방법은 세번째의 사용자 Request에 따른 session을 처리 하는 방식으로 필자의 경우에도 이방식을 Filter클래스를 이용하여 구현하였으며 UI쪽의 작업을 위해여 lazy type의 mapping을 이용하여 request가 요청할 때에만 session을 가져와 처리하고 UI처리를 끝냈을 경우 session을 닫았다(lazy나 filter클래스 이용방법은 아래의 예제나 설명을 덧붙이도록 하겠다). 물론 이 방법이 가장 최선이라는 것은 아니라 고객의 다양한 요구사항이나 상황에 따라 적절한 선택을 하면 될 것이며 위의 객체의 life cycle을 확실이 이해하고만 있다면 상황을 처리하는데 큰 어려움이 없으리라 생각 된다.
  • 단 여기서 확실히 집고 넘어갈 것은 Hibernate의 session과 Database에서의 connection을 완전 동일 시 하지 않을 것이다 session을 close한다고 해서 connection이 close되는것은 아니다. 이부분은 db pooling(JNDI, C3P0)등 다양한 방식으로 구현되는데 아래의 Configuration 단원에서 좀더 상세한 설명을 하도록 하겠다.

Configuration

  • Hibernate 는 다양한 환경을 고려하여 설계되어 있기때문에 많은 부분을 설정 하여야 할 수도 있으나 가장 기본적인 디폴트값을 제공하므로 별 어려움 없이 기본적인 사용환경을 설정 할 수 있다.
    Mapping Configuration
  • org.hibernate.cfg.Configuration클래스의 인스턴스 하나가 어플리케이션 내의 RDB 간의 전체 매핑을 담게 된다.
    • xml파일을 이용한 Configuration
      xml파일을 이용한 설정은 여러개의 mapping 파일을 담고 있는 하나의 xml파일로 부터 설정을 이용하는 방법으로 아래의 예제 코드 처럼 hibernate.cfg.xml 파일안에 여러 메핑 파일(xml)을 설정 하고 각 메핑 파일에 전역적으로 적용될수 있는 옵션들을 명기 하고 있다.
      <?xml version='1.0' encoding='utf-8'?>
      <!DOCTYPE hibernate-configuration PUBLIC 
      	"-//Hibernate/Hibernate Configuration DTD//EN"
      	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
      <hibernate-configuration>
      
      <session-factory>
      	<property name="show_sql">true</property>
      	<property name="myeclipse.connection.profile">localTest</property>
      	<property name="connection.url">
      		jdbc:mysql://localhost:3306/test
      	</property>
      	<property name="connection.username">dbuser</property>
      	<property name="connection.password">dbuser</property>
      	<property name="connection.driver_class">
      		com.mysql.jdbc.Driver
      	</property>
      	<property name="dialect">
      		org.hibernate.dialect.MySQLDialect
      	</property>
      
      	<mapping resource="model/mapping/Article.hbm.xml" />
      	<mapping resource="model/mapping/Zine.hbm.xml" />
      	<mapping resource="model/mapping/ZineIssue.hbm.xml" />
      
      </session-factory>
      </hibernate-configuration>
    • mapping파일을 직접적으로 이용하는 Configuration
      hibernate에서는 Configuration 객체에서 직접적으로 mapping파일을 load할 수도 있다. 또한 각 mapping파일의 properties를 따로 줄수도 있으나 동일한 프로퍼티를 가지는 메핑 파일들을 하나의 xml파일에 두고 사용 하는것을 권하는 바이다. 아래의 코드는 직접적으로 메핑 파일을 사용 하는 예시이다.
      HibernateUtil.java
      Configuration cfg = new Configuration()
          .addFile("model/mapping/Article.hbm.xml")
          .addFile("model/mapping/Zine.hbm.xml");
      "HibernateUtil.java
      Properties props = new Properties();
      ...
      Configuration cfg = new Configuration()
          .addClass(model.mapping.Article.class)
          .addClass(model.mapping.Zine.class)
          .setProperties(props);
  • org.hibernate.sessionFactory 인터페이스는 이러한 configuration으로 부터 session을 관리 할 수 있도록 하는 인터페이스의 역활을 담당 한다.
    • Configuration에 의해 모든매핑을 분석한 후, 어플리케이션은 Session 인스턴스들을 위한 팩토리를 확보해야 한다. 이 팩토리는 어플리케이션내 모든 쓰레드에 공유되는 게 정상이지만, 둘 이상의 SessionFactory를 인스턴스화 할 수도 있는데 둘 이상의 데이터베이스를 사용하는 경우 아주 유용하다.
      connection
  • SessionFactory가 Connection들을 맺게 할 수 있는 방법은 아래와 같은 설정 방법 들이 있다
    • java.util.Properties 인스턴스를 Configuration.setProperties()에 전달한다.
    • hibernate.properties를 클래스패스의 루트디렉토리에 둔다.
    • java -Dproperty=value를 사용하여 System 특성을 설정한다.
    • hibernate.cfg.xml내에 요소들을 포함시킨다.
  • 위와 같은 방법들이 있는데 가능 하다면 hibernate.cfg.xml내의 요소를 포함하여 사용하는 방법을 추천한다.
  • JDBC
    • JDBC연결은 SessionFactory에서 기본적으로 JDBC 연결을 사용하여 사용한다.
    • Hibernate의 내장된 JDBC Connection pooling 알고리즘은 기본적인 내용만 있는 간략 한 것으로 거의 TEST용으로만 사용이 되며 실제 프로젝트를 적용하기 위해서 사용해서는 안된다.
  • JNDI
    • Hibernate는 tomcat과 같은 web application 상에서 사용되는 Datasource를 사용할 수 도 있다.
  • C3P0
    • Hibernate의 C3P0ConnectionProvider를 Connection Pool로 사용하게 된다.

Mapping

  • Object/relational mapping은 XML문서로 정의된다. mapping문서는 가독성이 있고 직접 편집할 수 있게 설계되고 있다. mapping 언어는 자바를 중심으로한다. 이것은 mapping들이 테이블 선언이 아니라 선언된 persistent class를 중심으로 구성된다는 의미이다.
    대부분 XML mapping을 직접적으로 작성을 하지만 mapping document를 생성하는 툴(XDoclet, Middlegen, AndroMDA)이 몇개인가 존재한다. 필자의 경우에는 Middlegen을 사용하여 mapping파일을 만들고 필요한 경우 직접 xml을 수정하여 사용하였다.
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
            "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
    
    <hibernate-mapping package="eg">
    
            <class name="Cat" table="CATS" discriminator-value="C">
                    <id name="id" column="uid" type="long">
                            <generator class="hilo"/>
                    </id>
                    <discriminator column="subclass" type="character"/>
                    <property name="birthdate" type="date"/>
                    <property name="color" not-null="true"/>
                    <property name="sex" not-null="true" update="false"/>
                    <property name="weight"/>
                    <many-to-one name="mate" column="mate_id"/>
                    <set name="kittens">
                            <key column="mother_id"/>
                            <one-to-many class="Cat"/>
                    </set>
                    <subclass name="DomesticCat" discriminator-value="D">
                            <property name="name" type="string"/>
                    </subclass>
            </class>
    
            <class name="Dog">
                    <!-- mapping for Dog could go here -->
            </class>
    
    </hibernate-mapping>

    위의 xml파일을 mapping의 예인데 각 항목들의 설명은 아래를 참조하자.
    ORMapping

  • 위의 문서에서 개략적으로 mapping파일의 구조와 attribute들을 이해 하였다면 좀더 살펴봐야 할 몇가지 것들에 대하여 이야기 해보도록하자
    • composite-id : 우리는 DB의 테이블을 단일 PK를 사용할 수 도 있지만 대부분은 복합키를 생성 하여 사용하게 될것이다 이럴테 hibernate에서의 mapping시 두가지의 방법이 있는데, 그 하나는 mapping xml 파일상에서 다른 column과 같이 PK를 사용하는 방법이고 다른 하나는 복합키를 가지는 Class를 따로 작성 하는 방법이 있는데 각각에 대한 장단점이 있겠지만 보편적으로는 복합키를 가지는 클래스를 생성하여 사용하는 것이 좋은 방법이라 할 수 있다. 왜냐 하면 Hibernate자체가 OOP기반 ORM tool이라는 점에서 볼때 key 클래스 자체를 객체화 하는것은 어찌보면 당연할 수 있으며 직접 사용해본 경험으로도 key 클래스를 만들어 비교하거나 생성하는 작업이 소스의 가독성이나 객체기반의 프로그래밍에 더 부합하다는 생각이다.
    • relation mapping : DB의 관계를 mapping 함에 있어서 우리는 1:1, 1:N, N:1의 관계를 가지게 되는데 이러한 관계의 설정을 mapping파일에서 할 수 있으며 사용법은 위의 참조 문서에서 잘 설명이 되어 있다. 여기에서 lazy라는 attribute가 보여지게 되는데 문서에서는 이것을 lazy="true"를 셋팅하는 것은 proxy interface로써 클래스 자신의 이름을 지정하는 것에 동일한(equalivalent) 지름길이다. 이라고 이야기 한는데 무슨뜻인지 쉽사리 와닫지 않을 것이다 간단하게 이야기 한다면 어떤 table에 메핑된 객체와 관계를 가지고 있는 다른 table의 객체를 동적으로 가저올것인지 정적으로 가져올 건지를 이야기 하는것이다. 즉, 아래의 예제에서 zine이라는 table과 relation을 가지고 있는 zine_issue테이블의 데이터를 Zine이라는 객체를 가져오면서 같이 가져올것인지 Zine이라는 객체를 가져오고 ZineIssue라는 객체를 사용할 때 가져올 것인지를 설정 하는 것이라 할수 있다. (단 이부분에서 session 자체를 close한 상태 즉, detached된 상테에서는 lazy=true로 된 객체를 가져 올 수 없다.) 이런 것의 장점이라면 동일은 객체를 가지고서 여러 UI레이어에서 필요에 따른 재사용성을 증대 시킬 수 있다는 것이다. 다시 말하면 필요한곳에서 필요한 데이터만 가져오게 하는 방법이라 할 수 있겠다.

Query

  • 기본적으로 Hibernate에서 DB로 Query하는 방식은 Criteria, HQL, Native Query를 사용하는 방법이 있다.
    Criteria
  • Hibernate자체적으로 지원하는 API로서 Query와는 무관하게 객체를 사용하여 모든 쿼리를 하는 방식으로 Hibernate2에서는 다소 미흡한부분이 많아 Hibernate3에서 많은 update를 가져 왔던 분야이다.
    HQL
  • HQL은 SQL과 유사한 문법의 Hibernate의 언어로서 ORM에서 설정한 객체를 사용할 수 있다.
    Native Query
  • session에서 Connection을 가져와 POJO에서 사용하는것 처럼 직접 Query를 작성하여 사용하는 방식이다.

Sample Hibernate

  • 간단한 Web application의 요구사항에따른 다양한 방식의 예제를 보자.
  • 요구사항
    • web상으로 잡지를 편집/발간 할수있는 시스템
    • 사용자가 만든 잡지는 여러 단행본으로 발행 될 수 있으며 각 단행본은 여러개의 기사를 포함 할 수 있다.
    • 발행된 단행본들은 여러 조건에 따라 web 상에서 사용자에게 보여줄 수 있다.
  • ERD
  • DB Schema
    # Host: localhost
    # Database: test
    # Table: 'articles'
    # 
    CREATE TABLE `articles` (
      `zine_id` varchar(20) NOT NULL default '',
      `issue_no` int(11) NOT NULL default '0',
      `article_idx` int(11) NOT NULL default '0',
      `title` varchar(50) default NULL,
      `contents` text,
      PRIMARY KEY  (`zine_id`,`issue_no`,`article_idx`),
      CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`zine_id`, `issue_no`) REFERENCES `zine_issue` (`zine_id`, `issue_no`)
    ) ENGINE=InnoDB DEFAULT CHARSET=euckr; 
    
    # Host: localhost
    # Database: test
    # Table: 'zine'
    # 
    CREATE TABLE `zine` (
      `zine_id` varchar(20) NOT NULL default '',
      `zine_name` varchar(100) NOT NULL default '',
      `user_id` varchar(20) NOT NULL default '',
      `create_date` datetime NOT NULL default '0000-00-00 00:00:00',
      `delete_date` datetime NOT NULL default '0000-00-00 00:00:00',
      PRIMARY KEY  (`zine_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=euckr; 
    
    # Host: localhost
    # Database: test
    # Table: 'zine_issue'
    # 
    CREATE TABLE `zine_issue` (
      `zine_id` varchar(20) NOT NULL default '',
      `issue_no` int(11) NOT NULL default '0',
      `issue_date` datetime NOT NULL default '0000-00-00 00:00:00',
      `status` char(1) default '',
      PRIMARY KEY  (`zine_id`,`issue_no`),
      CONSTRAINT `zine_issue_ibfk_1` FOREIGN KEY (`zine_id`) REFERENCES `zine` (`zine_id`) ON DELETE CASCADE
    ) ENGINE=InnoDB DEFAULT CHARSET=euckr;
    Hibernate 기본 사용 예제
  • 아무런 특별한 설정없이 Hibernate를 사용하는 예제를 만들어 본다.
    • myEclipse에서 Web Project를 생성
    • zine이라는 Action과 zine.jsp를 struts로 만듬.
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE struts-config PUBLIC 
      "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" 
      "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
      
      <struts-config>
        <data-sources />
        <form-beans >
          <form-bean name="mainForm" type="org.apache.struts.action.DynaActionForm" />
          
      
        </form-beans>
      
        <global-exceptions />
        <global-forwards />
        <action-mappings >
          <action
            attribute="mainForm"
            name="mainForm"
            path="/main"
            scope="request"
            type="action.MainAction">
            <forward
              name="success"
              path="/pages/main.jsp"/>
          </action>
          <action path="/zine" type="action.ZineAction">
            <forward name="success" path="/pages/zine.jsp" />
          </action>
          
          
      
        </action-mappings>
      
        <message-resources parameter="ApplicationResources" />
      </struts-config>
      MainAction.java
      //Created by MyEclipse Struts
      // XSL source (default): platform:/plugin/com.genuitec.eclipse.cross.easystruts.eclipse_4.0.0/xslt/JavaClass.xsl
      
      package action;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      import model.service.ZineService;
      
      import org.apache.struts.action.Action;
      import org.apache.struts.action.ActionForm;
      import org.apache.struts.action.ActionForward;
      import org.apache.struts.action.ActionMapping;
      import org.apache.struts.action.DynaActionForm;
      
      import framework.db.common.Constants;
      
      /** 
       * MyEclipse Struts
       * Creation date: 09-15-2005
       * 
       * XDoclet definition:
       * @struts.action path="/main" name="mainForm" input="/form/main.jsp" scope="request" validate="true"
       * @struts.action-forward name="success" path="/pages/main.jsp" redirect="true"
       */
      public class MainAction extends Action {
      
      	// --------------------------------------------------------- Instance Variables
      
      	// --------------------------------------------------------- Methods
      
      	/** 
      	 * Method execute
      	 * @param mapping
      	 * @param form
      	 * @param request
      	 * @param response
      	 * @return ActionForward
      	 */
      	public ActionForward execute(
      		ActionMapping mapping,
      		ActionForm form,
      		HttpServletRequest request,
      		HttpServletResponse response) {
      		DynaActionForm mainForm = (DynaActionForm) form;
      		ZineService service = new ZineService();
      		request.setAttribute(Constants.NAME_OF_ZINES, service.getMainRecentZines());
      		
      		return(mapping.findForward("success"));
      	}
      
      }
      main.jsp
      <%@ page language="java" contentType="text/html;charset=euc-kr"%>
      <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%> 
      <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
      <%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic"%>
      <%@page import="framework.db.common.Constants"%>
      <html> 
      	<head>
      		<title>JSP for mainForm form</title>
      	</head>
      <body>
      <table align="center" border="1">
      <tr>
      	<td align="center"> zine</td>
      	<td> 발행호수 </td>
      </tr>
      <tr>
      	<logic:iterate id="issuedZine" name="<%=Constants.NAME_OF_ZINES%>">
      	<td><bean:write name="issuedZine" property="comp_id.zineId"/></td>
      	<td><bean:write name="issuedZine" property="comp_id.issueNo"/>호</td>
      	</logic:iterate>
      </tr>
      </table>
      </body>
      </html>
    • myEclipse의 Hibernate Capabilities를 추가 하여 JDBC를 이용한 SessionFactory를 generation.
      SessionFactory.java
      package framework.db;
      
      import org.hibernate.HibernateException;
      import org.hibernate.Session;
      import org.hibernate.cfg.Configuration;
      
      /**
       * Configures and provides access to Hibernate sessions, tied to the
       * current thread of execution.  Follows the Thread Local Session
       * pattern, see {@link http://hibernate.org/42.html}.
       */
      public class SessionFactory {
      
          /** 
           * Location of hibernate.cfg.xml file.
           * NOTICE: Location should be on the classpath as Hibernate uses
           * #resourceAsStream style lookup for its configuration file. That
           * is place the config file in a Java package - the default location
           * is the default Java package.<br><br>
           * Examples: <br>
           * <code>CONFIG_FILE_LOCATION = "/hibernate.conf.xml". 
           * CONFIG_FILE_LOCATION = "/com/foo/bar/myhiberstuff.conf.xml".</code> 
           */
          private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
      
          /** Holds a single instance of Session */
          private static final ThreadLocal threadLocal = new ThreadLocal();
      
          /** The single instance of hibernate configuration */
          private static final Configuration cfg = new Configuration();
      
          /** The single instance of hibernate SessionFactory */
          private static org.hibernate.SessionFactory sessionFactory;
      
          /**
           * Returns the ThreadLocal Session instance.  Lazy initialize
           * the <code>SessionFactory</code> if needed.
           *
           *  @return Session
           *  @throws HibernateException
           */
          public static Session currentSession() throws HibernateException {
              Session session = (Session) threadLocal.get();
      
              if (session == null) {
                  if (sessionFactory == null) {
                      try {
                          cfg.configure(CONFIG_FILE_LOCATION);
                          sessionFactory = cfg.buildSessionFactory();
                      }
                      catch (Exception e) {
                          System.err.println("%%%% Error Creating SessionFactory %%%%");
                          e.printStackTrace();
                      }
                  }
                  session = sessionFactory.openSession();
                  threadLocal.set(session);
              }
      
              return session;
          }
      
          /**
           *  Close the single hibernate session instance.
           *
           *  @throws HibernateException
           */
          public static void closeSession() throws HibernateException {
              Session session = (Session) threadLocal.get();
              threadLocal.set(null);
      
              if (session != null) {
                  session.close();
              }
          }
      
          /**
           * Default constructor.
           */
          private SessionFactory() {
          }
      
      }
    • MiddleGen을 이용하여 DB의 mapping파일과 dto객체를 생성.
      아래는 hibernate.cfg.xml 파일이다
      <?xml version='1.0' encoding='utf-8'?>
      <!DOCTYPE hibernate-configuration PUBLIC 
      	"-//Hibernate/Hibernate Configuration DTD//EN"
      	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
      <hibernate-configuration>
      
      <session-factory>
      	<property name="show_sql">true</property>
      	<property name="myeclipse.connection.profile">localTest</property>
      	<property name="connection.url">
      		jdbc:mysql://localhost:3306/test
      	</property>
      	<property name="connection.username">dbuser</property>
      	<property name="connection.password">dbuser</property>
      	<property name="connection.driver_class">
      		com.mysql.jdbc.Driver
      	</property>
      	<property name="dialect">
      		org.hibernate.dialect.MySQLDialect
      	</property>
      
      	<mapping resource="model/mapping/Article.hbm.xml" />
      	<mapping resource="model/mapping/Zine.hbm.xml" />
      	<mapping resource="model/mapping/ZineIssue.hbm.xml" />
      
      </session-factory>
      </hibernate-configuration>
    • model의 service와 dao객체를 생성
    • action코드 생성.
    • 위의 각 코드들은 이 페이지의 attachment에 있는 SimpleExam.zip 파일의 소스를 참조 하여 확인 하기 바란다.
Hibernate C3P0 db pooling 사용 예제
  • 위의 예제에서 JDBC를 사용하지 않고 C3P0 DB 풀링 라이브러리를 사용하여 SessionFactory를 변경 한 예제
  • 변경된 부분
    • Hibernate configuration file
      <?xml version='1.0' encoding='UTF-8'?>
      <!DOCTYPE hibernate-configuration PUBLIC
                "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                
번호 제목 글쓴이 날짜 조회 수
47 XpressEngine(XE) 에서 엮인글 스팸 방지법 file 황제낙엽 2017.08.25 461
46 [Hibernate] 페이징 처리 정리 황제낙엽 2007.02.26 457
» [Hibernate] Hibernate 프레임워크를 이용한 효율적인 개발 전략 황제낙엽 2007.01.30 389
44 XStream 배우기 : 별칭(Alias) 배우기 황제낙엽 2011.04.20 340
43 JSTL과 Velocity를 활용한 UI 레이어 구현 방법 황제낙엽 2007.01.30 339
42 XStream 배우기 : 변환기(Converter) 배우기 황제낙엽 2011.04.20 298
41 Custom XStream Converter 황제낙엽 2011.04.26 212
40 [Hibernate] Hibernate Tutorial 황제낙엽 2012.11.15 166
39 [Hibernate] Hibernate 와 Ant 에서 composite id 사용하기 예제 file 황제낙엽 2005.11.29 134
38 검색엔진 루씬 Lucene... Analyzer의 선택 황제낙엽 2007.11.27 102
37 Map <-> XML (2) 황제낙엽 2011.04.29 99
36 [Hibernate] 하이버네이트 참조문서 버전 3.2 cr3의 최신 업데이트 한글 번역본 file 황제낙엽 2007.07.03 98
35 벨로시티에서 loop 작성 황제낙엽 2005.12.13 85
34 XE 서버 이전 계획 file 황제낙엽 2018.08.29 80
33 SiteMesh를 이용하여 웹 페이지의 레이아웃을 처리 황제낙엽 2007.08.13 67
32 XML -> (Map) XML 황제낙엽 2011.04.29 61
31 Jakarta Lucene (루씬) - 들어가기 황제낙엽 2007.07.30 57
30 Xdoclet 사용하기 1부 황제낙엽 2006.10.04 45
29 XStream 배우기 : 2분만에 배우는 XStream 황제낙엽 2011.04.20 40
28 Xdoclet 사용하기 2부 황제낙엽 2006.10.04 40