주요글: 도커 시작하기
반응형
Hibernate API에서 사용될 세션 및 트랜잭션 프로퍼티를 설정하는 방법에 대해서 살펴본다.

Hibernate의 세션 설정 방법 및 프로퍼티 설정 방법

Hibernate API를 사용하는 기본 코드는 아래와 같은 형태를 띄고 있다.

    // 1. 초기화 메소드에서 SessionFactory를 초기화한다.
    Configuration cfg = new Configuration();
    cfg.configure();
    SessionFactory sessionFactory = cfg.buildSessionFactory();
    
    // 2. 실제 메소드에서는 sessionFactory에서 Session을 생성해서 사용한다.
    Session s = sessionFactory.openSession();
    ... // Session을 사용해서 CRUD 실행
    s.close();

코드는 크게

  1. OR 매핑 정보 및 커넥션 프로퍼티 정보를 담고 있는 Configuration을 생성하고, Configuration으로부터 SessionFactory를 생성하는 부분과
  2. SessionFactory로부터 Session을 구해 CRUD 작업을 처리하는 부분
이렇게 두 부분을 구성된다. 첫번째 부분은 Hibernate Session에 대한 어플리케이션의 초기화 과정에서 실행될 것이고, 두번째 부분은 어플리케이션의 로직을 구현하는 과정에서 사용될 것이다. 본 글에서는 첫번째 부분, 즉 OR 매핑 정보 및 커넥션 프로퍼티 정보를 담고 있는 Configuration을 생성하는 방법에 대해서 살펴볼 것이다.

Hibernate Session 생성 방법

Session은 데이터베이스 세션을 의미하는 것으로서, Session이 사용할 데이터베이스 커넥션을 구하는 방법은 다음과 같이 3가지가 존재한다.

  • JNDI로부터 커넥션 DataSource를 받아서 Session에 전달하기
  • Hibernate가 제공하는 커넥션 풀을 사용하기
  • 직접 생성한 커넥션을 Session에 전달하기
JNDI로부터 DataSource를 받거나 Hibernate가 제공하는 커넥션 풀을 사용하는 것이 일반적인 방법이다. 경우에 따라서 직접 커넥션을 생성해서 Hibernate Session에 제공해주어야 하는 경우가 있지만 비추천, 어쩔 수 없는 경우가 아니라면 직접 커넥션을 생성해서 제공하지 않기 바란다.

프로퍼티 정보 명시하기

커넥션 정보는 프로퍼티를 통해서 명시할 수 있는데, 프로퍼티는 4가지 방법으로 지정할 수 있다. Hibernate는 다음의 순서대로 프로퍼티 정보를 읽어온다.

  1. Configuration.setProperties() 메소드에 전달된 java.util.Properties에 저장된 프로퍼티 정보
  2. 클래스패스 루트에 위치한 hibernate.properties 파일에 저장된 프로퍼티 정보
  3. java -Dproperty=value를 사용해서 전달된 시스템 프로퍼티
  4. hibernate.cfg.xml 파일에 <property> 태그로 명시한 프로퍼티 정보
본 시리즈의 첫번째 글인 'Hibernate를 이용한 ORM 1 - 퀵 스타트'에서 사용한 방법은 hibernate.cfg.xml 파일을 사용한 네번째 방법이었다. 필자의 경우는 두번째와 네번째 방법을 선호하는데, 자신만의 설정 시스템에 Hibernate 설정 정보를 담고 싶다면 첫번째 방법을 사용하면 된다.

위의 네 가지 방법 중에서 두번째와 네번째는 파일을 클래스패스 루트에 위치시키면 다음의 코드를 통해서 Hibernate가 설정 파일로부터 프로퍼티를 읽어온다.

    Configuration cfg = new Configuration().configure();
    // 또는 아래와 같이 두줄로 실행
    // Configuration cfg = new Configuration();
    // cfg.configure();

하지만, 첫번째 방법을 사용하고 싶다면 아래와 같이 직접 코드에서 Properties 객체를 생성한 뒤 Configuration 객체에 전달해주어야 한다.

    Properties prop = new Properties();
    prop.setProperty(...);
    ...
    Configuration cfg = new Configuration();
    cfg.setProperties(prop);

네 가지 방법 중 어떤 방법으로 사용하더라도 Hibernate가 사용하는 프로퍼티의 이름과 값의 의미는 동일하므로, 본 글에서는 XML 파일 포맷인 hibernate.cfg.xml 파일에 프로퍼티 정보를 명시하는 방법을 사용해서 커넥션 정보를 명시하는 형태로 예제를 보여줄 것이다.

