Hibernate XML 설정 가이드 - 매핑, 캐시, 트랜잭션
Source: Dev.to
기본 설정 (hibernate.cfg.xml)
Hibernate의 핵심 설정 파일입니다. 데이터베이스 연결 정보와 매핑 파일을 지정합니다.
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
<property name="connection.username">system</property>
<property name="connection.password">oracle</property>
<property name="dialect">org.hibernate.dialect.Oracle9Dialect</property>
<property name="hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- 매핑 파일 지정 -->
<!-- <mapping resource="..."/> -->
</session-factory>
</hibernate-configuration>
주요 프로퍼티 설명
| 프로퍼티 | 설명 |
|---|---|
hbm2ddl.auto | 테이블 자동 생성 전략 |
dialect | 데이터베이스별 SQL 방언 |
show_sql | 실행되는 SQL 콘솔 출력 |
format_sql | SQL 포맷팅 |
hbm2ddl.auto 옵션
| 옵션 | 설명 | 사용 환경 |
|---|---|---|
create | 기존 테이블 삭제 후 새로 생성 | 테스트 |
create-drop | 세션 종료 시 테이블 삭제 | 테스트 |
update | 변경사항만 반영 | 개발 |
validate | 매핑 검증만 수행 | 운영 |
none | 아무것도 하지 않음 | 운영 |
운영 환경에서는 validate 또는 none을 사용하세요. update도 위험할 수 있습니다.
컬렉션 매핑
Hibernate는 list, set, bag, map 등의 컬렉션 매핑을 지원합니다.
기본 타입 컬렉션 (List of String)
<list name="nicknames" table="EMP_NICKNAMES">
<key column="EMP_ID"/>
<list-index column="LIST_ORDER"/>
<element column="NICKNAME" type="string"/>
</list>
객체 컬렉션 (List of Entity)
<list name="addresses" cascade="all">
<key column="EMP_ID"/>
<list-index column="LIST_ORDER"/>
<one-to-many class="Address"/>
</list>
컬렉션 타입 비교
| 타입 | 순서 보장 | 중복 허용 | 특징 |
|---|---|---|---|
list | O | O | 인덱스 컬럼 필요 |
set | X | X | 유일한 요소만 |
bag | X | O | 인덱스 없이 순서 무관 |
map | X | X | 키‑값 쌍 저장 |
list 태그 속성
<list name="items" table="ITEMS_TABLE" cascade="all">
<key column="PARENT_ID"/>
<list-index column="IDX"/>
<element column="VALUE" type="string"/>
</list>
Component Mapping
별도 테이블 없이 객체를 컬럼으로 포함합니다.
<composite-element name="address">
<property name="city" column="CITY"/>
<property name="country" column="COUNTRY"/>
<property name="pincode" column="PINCODE"/>
</composite-element>
결과 테이블 구조
| id | name | city | country | pincode |
|---|---|---|---|---|
| 1 | 홍길동 | Seoul | Korea | 12345 |
Component는 별도 테이블이 아닌 부모 테이블의 컬럼으로 저장됩니다.
객체 참조 매핑
Many-to-One
여러 Employee가 하나의 Address를 참조합니다.
<many-to-one name="address" class="Address" column="ADDRESS_ID" fetch="select"/>
One-to-One
Employee와 Address가 1:1 관계입니다.
<one-to-one name="address" class="Address" cascade="all"/>
Cascade 옵션
| 옵션 | 설명 |
|---|---|
none | 기본값, 연관 객체에 영향 없음 |
save-update | 저장/수정 시 연관 객체도 함께 |
delete | 삭제 시 연관 객체도 삭제 |
all | 모든 작업 전파 |
all-delete-orphan | all + 부모에서 제거된 자식 삭제 |
Lazy Loading
연관된 객체를 실제로 사용할 때까지 로딩을 지연합니다.
<class name="Employee" lazy="true">
<!-- 매핑 정의 -->
</class>
Lazy Loading 옵션
| 옵션 | 설명 |
|---|---|
true | 지연 로딩 (기본값, Hibernate 3.0+) |
false | 즉시 로딩 |
extra | 컬렉션 크기 조회 시 COUNT 쿼리만 실행 |
Lazy Loading은 성능 최적화에 중요합니다. 하지만 세션이 닫힌 후 접근하면 LazyInitializationException이 발생합니다.
캐시 (Cache)
Hibernate는 두 가지 레벨의 캐시를 제공합니다.
First Level Cache (1차 캐시)
- 범위:
Session객체 단위 - 기본값: 항상 활성화
- 특징: 같은 세션 내에서 동일 엔티티 재조회 시 DB 접근 없음
Session session = sessionFactory.openSession();
// 첫 번째 조회 - DB에서 가져옴
Employee emp1 = session.get(Employee.class, 1);
// 두 번째 조회 - 1차 캐시에서 가져옴 (DB 접근 없음)
Employee emp2 = session.get(Employee.class, 1);
session.close();
Second Level Cache (2차 캐시)
- 범위:
SessionFactory단위 (전체 애플리케이션) - 기본값: 비활성화
- 특징: 여러 세션에서 공유, 별도 캐시 구현체 필요
설정 방법 (예시)
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
Hibernate 캐시 설정 (XML)
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
엔티티에 캐시 적용
<class name="Employee" cache="read-write">
<!-- 매핑 정의 -->
</class>
캐시 전략
| 전략 | 설명 | 사용 시점 |
|---|---|---|
read-only | 읽기 전용, 수정 불가 | 코드 테이블 등 변경 없는 데이터 |
read-write | 읽기/쓰기 가능 | 일반적인 경우 |
nonstrict-read-write | 약한 일관성 보장 | 동시 수정이 거의 없는 경우 |
transactional | JTA 트랜잭션과 연동 | 분산 환경 |
트랜잭션 관리
데이터 일관성을 위해 트랜잭션을 적절히 관리해야 합니다.
기본 트랜잭션 패턴
Session session = null;
Transaction tx = null;
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
// 비즈니스 로직 수행
Employee emp = new Employee();
emp.setName("홍길동");
session.save(emp);
tx.commit();
} catch (Exception ex) {
if (tx != null) {
tx.rollback();
}
ex.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
Spring에서의 트랜잭션 관리
Spring을 사용하면 @Transactional 어노테이션으로 간편하게 관리할 수 있습니다.
@Service
@Transactional
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
public void createEmployee(Employee emp) {
employeeRepository.save(emp);
// 예외 발생 시 자동 롤백
}
@Transactional(readOnly = true)
public Employee getEmployee(Long id) {
return employeeRepository.findById(id).orElse(null);
}
}
트랜잭션 전파 옵션
| 옵션 | 설명 |
|---|---|
REQUIRED | 기존 트랜잭션 있으면 참여, 없으면 새로 생성 (기본값) |
REQUIRES_NEW | 항상 새 트랜잭션 생성 |
SUPPORTS | 트랜잭션 있으면 참여, 없으면 없이 실행 |
NOT_SUPPORTED | 트랜잭션 없이 실행, 기존 있으면 보류 |
MANDATORY | 반드시 기존 트랜잭션 필요 |
NEVER | 트랜잭션 있으면 예외 발생 |
NESTED | 중첩 트랜잭션 생성 |
어노테이션 vs XML 비교
| 특징 | 어노테이션 | XML |
|---|---|---|
| 가독성 | 코드와 함께 있어 이해 쉬움 | 설정이 분리되어 코드 간결 |
| 수정 시 | 재컴파일 필요 | 재컴파일 불필요 |
| IDE 지원 | 자동완성·검증 우수 | 상대적으로 약함 |
| 유지보수 | 일반적으로 선호 | 레거시 시스템에서 사용 |
현재는 어노테이션 기반 설정이 주류입니다. 새 프로젝트에서는 어노테이션을 사용하세요. XML은 레거시 시스템 유지보수나 설정을 코드와 분리해야 할 때 고려하세요.
결론
Hibernate XML 설정의 핵심을 정리하면:
- hibernate.cfg.xml: 데이터베이스 연결과 Hibernate 동작 설정
- 컬렉션 매핑:
list,set,bag,map등 다양한 컬렉션 표현 - 객체 참조:
many-to-one,one-to-one로 관계 정의 - Lazy Loading: 성능 최적화를 위한 지연 로딩
- 캐시: 1차/2차 캐시로 DB 부하 감소
- 트랜잭션: 데이터 일관성을 위한 트랜잭션 관리
XML 설정을 이해하면 레거시 프로젝트 유지보수에 도움이 되며, Hibernate의 동작 원리를 더 깊이 이해할 수 있습니다.