프로그래밍/Hibernate(하이버네이트)

하이버네이트 쉽게 입문하기 (기초)-환경설정,입력조회 개발

Terry Cho 2014. 5. 17. 00:23

하이버네이트 입문하기 (기초 #1)

MyBatis만 사용하다가 근래에 들어서 Hibernate를 봐야하는 일이 생겨서, 늙은 나이에, Hibernate를 처음부터 보고 있다.

한국에서는 거의 MyBatis가 대세를 이루지만 해외의 경우 높은 생산성등을 이유로 Hibernate가 거의 대세를 이룬다.

그러나, 한국에서 안사용하는 이유 처럼, Hibernate는 자체적으로 쿼리를 생성하고, OR Mapper로써, 객체들을 DB에서 로딩할때, 레퍼런스된 객체등을 로딩하는등, 제대로 특성을 이해하고 사용하지 않으면 장애를 일으키는 경우가 많지 않기 때문에, 해외에 비해서 한국에서는 많이 사용되지 않는다. (SI에서 기피)


이글에서는 하이버네이트에 대한 간단한 사용법에 대해서 소개하고자 한다.

하이버네이트는 자바 기반의 ORM (Object Relationship Mapper)이다. 쉽게 이야기 해서, 자바 객체를 RDBMS의 하나의 ROW로 맵핑해준다.

이 ORM 개념은 상당히 오래전부터 나왔는데, 처음의 시초는 EJB Entity Beans라고 할 수 있는데, 이 EJB는 여러가지 강력한 기능을 가졌음에도 불구하고, 사용법이 너무 어려워서, 근래에는 거의 사용되지 않는다. 이렇게 사용법이 어려워서 이를 간편화 하여 사용법은 편하게 하면서도, 더 많은 기능을 지원하게 한것이 ORM인데, 그중에서 대표적인 것이 Hibernate이다.

Hibernate가 개발된후에, Java 스펙에도 ORM의 표준 스펙을 정의했는데 이것이 JPA (Java Persistence API)이다. JPA는 하나의 스펙으로 JPA에 대한 구현체가 필요하다. 실제 구현체는 TopLink, OpenJPA등 여러가지가 있는데, 이렇게 ORM API를 JPA를 통해서 추상화 시켜놓음으로써, 다른 ORM과 쉽게 교체가 가능하게 만든 개념이지만, 실제로 ORM은 거의 Hibernate만 사용되기 때문에, JPA를 사용하는 것은 큰 의미가 없다.

JPA는 기본적으로 JBoss나 Weblogic과 같은 JEE 컨테이너를 가정으로 개발되어서 JTS/JTA, EJB3등과 사용하기에는 좋지만, 컨테이너 없이 Tomcat과 같은 Servlet 컨테이너만 사용할 경우, 큰 이득을 보기 어렵다.


1. 개발환경 

하이버네이트를 조금 더 쉽게 개발하려면 이클립스에 JBoss Tool이라는 것을 설치한다. (이 안에 있는 Hibernate Tool만 골라서 설치해도 된다.)

다음에, 이클립스에서 Hibernate Perspective를 추가 한다. 그리고, Hibernate lib와, JDBC 라이브러리를 다운받아서 이클립스 클래스 패스에 추가한다.


2. hibernate configuration 설정

hibernate를 사용하기 위해서는 hibernate에 대한 설정을 해야 한다. 사용하는 DB 정보등을 정해야 하는데, 앞에서 설치한 Hibernate Tool을 이용하면 간편하게 설정할 수 있다.



다음과 같이 데이타 베이스 환경등을 설정하자.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

                                         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

 <session-factory >

  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

  <property name="hibernate.connection.password">asdf1234</property>

  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306</property>

  <property name="hibernate.connection.username">terry</property>

  <property name="hibernate.default_schema">terry</property>

  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<mapping resource="terry/hibernate/simple/model/User.hbm.xml"/>

 </session-factory>

</hibernate-configuration>


다음으로, 데이타베이스에, User 라는 테이블을 생성한다.

create table terry.USER (EMAIL varchar(255) not null, TENANT_ID varchar(255), LASTNAME varchar(255), FIRSTNAME varchar(255), COMPANY varchar(255), primary key (EMAIL))


3. Model Class 생성

모델 클래스는 POJO 클래스로, 하나의 테이블 ROW에 맵핑되는 자바 클래스이다.

데이타를 저장하는 attribute와 set/getter로 구성한다.

package terry.hibernate.simple.model;

public class User {
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getLastName() {
		return LastName;
	}
	public void setLastName(String lastName) {
		LastName = lastName;
	}
	public String getFirstName() {
		return FirstName;
	}
	public void setFirstName(String firstName) {
		FirstName = firstName;
	}
	public String getCompany() {
		return company;
	}
	public void setCompany(String company) {
		this.company = company;
	}
	public String getTenant_id() {
		return tenant_id;
	}
	public void setTenant_id(String tenant_id) {
		this.tenant_id = tenant_id;
	}
	String email;
	String LastName;
	String FirstName;
	String company;
	String tenant_id;
	
}


4. Model Class to DB Table 맵핑

이렇게 클래스를 생성했으면, 이 클래스가 DBMS의 테이블과 어떻게 맵핑이 되는지를 명시해야 하는데, 이는 *.hbm.xml 파일을 사용하는 방법과 Java Annotation을 사용하는 방법 두가지가 있다. 일반적으로 annotation을 사용하는 방법이 많이 쓰이지만 여기서는 *.hbm.xml 을 어떻게 사용하는지 소개한다.

이클립스에서 New 를 선택하면 아래 그림과 같이 Hibernate 메뉴 아래에 "Hibernate XML Mapping file"을 선택할 수 있다.

그후에 다음으로 넘어가면, hbm 파일을 생성할 자바 클래스를 고르도록 하는데, 클래스를 생성하면, 자동으로 *.hbm.xml 파일이 생성된다.

다음은 자동으로 생성된 User.hbm.xml 파일이다.

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<!-- Generated 2014. 5. 16 ???? 12:36:25 by Hibernate Tools 3.4.0.CR1 -->

<hibernate-mapping>

    <class name="terry.hibernate.simple.model.User" table="USER">

        <id name="email" type="java.lang.String">

            <column name="EMAIL" />

            <generator class="assigned" />

        </id>

        <property name="tenant_id" type="java.lang.String">

            <column name="TENANT_ID" />

        </property>

         <property name="LastName" type="java.lang.String">

            <column name="LASTNAME" />

        </property>

        <property name="FirstName" type="java.lang.String">

            <column name="FIRSTNAME" />

        </property>

        <property name="company" type="java.lang.String">

            <column name="COMPANY" />

        </property>

    </class>

</hibernate-mapping>


여기서, DB의 PrimaryKey의 경우에는 위와 같이 <id> 태그로 지정해줘야하고, 나머지의 경우에는 <property> 로 지정해주면 된다.
다음으로, 하이버네이트에, 이 hbm 파일의 위치를 알려줘야 하는데, 앞에서 생성한, hibernate.cfg.xml에 그 내용을 아래와 같이 추가한다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

                                         "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

 <session-factory >

  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

  <property name="hibernate.connection.password">asdf1234</property>

  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306</property>

  <property name="hibernate.connection.username">terry</property>

  <property name="hibernate.default_schema">terry</property>

  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<mapping resource="terry/hibernate/simple/model/User.hbm.xml"/>

 </session-factory>

</hibernate-configuration>


5. Session Factory 생성


하이버네이트는 SessionFactory에서 Session을 생성해서 (DB Connection과 유사-나중에 디테일은 좀 찾아봐야 겠음). 데이타베이스에 대한 명령을 처리하는데, 여기서는 아래와 같이 Singletone 패턴을 이용해서 생성하였다.
package terry.hibernate.simple.utils;

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {
	static SessionFactory sessionFactory;
	static ServiceRegistry serviceRegistry;
	
	static{
		try{
			Configuration configuration = new Configuration().configure();
			serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
		}catch(HibernateException e){
			e.printStackTrace();
		}
	}
	
	public static SessionFactory getSessionFactory(){ 
		return sessionFactory;
	}

}


6. Insert , Select & Update

다음으로, 실제로 insert & select를 하는 코드를 구현해보자


package terry.hibernate.simple;

package terry.hibernate.simple;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

import terry.hibernate.simple.model.User;
import terry.hibernate.simple.utils.HibernateUtil;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        User user = new User();
        user.setEmail("cath@gmail.com");
        user.setFirstName("Terry");
        user.setLastName("Cho");
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(user);
        session.getTransaction().commit();
        System.out.println("Insert completed");
     
        session.close();
        sessionFactory.close();
    }
}