Sessin 설정 방법: JNDI 설정/커넥션 풀/직접 커넥션 제공

앞서 말했듯이 Hibernate Session이 사용할 데이터베이스 커넥션을 JNDI나 Hibernate의 커넥션 풀로부터 얻어오거나 또는 사용자가 직접 제공한 커넥션을 사용할 수도 있다.

JNDI 설정

Hibernate가 JNDI에 바인딩된 DataSource로부터 데이터베이스 커네션을 구하도록 설정하려면 다음과 같은 프로퍼티를 사용하면 된다.

프로퍼티 이름
hibernate.connection.datasource DataSource의 JNDI 이름
hibernate.jndi.url JNDI 제공자의 URL (옵션)
hibernate.jndi.class JNDI의 InitialContextFactory의 클래스 (옵션)
hibernate.connection.username 데이터베이스 사용자 (옵션)
hibernate.connection.password 데이터베이스 사용자 암호 (옵션)

예를 들어, JNDI에 등록된 DataSource의 이름이 "hibernate" 라면 다음과 같이 connection.datasource 프로퍼티 값을 설정해주면 된다. (위 표에 있는 나머지 프로퍼티들은 대부분의 WAS에서 설정하지 않아도 문제가 되지 않는다.)

    <?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="connection.datasource">hibernate</property>
            <property name="hibernate.transaction.factory_class">
                net.sf.hibernate.transaction.JTATransactionFactory
            </property>
            <property name="hibernate.trasaction.manager_lookup_class">
                net.sf.hibernate.transaction.WeblogicTransactionManagerLookup
            </property>
            <property name="dialect">net.sf.hibernate.dialect.Oracle9Dialect</property>
            
            <mapping resource="Cat.hbm.xml" />
        </session-factory>
    </hibernate-configuration>

위 프로퍼티에서 DataSource외에 나머지 프로퍼티에 대해서 'SQL Dialect와 트랜잭션 전략 설정' 부분에서 설명할 것이므로 아직까진 신경쓰지 말기 바란다.

간단하게 톰캣의 예를 들어, 데이터 소스 설정 예제를 살펴보도록 하겠다. 참고로, 여기서 보여주는 예제는 지난 1회 글 'Hibernate를 이용한 ORM 1 - 퀵 스타트'에서 사용한 예제에서 설정 파일부분만 변경한 것이다.