Session Factory에서 session을 가져온 후에, User Model 객체를 생생한후, beginTransaction으로 트렌젝션을 시작하고, save를 한후에, 트렌젝션을 commit한후, session과 sessionFactory를 close한다. 데이타 베이스를 확인해보면, 하나의 ROW가 Insert 되었음을 확인할 수 ㅇ

이때 HibernateTool을 사용했으면 아래와 같은 에러가 날 수 있는데,

org.hibernate.engine.jndi.JndiException: Error parsing JNDI name [] 이는 hibernate.cfg.xml에서 위자드로 자동 생성될때 아래와 같이 생성되는데 <session-factory name=""> name 부분이 “” 으로 정의되어 있어서 그렇다. <session-factory>로 수정해주면 된다.(Hibernate tools의 버그인듯)


다음으로, select와 update를 추가해보자

select는 session.get(클래스명,PrimaryKey값)을 사용하면 된다. 또는 session.load를 사용해도 된다 select된 model 객체는 값을 set을 한후에, session.commit을 하게 되면, 데이타베이스에 그 값이 update된다.

public class App 
{
    public static void main( String[] args )
    {
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        User user = new User();
        user.setEmail("cath@gmail.com");
        user.setFirstName("Terry");
        user.setLastName("Cho");
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(user);
        session.getTransaction().commit();
        System.out.println("Insert completed");

        session.beginTransaction();
        User myuser = (User)session.get(User.class, "cath@gmail.com");
        System.out.println("user name:"+myuser.getFirstName());
        myuser.setFirstName("Cath");
        session.getTransaction().commit();
        
        session.close();
        sessionFactory.close();
    }
}