먼저 톰캣에 웹 어플리케이션에서 사용할 DataSource에 대한 JNDI 설정을 추가해야 한다. 이 설정은 [톰캣]/conf/server.xml 파일에 추가하면 되며, 아래와 같은 형태의 코드를 사용하면 된다. (톰캣에서 DataSource를 추가하는 방법에 대한 자세한 내용은 톰캣 사이트인 http://jakarta.apache.org/tomcat/을 참고하기 바란다.)

파일 [톰캣]/config/server.xml에 추가할 내용
    <Host ...>
        ...
        <Context path="/hibernate" docBase="hibernate">
            <Resource name="jdbc/hibernate" auth="Container" 
                         scope="Shareable" type="javax.sql.DataSource" />
            <ResourceParams name="jdbc/hibernate">
                <parameter>
                    <name>factory</name>
                    <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
                </parameter>
                <parameter>
                    <name>url</name>
                    <value>jdbc:oracle:thin:@localhost:1521:ORA</value>
                </parameter>
                <parameter>
                    <name>driverClassName</name>
                    <value>oracle.jdbc.driver.OracleDriver</value>
                </parameter>
                <parameter>
                    <name>username</name>
                    <value>scott</value>
                </parameter>
                <parameter>
                    <name>password</name>
                    <value>tiger</value>
                </parameter>
                <parameter>
                    <name>maxWait</name>
                    <value>3000</value>
                </parameter>
                <parameter>
                    <name>maxIdle</name>
                    <value>100</value>
                </parameter>
                <parameter>
                    <name>maxActive</name>
                    <value>10</value>
                </parameter>
            </ResourceParams>
        </Context>
        
        ...
    </Host>

위 코드에서는 JNDI에 등록될 DataSource의 이름을 "jdbc/hibernate"로 지정하였는데, 이는 실제로 "java:comp/env/" 콘텍스트에 추가되므로 설정한 DataSource의 완전한 JNDI 이름은 "java:comp/env/jdbc/hibernate"가 된다. 따라서, Hibernate의 설정 파일에 아래와 같이 hibernate.connection.datasource 프로퍼티의 값을 주면 된다.

파일경로: [톰캣]/webapps/hibernate/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="connection.datasource">java:comp/env/jdbc/hibernate</property>
        <property name="dialect">net.sf.hibernate.dialect.Oracle9Dialect</property>
        
        <mapping resource="Cat.hbm.xml" />
    </session-factory>
</hibernate-configuration>

어플리케이션 서버(톰캣)에 DataSource를 JNDI에 등록하고, JNDI를 사용하도록 Hibernate의 프로퍼티를 설정하면 모든 게 완료된다. 소스 코드는 전혀 변경할 필요가 없이 이 두가지만 변경해주면 1회에 작성했던 예제를 실행할 수 있을 것이다.

커넥션 풀 설정

Hibernate는 자체적으로 커넥션 풀 기능을 제공하고 있는데, Hibernate Session이 커넥션 풀로부터 필요한 데이터베이스 커넥션을 구하도록 설정할 수 있다. 기본적인 설정 방법은 다음과 같다.

<?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">JDBC 드라이버 클래스</property>
        <property name="hibernate.connection.url">JDBC URL</property>
        <property name="hibernate.connection.username">DB User</property>
        <property name="hibernate.connection.password">DB User Password;/property>
        <property name="hibernate.connection.pool_size">DB User Password;/property>
        
        ...
        
    </session-factory>
</hibernate-configuration>

위의 설정 내용을 보면 알겠지만, Hibernate Session이 Hibernate가 제공하는 커넥션 풀을 사용하도록 설정하려면 다음의 네 가지 기본 프로퍼티를 요구한다.

  • hibernate.connection.driver_class - DB 연결시 사용할 JDBC 드라이버 클래스
  • hibernate.connection.url - JDBC URL
  • hibernate.connection.username - DB 사용자 이름
  • hibernate.connection.password - DB 사용자 암호
  • hibernate.connection.pool_size - 커넥션 풀의 최대 크기 명시. (선택)
위의 프로퍼티를 설정하면 Hibernate는 자체적으로 제공하는 커넥션 풀을 사용하게 된다. 하지만, Hibernate에서 제공하는 커넥션 풀은 그다지 좋지 못하며, Hibernate 레퍼런스 문서를 보면 Hibernate의 배포판과 함께 제공하는 C3P0, DBCP 또는 Proxool 라이브러리를 사용하도록 권고하고 있다. 위의 다섯가지 속성 중에서 hibernate.connection.pool_size 대신에 DBCP, C3P0 또는 Proxool 관련 프로퍼티를 설정하면 Hibernate는 자동으로 이들 라이브러리를 커넥션 풀로 사용한다. 본 글에서는 DBCP에 대한 내용만 살펴보며, 나머지 C3P0와 Proxool에 대해서는 살펴보지 않겠다. C3P0와 Proxool를 설정하기 위한 프로퍼티는 Hibernate 홈페이지 및 각 라이브러리의 홈페이지를 참고하기 바란다.

DBCP 관련 프로퍼티

  • DBCP를 실행하는 데 필요한 jar 파일을 복사한다. 이 파일들은 hibernate 배포판의 lib/ 폴더에 포함되어 있다. 웹 어플리케이션이라면 DBCP 관련 jar 파일들을 WEB-INF/lib 폴더에 복사해주면된다.
    commons-dbcp-1.2.1.jar, commons-collections-2.1.1.jar, commons-pool-1.2.jar
  • DBCP 관련 프로퍼티를 설정한다.
DBCP 관련 프로퍼티에는 다음과 같은 것들이 있다.

프로퍼티 이름
hibernate.dbcp.maxActive 한번에 풀에서 갖다 쓸 수 있는 최대 커넥션 개수
hibernate.dbcp.whenExhaustedAction 풀에서 더이상 가져올 수 있는 커넥션이 없을 때 어떻게 처리할지를 명시한다. 값이 1이면, 풀에서 가져올 커넥션이 생길 때 까지 블럭킹된다. 값이 2이면, 풀은 새로운 커넥션을 생성해서 리턴한다. 값이 0이면, 에러를 발생시킨다. 기본값은 1이다.
hibernate.dbcp.maxWait hibernate.dbcp.whenExhaustedAction 프로퍼티의 값이 1인 경우, 블럭킹될 시간을 1/1000초 단위로 지정한다. 0보다 작으면 무한히 대기한다.
hibernate.dbcp.maxIdle 사용되지 않고 풀에 저장될 수 있는 최대 커넥션 개수
hibernate.dbcp.validationQuery 커넥션을 가져올 때 커넥션의 유효성 여부를 검사할 쿼리를 입력한다. 예를 들어, 오라클인 경우 select count(*) from dual과 같은 쿼리를 사용할 수 있다.
hibernate.dbcp.testOnBorrow true이면 풀에서 커넥션을 가져올 때 커넥션이 유효한지 검사한다.
hibernate.dbcp.testOnReturn true이면 커넥션을 풀에 반환할 때 커넥션이 유효한지 검사한다.

위의 모든 프로퍼티를 지정할 필요는 없으며, hibernate.dbcp. 로 시작하는 프로퍼티가 존재하면 Hibernate는 DBCP를 커넥션 풀 모듈로 사용하게 된다.

커넥션 Session에 직접 전달

앞서 두 가지 방법, 즉 JNDI를 사용하는 방법과 커넥션 풀을 사용하는 방법은 Hibernate의 프로퍼티로 설정할 수 있었다. 그런데 부득이한 이유로 JNDI나 커넥션 풀을 사용할 수 없는 경우도 있을 것이다. 예를 들어, 기존 커넥션 관련 모듈이 있어서 Hibernate도 이 모듈에서 제공하는 커넥션을 사용해야 하는 경우가 발생할 수 있다.

이런 경우에는 커넥션 관련된 Hibernate 프로퍼티에서 설정할 값이 없으며, 다음과 같이 SessionFactory.openSession(Connection conn) 메소드를 호출해서 Session 객체를 생성하면 된다.

    SessionFactory sessionFactory = ....;
    
    Connectoin conn = ...;
    
    Session session = sessionFactory.openSession(conn);
    ...
    session.close();
    conn.close();

직접 커넥션을 Session에 전달할 때 주의할 점은, 하나의 JDBC 커넥션에 대해서 동시에 두 개의 열린 Session을 생성해서는 안 된다는 점이다. 즉, 다음과 같은 코드를 작성하지 않도록 주의해야 한다.

    Connectoin conn = ...;
    
    Session session1 = sessionFactory.openSession(conn);
    Session session2 = sessionFactory.openSession(conn); // 같은 커넥션으로 Session 생성
    ...
    session1.close();
    session2.close();
    ...
    
    conn.close();

SQL Dialect와 트랜잭션 속성/처리 클래스 설정

Hibernate는 DBMS에 따라서 최적화된 기능을 제공할 수 있으며, 또한 사용할 어플리케이션 서버에 알맞은 트랜잭션 기능을 제공할 수 있다. DBMS에 최적화된 기능을 제공하기 위해 사용되는 것이 SQL Dialect이며, hibernate.trasaction.manager_lookup_class 프로퍼티를 통해서 트랜잭션 처리 클래스를 설정할 수 있다.

SQL Dialect

Hibernate는 특정 DBMS가 제공하는 기능을 사용할 수 있는 기능을 제공하며, Dialect 프로퍼티를 사용해서 이 기능을 사용할 수 있다. 예를 들어, Hibernate는 오라클를 사용할 때는 오라클에 최적화되도록 기능을 수행하며 MySQL을 사용하는 경우에는 MySQL에 최적화되도록 기능을 수행할 수 있다. 예를 들어, 오라클에 알맞은 Dialect를 설정하면, Hibernate는 시퀀스와 같이 DBMS에 특징적인 기능을 개발자가 직접 코딩하지 않고도 사용할 수 있게 된다.

hibernate.dialect 프로퍼티 값을 명시하면 SQL Dialect를 설정할 수 있는데, 각 DBMS에 따라 다음 표와 같이 프로퍼티값을 명시하면 된다.

DBMS 프로퍼티값
DB2 net.sf.hibernate.dialect.DB2Dialect
DB2 AS/400 net.sf.hibernate.dialect.DB2400Dialect
DB2 OS390 net.sf.hibernate.dialect.DB239Dialect
PostgreSQL net.sf.hibernate.dialect.PostgreSQLDialect
MySQL net.sf.hibernate.dialect.MySQLDialect
Oracle (모든 버전) net.sf.hibernate.dialect.OracleDialect
Oracle 9/10g net.sf.hibernate.dialect.Oracle9Dialect
Sybase net.sf.hibernate.dialect.SybaseDialect
Sybase Anywhere net.sf.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server net.sf.hibernate.dialect.SQLServerDialect
SAP DB net.sf.hibernate.dialect.SAPDBDialect
Informix net.sf.hibernate.dialect.InformixDialect
HypersonicSQL net.sf.hibernate.dialect.HSQLDialect
Ingres net.sf.hibernate.dialect.IngresDialect
Progress net.sf.hibernate.dialect.ProgressDialect
Mckoi SQL net.sf.hibernate.dialect.MckoiDialect
Interbase net.sf.hibernate.dialect.InterbaseDialect
Pointbase net.sf.hibernate.dialect.PointbaseDialect
FrontBase net.sf.hibernate.dialect.FrontbaseDialect
Firebird net.sf.hibernate.dialect.FirebirdDialect

트랜잭션 속성 및 처리 클래스 설정

Hibernate의 Transaction 객체를 사용하기 위해서는 hibernate.transaction.factory_class 프로퍼티 속성을 설정해야 한다. 이 속성을 통해서 사용할 Transaction 구현체를 명시하게 된다. Hibernate는 기본적으로 두 가지 Transaction 구현체를 제공하고 있으며, 이 두 구현체는 다음과 같다.

  • net.sf.hibernate.transaction.JDBCTransactionFactory
    JDBC 트랜잭션에 트랜잭션 처리를 위임한다.
  • net.sf.hibernate.transaction.JTATransactionFactory
    JTA에 처리를 위임한다. (기존의 트랜잭션이 존재하면 그 트랜잭션을 따르고, 존재하지 않을 경우 Session은 새로운 트랜잭션을 수행한다.
JTATransactionFactory 구현체를 사용할 때, JTATransactionFactory가 어플리케이션 서버로부터 JTA UserTransaction을 구하기 위해 사용할 JNDI 이름은 jta.UserTransaction 프로퍼티를 사용해서 명시할 수 있다.

JTA 환경에서 변경가능한 데이터를 JVM 레벨에서 캐싱하기 위해서는, JTA TransactionManager를 구하기 위한 전력을 명시해야 한다. JTA TransactionManager를 구할 클래스를 명시할 때에는 hibernate.transaction.manager_lookup_class 프로퍼티의 값을 WAS에 따라 알맞게 명시하면 된다. 아래표는 WAS에 따라 사용할 프로퍼티 값 목록이다.

WAS 프로퍼티값
JBoss net.sf.hibernate.transaction.JBossTransactionManagerLookup
Weblogic net.sf.hibernate.transaction.WeblogicTransactionManagerLookup
WebSphere net.sf.hibernate.transaction.WebSphereTransactionManagerLookup
Orion net.sf.hibernate.transaction.OrionTransactionManagerLookup
Resin net.sf.hibernate.transaction.ResinTransactionManagerLookup
JOTM net.sf.hibernate.transaction.JOTMTransactionManagerLookup
JOnAS net.sf.hibernate.transaction.JOnASTransactionManagerLookup
JRun4 net.sf.hibernate.transaction.JRun4TransactionManagerLookup
Borland ES net.sf.hibernate.transaction.BESTransactionManagerLookup

SessionFactory JNDI 등록

hibernate.session_factory_name 프로퍼티를 사용해서 JNDI 이름공간에 SessionFactory를 등록할 수 있다. hibernate.session_factory_name 프로퍼티의 값은 SessionFactory를 JNDI에 등록할 때 사용될 이름이 된다. 예를 들어, java:comp/env/hibernate/SessionFactory와 같은 이름을 사용하게 된다.

hibernate.session_factory_name 프로퍼티를 생략하면 SessionFactory를 JNDI에 등록하지 않는다. Tomcat과 같이 읽기전용 JNDI 구현체를 사용하는 서버에서는 hibernate.session_factory_name 프로퍼티 값을 설정하지 않는 것이 좋다.

hibernate.jndi.url 프로퍼티와 hibernate.jndi.class 프로터리를 사용해서 SessionFactory를 JNDI에 등록할 때 사용할 InitialContext를 명시할 수 있다. 이 속성값을 명시하지 않으면 기본 InitialContext가 사용된다.

다음 글에서는

이번 2회에서는 Hibernate가 사용할 데이터베이스 세션을 설정하는 세 가지 방법, 즉 JNDI를 이용하는 방법, 커넥션 풀을 사용하는 방법, 그리고 직접 코드에서 커넥션을 제공하는 방법에 대해서 살펴보았다. 그리고 DBMS의 기능을 활용할 수 있는 SQL Dialect 설정 방법과 WAS 서버에 따라 달라지는 JNDI 트랜잭션 속성 및 처리 클래스의 설정 방법에 대해서도 알아보았다.

다음 3회 글에서는 OR 매핑의 한 부분인 Object, 즉, Hibernate가 데이터를 메모리 상에서 표현할 자바 퍼시스턴트 클래스의 작성 방법에 대해서 살펴볼 것이다.

관련링크:

+ Recent posts