7. 테이블 자동 생성
하이버네이트에서는, 편한 개발을 위해서, 데이타베이스에 테이블을 자동으로 생성하는 기능을 제공한다.
이 기능은, 단지 개발에서만 사용해야 하며, (데이타타입이나 길이가 의되하지 않은 형태로 생성됨), 운영 환경에서는 사용하지 않도록 하자.
이 기능을 사용하기 위해서는Hibernate configuration의 SessionFactory property에서 hdm2ddl auto를 선택해준다.
4가지 옵션이 있는데,
  • Validate - 테이블을 전혀 손대지 않고, 프로그램에 필요한 테이블 구조와 일치하는지 확인한후, 일치하지 않으면 에러를 출력한다.
  • Update - 기존 테이블이 현재 실행에 필요한 테이블 구조와 다를 경우 수정한다. (데이타는 지우지 않는다.)
  • Create - 기존 테이블을 지운 후, 실행할때 마다 매번 생성한다.
  • Create Drop - 테이블을 생성해서 프로그램을 실행한 후에, 테이블을 다시 지워버림
아래에서는 매번 테이블을 새로 생성하도록 create를 사용했다.

<hibernate-configuration> <session-factory name=""> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.password">asdf1234</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306</property> <property name="hibernate.connection.username">terry</property> <property name="hibernate.default_schema">mydb</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.hbm2ddl.auto">create</property> <mapping resource="terry/hibernate/simple/model/User.hbm.xml"/> </session-factory> </hibernate-configuration>


8. 하이버네이트에 의해서 수행되는 SQL 쿼리 보기
앞에서도 언급하였듯이, 하이버네이트는 자동으로, SQL 쿼리를 생성하기 때문에, 어떤 쿼리가 수행되었는지 확인하고 싶을 수 가 있다.
이때는 config 파일에 다음과 같은 property를 추가한다.
<property name="hibernate.show_sql">true</property>

그후에 실행을 해보면, 

다음과 같이 실행되는 SQL을 확인할 수 있다.

5 16, 2014 11:20:41 오후 org.hibernate.tool.hbm2ddl.SchemaExport execute

INFO: HHH000227: Running hbm2ddl schema export

Hibernate: drop table if exists terry.USER

Hibernate: create table terry.USER (EMAIL varchar(255) not null, TENANT_ID varchar(255), LASTNAME varchar(255), FIRSTNAME varchar(255), COMPANY varchar(255), primary key (EMAIL))

5 16, 2014 11:20:41 오후 org.hibernate.tool.hbm2ddl.SchemaExport execute

INFO: HHH000230: Schema export complete

Hibernate: insert into terry.USER (TENANT_ID, LASTNAME, FIRSTNAME, COMPANY, EMAIL) values (?, ?, ?, ?, ?)

Insert completed

user name:Terry

Hibernate: update terry.USER set TENANT_ID=?, LASTNAME=?, FIRSTNAME=?, COMPANY=? where EMAIL=?

5 16, 2014 11:20:41 오후 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop

INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306]



※ 참고 : 하이버네이트에서는 자체적으로 c3p0 connection pool을 사용한다. 이 Pool은 개발용으로는 충분할지 몰라도, 기능들이 부족하여, 운영환경에서는 적합하지 않다. 반드시 운영환경에서는 dbcp와 같은 connection pool로 변경하기 바란다.(다음에 기회 되면 포스팅)


간단하게, hibernate에 대한 기본 사용법에 대해 알아보았다. 

실제로 hibernate는 spring-hibernate 프레임웍과 많이 사용되지만, hibernate에 대한 개념을 조금더 명확히 이해하기 위해서 spring을 배제하고, 설명하였다. (나중에 기회되면, spring-hibernate 연동에 대해서도 포스팅 예정)