151

하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

  • View
    340

  • Download
    40

Embed Size (px)

DESCRIPTION

크리스찬 바우어, 개빈 킹 지음 | 박찬욱, 백기선, 이대엽 옮김 | 오픈소스 & 웹 시리즈 _ 021 | ISBN: 9788992939508 | 45,000원 | 2010년 06월 15일 발행 | 960쪽

Citation preview

Page 1: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍
Page 2: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍
Page 3: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

하이버네이트

완벽 가이드

Page 4: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

iv

•첫 번째 판에 대한 찬사•

“하이버네이트 인 액션(Hibernate In Action)은 하이버네이트에 관한 바이블이다. 저자들이

직접 하이버네이트 프로젝트에 관여했기에 그들이 제시한 하이버네이트에 대한 통찰력은 누

구도 쉽게 따라오지 못한다.”

―JavaRanch.com

“하이버네이트를 빠르게 익힐 수 있을뿐만 아니라... 제품 수준의 하이버네이트 애플리케이션

을 올바르게 개발하고 최적화하는 방법도 소개한다. 이런 점에도 불구하고 굉장히 기술적 깊

이가 있고, 놀라울 정도로 쉽게 읽을 수 있으며 ... 근래에 보기 힘든 훌륭한 책이다...”

―JavaLobby.com

“하이버네이트에 관해 가장 먼저 유일하게 모든 내용을 다룬 설명서이자 참고서이며, 권위있

는 지침서이며, 올해 하이버네이트 사용자가 가장 기대했던 책.”

―Dr. Dobb’s 저널

“... 기대 이상의 책이었으며 ... 이 책엔 궁극적인 해법이 담겨있다.”

―Javalobby.org, (2차 검토, 2005년 가을)

“... 다름아닌 선도 개발자나 선도 문서화 담당자에게는 이 책이 바로 하이버네이트를 쓸 때 가

장 훌륭한 소개서이자 참조 문서라는 것이다. 이 책은 매우 단순한 개념에서 시작해서 더 복

잡한 주제로 나아가는 방식으로 구성돼 있으며, 저자들은 적절한 예제를 들어 모든 세부사항

을 잘 설명하고 있다. ... 이 책은 하이버네이트와 제반 기능(문서화가 굉장히 잘 되어 있다)을

빠르게 익힐 수 있게 해줄 뿐 아니라, 제품 수준의 하이버네이트 애플리케이션을 올바르게 개

발하고 최적화하는 방법도 소개한다.”

―Slashdot.org

Page 5: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

v

“강력 추천. 오늘날 최첨단 주제에 관해 매우 잘 설명하고 있으며, 말 그대로 하이버네이트를

만든 사람에게서 직접 듣는 것이기에 특히 그렇다.”

―C Vu, ACCU 저널

“하이버네이트 오픈소스 프로젝트 최고의 지침서. 이 책은 하이버네이트의 아키텍처와 설정,

그리고 하이버네이트를 이용한 개발에 관한 깊이 있는 정보를 전해준다. .. 또한 이 책은 객체/

관계형 매핑(ORM), 영속성, 캐싱, 쿼리에 관한 핵심적인 개념을 설명하고 하이버네이트와 관

련해서 그것들을 어떻게 다뤄야 할지를 기술한다. ...이 책은 하이버네이트를 만든 이가 직접

썼으며, 저자는 하이버네이트를 소개하고 활용하기 위해 이 책에서 최선의 노력을 기울이고

있다. 하이버네이트를 익히려는 모든 이에게 이 책을 추천한다.”

―JavaReference.com

“구입할 만한 가치가 충분히 있다… 온라인 문서도 괜찮지만 (이 책의 저자인 바우어는 온라인

문서화를 담당한 사람 중 한 명이다) 이 책이 더 낫다. 이 책은 여러분이 하고자 하는 것을 설명

하는 것으로 시작해(종종 컴퓨터 관련 서적은 여기서 그친다), 일관된 방법으로 전체 하이버네

이트 시스템으로 이끌어준다. 훌륭한 책이다!”

―Books-on-Line

“간결하고, 해당 주제에 관해 잘 집약돼 있고, 버릴만한 부분이 하나도 없으며, ORM이라는 분

야에 뛰어 들기 위한 모든 이에게 귀중한 자원이다. 이 책의 처음 세 장은 하이버네이트를 활용

하는 애플리케이션을 재빨리 개발하길 원하는 개발자에게도 필수적인 내용이지만, 오히려 하

이버네이트의 개념, 프레임워크, 방법론, 그리고 프레임워크 설계가 이렇게 형성된 이유를 진정

으로 파악하고자 하는 이에게 더욱 중요하다. 나머지 장에서는 계속해서 객체를 매핑하고 영

속화하는 방법, 상속, 트랜잭션, 동시성, 캐싱, HQL을 이용한 효과적인 객체 조회, 관리 및 비

관리 환경에서의 하이버네이트 설정, 그리고 다양한 개발 상황에서 활용할 수 있는 하이버네

이트 도구를 포함해서 하이버네이트에 관해 포괄적으로 기술하고 있다.”

―컬럼비아 자바 사용자 그룹

Page 6: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

vi

•목 차•

1부 하이버네이트와 EJB 3.0 시작하기

1장 객체/관계형 영속화 이해하기 31.1 영속화란 무엇인가? ...................................................................................5

1.1.1 관계형 데이터베이스 .......................................................................5

1.1.2 SQL 이해하기 ...................................................................................6

1.1.3 자바에서 SQL 사용하기 ..................................................................7

1.1.4 객체지향 애플리케이션에서의 영속화 ..........................................8

1.2 패러다임 불일치 ...................................................................................... 10

1.2.1 세밀함의 불일치 문제 ....................................................................12

1.2.2 하위 타입 문제 ................................................................................14

1.2.3 동일성 문제 .....................................................................................15

1.2.4 연관 관계와 관련된 문제 ...............................................................17

1.2.5 데이터 탐색 문제 ............................................................................19

1.2.6 패러다임 불일치에 따르는 비용 ...................................................20

1.3 영속화 계층과 대안 ................................................................................. 21

1.3.1 계층형 아키텍처 .............................................................................22

1.3.2 SQL과 JDBC를 이용해서 직접 작성한 영속화 계층 ...................23

1.3.3 객체 직렬화 사용하기 ....................................................................24

1.3.4 객체지향 데이터베이스 시스템 ....................................................25

1.3.5 그 밖의 대안 ....................................................................................26

1.4 객체/관계형 매핑 .................................................................................... 26

1.4.1 ORM이란 무엇인가? .....................................................................27

1.4.2 일반적인 ORM 문제 ......................................................................29

Page 7: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

vii

1.4.3 왜 ORM인가 ...................................................................................30

1.4.4 하이버네이트, EJB3, JPA 소개 ......................................................33

1.5 정리 .......................................................................................................... 37

2장 프로젝트 시작하기 392.1 하이버네이트 프로젝트 시작하기 ........................................................ 40

2.1.1 개발 프로세스 선택하기 ...............................................................41

2.1.2 프로젝트 구성하기 .........................................................................43

2.1.3 하이버네이트 설정과 시작 ............................................................52

2.1.4 애플리케이션 실행과 확인 ............................................................63

2.2 자바 퍼시스턴스 프로젝트 시작하기 .................................................... 72

2.2.1 하이버네이트 애노테이션 사용하기 ............................................72

2.2.2 하이버네이트 EntityManager 사용하기 ......................................76

2.2.3 EJB 컴포넌트 도입하기 .................................................................85

2.2.4 하이버네이트 인터페이스로 교체하기 ........................................93

2.3 레거시 데이터베이스 역공학하기 ......................................................... 95

2.3.1 데이터베이스 설정 만들기 ............................................................95

2.3.2 역공학 과정 재정의하기 ................................................................97

2.3.3 자바 소스 코드 생성하기 ...............................................................99

2.4 자바 EE 서비스와의 통합 ..................................................................... 104

2.4.1 JTA와의 통합 ................................................................................104

2.4.2 JNDI와 연동된 SessionFactory ..................................................109

2.4.3 JMX 서비스 배포 ..........................................................................111

2.5 정리 ....................................................................................................... 112

3장 도메인 모델과 메타데이터 1153.1 CaveatEmptor 애플리케이션 ............................................................. 116

3.1.1 비즈니스 도메인 분석하기 ..........................................................116

3.1.2 CaveatEmptor 도메인 모델 .........................................................117

Page 8: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

viii

3.2 도메인 모델 구현하기 ........................................................................... 120

3.2.1 관심사 누수 다루기 ......................................................................120

3.2.2 투명하고 자동화된 영속화 ..........................................................121

3.2.3 POJO와 영속 엔티티 클래스 작성하기 ......................................123

3.2.4 POJO 연관 관계 구현하기 ..........................................................126

3.2.5 접근 메서드에 로직 추가하기 ....................................................131

3.3 객체/관계형 매핑 메타데이터 ............................................................. 133

3.3.1 XML 메타데이터 .........................................................................134

3.3.2 애노테이션 기반 메타데이터 .....................................................136

3.3.3 XDoclet 사용하기 ........................................................................143

3.3.4 전역 메타데이터 다루기 .............................................................144

3.3.5 실행 시점에 메타데이터 조작하기 .............................................150

3.4 그 밖의 엔티티 표현 방법 ..................................................................... 152

3.4.1 동적인 애플리케이션 만들기 .....................................................153

3.4.2 XML로 데이터 표현하기 ............................................................161

3.5 정리 ....................................................................................................... 166

2부 매핑 개념과 전략

4장 영속 클래스 매핑하기 1714.1 엔티티와 값 타입 이해하기 .................................................................. 171

4.1.1 구성 단위가 세밀한 도메인 모델 ................................................172

4.1.2 개념 정의 .......................................................................................172

4.1.3 엔티티와 값 타입 식별하기 .........................................................174

4.2 식별자가 있는 엔티티 매핑하기 .......................................................... 175

4.2.1 자바의 동일성과 동등성 이해하기 .............................................175

4.2.2 데이터베이스 동일성 다루기 ......................................................176

4.2.3 데이터베이스 주키 .......................................................................180

Page 9: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

ix

4.3 클래스 매핑 옵션 ................................................................................... 186

4.3.1 동적 SQL 생성 ..............................................................................186

4.3.2 엔티티가 불변성을 띠게 만들기 .................................................187

4.3.3 쿼리에 사용할 엔티티의 이름 부여하기.....................................188

4.3.4 패키지 이름 선언하기 ..................................................................189

4.3.5 SQL 식별자의 인용부호 처리하기 ..............................................189

4.3.6 작명 규약 구현하기 ......................................................................190

4.4 구성 단위가 세밀한 모델과 매핑 ......................................................... 192

4.4.1 기본 프로퍼티 매핑하기 ..............................................................193

4.4.2 컴포넌트 매핑하기 .......................................................................201

4.5 정리 ........................................................................................................ 207

5장 상속과 사용자 정의 타입 2095.1 클래스 상속 매핑하기 ........................................................................... 209

5.1.1 암시적 다형성을 이용한 구상 클래스마다 하나의 테이블 .......210

5.1.2 유니온을 이용한 구상 클래스마다 하나의 테이블 ....................213

5.1.3 클래스 계층 구조마다 하나의 테이블 ........................................217

5.1.4 하위 클래스마다 하나의 테이블 .................................................223

5.1.5 상속 전략 섞어 쓰기 .....................................................................227

5.1.6 매핑 전략 선택 ..............................................................................230

5.2 하이버네이트 타입 시스템 ................................................................... 232

5.2.1 엔티티와 값 객체 요약정리 .........................................................232

5.2.2 내장 매핑 타입 ..............................................................................234

5.2.3 매핑 타입 사용하기 ......................................................................239

5.3 사용자 정의 매핑 타입 만들기 ............................................................. 241

5.3.1 사용자 정의 매핑 타입 고려하기 ................................................242

5.3.2 확장점 ............................................................................................243

5.3.3 사용자 정의 매핑 타입이 필요한 경우 .......................................244

5.3.4 UserType 만들기 ..........................................................................245

Page 10: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

x

5.3.5 CompositeUserType 만들기 .......................................................249

5.3.6 사용자 정의 타입 매개변수화하기 .............................................252

5.3.7 열거형 매핑하기 ...........................................................................255

5.4 정리 ........................................................................................................ 262

6장 컬렉션과 엔티티 연관 관계 매핑하기 2636.1 값 타입의 컬렉션 ................................................................................... 263

6.1.1 컬렉션 인터페이스 선택하기 ......................................................264

6.1.2 Set 매핑하기..................................................................................266

6.1.3 식별자 Bag 매핑하기 ...................................................................267

6.1.4 List 매핑하기 ................................................................................269

6.1.5 Map 매핑하기 ...............................................................................271

6.1.6 정렬 컬렉션과 순차 컬렉션 .........................................................272

6.2 컴포넌트의 컬렉션 ................................................................................ 275

6.2.1 컴포넌트 클래스 작성하기 ..........................................................276

6.2.2 컬렉션 매핑하기 ...........................................................................276

6.2.3 양방향 탐색 활성화하기 ..............................................................277

6.2.4 not-null 열 만들지 않기 ...............................................................278

6.3 애노테이션을 이용해서 컬렉션 매핑하기 .......................................... 280

6.3.1 기본적인 컬렉션 매핑 ..................................................................281

6.3.2 정렬 컬렉션과 순차 컬렉션 .........................................................282

6.3.3 내장 객체의 컬렉션 매핑하기 .....................................................283

6.4 부모/자식 관계 매핑하기 ..................................................................... 285

6.4.1 다중성 ............................................................................................286

6.4.2 가능한 가장 단순한 연관 관계 ....................................................287

6.4.3 연관 관계를 양방향으로 만들기 .................................................289

6.4.4 연쇄적으로 객체 상태 적용하기 .................................................293

6.5 정리 ........................................................................................................ 302

Page 11: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xi

7장 고급 엔티티 연관 관계 매핑 3037.1 단일값 엔티티 연관 관계 ...................................................................... 304

7.1.1 공유 주키 연관 관계 .....................................................................305

7.1.2 일대일 외래키 연관 관계 .............................................................309

7.1.3 조인 테이블을 이용한 매핑 .........................................................312

7.2 다중 값 엔티티 연관 관계 ..................................................................... 317

7.2.1 일대다 연관 관계 ..........................................................................317

7.2.2 다대다 연관 관계 ..........................................................................325

7.2.3 조인 테이블에 열 추가하기 .........................................................331

7.2.4 Map 매핑하기 ...............................................................................340

7.3 다형적인 연관 관계 ............................................................................... 343

7.3.1 다형적 다대일 연관 관계 .............................................................344

7.3.2 다형적 컬렉션 ...............................................................................346

7.3.3 유니온에 대한 다형적 연관 관계 ................................................347

7.3.4 다형적인 구상 클래스마다 하나의 테이블 .................................350

7.4 정리 ........................................................................................................ 353

8장 레거시 데이터베이스와 사용자 정의 SQL 3558.1 레거시 데이터베이스 통합하기 ........................................................... 356

8.1.1 주키 다루기 ...................................................................................357

8.1.2 수식을 이용한 임의 조인 조건 ....................................................371

8.1.3 임의 테이블 조인하기 ..................................................................377

8.1.4 트리거 이용하기 ...........................................................................382

8.2 SQL 직접 재정의하기 ........................................................................... 386

8.2.1 사용자 정의 CRUD 문 작성하기 ................................................388

8.2.2 저장 프로시저와 저장 함수 통합하기 ........................................393

8.3 스키마 DDL 개선하기 .......................................................................... 402

8.3.1 사용자 정의 SQL 이름과 데이터 타입 ........................................403

Page 12: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xii

8.3.2 데이터의 일관성 보장하기 ..........................................................406

8.3.3 도메인 및 열 제약 조건 추가하기 ...............................................407

8.3.4 테이블 수준의 제약 조건 .............................................................409

8.3.5 데이터베이스 제약 조건 ..............................................................413

8.3.6 인덱스 생성하기 ...........................................................................415

8.3.7 보조 DDL 추가하기 .....................................................................416

8.4 정리 ........................................................................................................ 418

3부 대화형 객체 처리

9장 객체를 활용한 개발 4239.1 영속성 생명주기 .................................................................................... 424

9.1.1 객체 상태 .......................................................................................424

9.1.2 영속성 컨텍스트 ...........................................................................428

9.2 객체 동일성과 동등성 ........................................................................... 431

9.2.1 컨버세이션 소개 ...........................................................................431

9.2.2 객체 동일성 범위 ..........................................................................433

9.2.3 준영속 객체의 동일성 ..................................................................435

9.2.4 영속성 컨텍스트 확장하기 ..........................................................441

9.3 하이버네이트 인터페이스 .................................................................... 442

9.3.1 객체 저장하고 불러오기 ..............................................................443

9.3.2 준영속 객체 이용하기 ..................................................................450

9.3.3 영속성 컨텍스트 관리하기 ..........................................................457

9.4 자바 퍼시스턴스 API ............................................................................ 460

9.4.1 객체 저장하고 불러오기 ..............................................................461

9.4.2 준영속 엔티티 인스턴스 이용하기 .............................................466

9.5 EJB 컴포넌트에서 자바 퍼시스턴스 사용하기 ................................... 470

9.5.1 EntityManager 주입하기 .............................................................471

Page 13: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xiii

9.5.2 EntityManager 검색하기 .............................................................473

9.5.3 EntityManagerFactory에 접근하기 ...........................................474

9.6 정리 ........................................................................................................ 476

10장 트랜잭션과 동시성 47910.1 트랜잭션 필수 지식 ............................................................................. 479

10.1.1 데이터베이스와 시스템 트랜잭션 ............................................481

10.1.2 하이버네이트 애플리케이션에서의 트랜잭션 .........................483

10.1.3 자바 퍼시스턴스에서의 트랜잭션 ............................................495

10.2 동시 접근 제어하기 ............................................................................. 500

10.2.1 데이터베이스 수준의 동시성 이해하기 ...................................501

10.2.2 낙관적 동시성 제어 ....................................................................506

10.2.3 그 외 격리성 획득 보장하기 ......................................................513

10.3 트랜잭션 미적용 데이터 접근 ............................................................ 518

10.3.1 자동 커밋 신화 밝혀내기 ...........................................................519

10.3.2 하이버네이트에서 트랜잭션 적용하지 않고 개발하기 ...........521

10.3.3 그 밖의 JTA를 사용하는 트랜잭션 ...........................................522

10.4 정리 ...................................................................................................... 524

11장 컨버세이션 구현하기 52511.1 하이버네이트 세션 전파하기 ............................................................. 526

11.1.1 세션 전파의 쓰임새 ....................................................................526

11.1.2 스레드 로컬로 전파하기 ............................................................528

11.1.3 JTA를 이용한 전파 .....................................................................531

11.1.4 EJB를 사용한 전파 .....................................................................532

11.2 하이버네이트를 이용한 컨버세이션 ................................................. 534

11.2.1 컨버세이션 처리 보장하기 ........................................................534

11.2.2 준영속 객체를 이용하는 컨버세이션 .......................................535

11.2.3 컨버세이션까지 Session 확장하기 ...........................................539

Page 14: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xiv

11.3 JPA를 이용하는 컨버세이션 .............................................................. 547

11.3.1 자바 SE에서 영속성 컨텍스트 전파하기 ..................................548

11.3.2 컨버세이션에서 준영속 객체 병합하기 ...................................550

11.3.3 자바 SE에서 영속성 컨텍스트 확장하기 ..................................552

11.4 EJB 3.0을 이용하는 컨버세이션 ........................................................ 558

11.4.1 EJB를 사용한 컨텍스트 전파 .....................................................559

11.4.2 EJB를 사용한 영속성 컨텍스트 확장 ........................................562

11.5 정리 ...................................................................................................... 569

12장 효율적인 객체 수정 57112.1 영속성 전이 ......................................................................................... 572

12.1.1 접근성을 통한 영속화 ................................................................572

12.1.2 연관 관계에 연쇄 작용 적용하기 ..............................................574

12.1.3 상태 전이 이용하기 ....................................................................578

12.2 벌크와 배치 연산................................................................................. 587

12.2.1 HQL과 JPA QL을 이용하는 벌크 문 ........................................588

12.2.2 배치를 이용해 처리하기 ............................................................592

12.2.3 무상태 Session 사용하기 ...........................................................594

12.3 데이터 필터링과 가로채기 ................................................................. 596

12.3.1 동적 데이터 필터 ........................................................................597

12.3.2 하이버네이트 이벤트 가로채기 ................................................602

12.3.3 핵심 이벤트 시스템 ....................................................................610

12.3.4 엔티티 리스너와 콜백 ................................................................613

12.4 정리 ...................................................................................................... 615

13장 페치와 캐시 최적화 61713.1 전역 페치 계획 정의하기 .................................................................... 617

13.1.1 객체 조회 방법 ............................................................................617

13.1.2 기본적인 지연 페치 계획 ...........................................................621

Page 15: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xv

13.1.3 프록시 이해하기 ...........................................................................622

13.1.4 프록시 생성 비활성화하기 ........................................................625

13.1.5 연관과 컬렉션의 즉시 로딩 .......................................................627

13.1.6 가로채기를 이용한 지연 로딩 ...................................................630

13.2 페치 전략 선택하기 ............................................................................. 632

13.2.1 배치로 데이터 선행 페치하기 ...................................................633

13.2.2 하위 쿼리를 이용한 컬렉션 선행 페치 .....................................636

13.2.3 조인을 이용한 즉시 페치 ...........................................................637

13.2.4 보조 테이블에 대한 페치 최적화하기 ......................................641

13.2.5 최적화 지침 .................................................................................645

13.3 캐싱 기초 ............................................................................................. 653

13.3.1 캐싱 전략과 유효 범위 ...............................................................654

13.3.2 하이버네이트 캐시 아키텍처 ....................................................658

13.4 캐싱 실전 ............................................................................................. 662

13.4.1 동시성 제어 전략 선택하기 .......................................................663

13.4.2 캐시 영역 이해하기 ....................................................................665

13.4.3 로컬 캐시 공급자 설정 ...............................................................666

13.4.4 복제 캐시 설정하기 ....................................................................667

13.4.5 2차 캐시 제어 ..............................................................................672

13.5 정리 ...................................................................................................... 674

14장 HQL과 JPA QL을 이용한 쿼리 67714.1 쿼리 생성과 실행................................................................................. 678

14.1.1 쿼리 준비 .....................................................................................679

14.1.2 쿼리 실행 .....................................................................................690

14.1.3 명명 쿼리 사용하기 ....................................................................694

14.2 기본 HQL과 JPA QL 쿼리 ................................................................. 698

14.2.1 조회 ..............................................................................................698

14.2.2 한정 ..............................................................................................700

14.2.3 프로젝션 ......................................................................................706

Page 16: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xvi

14.3 조인, 리포팅 쿼리, 하위 쿼리 ............................................................. 708

14.3.1 조인 관계와 연관 관계 ...............................................................709

14.3.2 리포팅 쿼리 .................................................................................722

14.3.3 하위 쿼리 사용하기 ....................................................................727

14.4 정리 ...................................................................................................... 730

15장 고급 쿼리 옵션 73115.1 Criteria와 Example을 이용한 쿼리 수행 .......................................... 732

15.1.1 기본 Criteria 쿼리 ......................................................................733

15.1.2 조인과 동적 페치 수행 ...............................................................739

15.1.3 프로젝션과 리포팅 쿼리 ............................................................745

15.1.4 Example을 이용한 쿼리 .............................................................750

15.2 네이티브 SQL 쿼리 사용하기 ............................................................ 753

15.2.1 자동화된 결과 집합 처리 ...........................................................754

15.2.2 스칼라 값 받아오기 ....................................................................755

15.2.3 자바 퍼시스턴스에서의 네이티브 SQL 사용 ...........................757

15.3 컬렉션 필터링 ..................................................................................... 759

15.4 쿼리 결과 캐시 .................................................................................... 762

15.4.1 쿼리 결과 캐시 활성화하기 .......................................................762

15.4.2 쿼리 캐시 이해하기 ....................................................................763

15.4.3 쿼리 캐시는 언제 사용할까? .....................................................764

15.4.4 고유 식별자를 이용한 캐시 룩업 ..............................................765

15.5 정리 ...................................................................................................... 767

16장 계층형 애플리케이션 작성과 테스트 76916.1 웹 애플리케이션에서의 하이버네이트 ............................................. 770

16.1.1 기능 소개 .....................................................................................770

16.1.2 컨트롤러 작성하기 .....................................................................771

16.1.3 Open Session in View 패턴 ......................................................773

Page 17: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xvii

16.1.4 지능적인 도메인 모델 설계하기 ...............................................777

16.2 영속화 계층 만들기 ............................................................................. 780

16.2.1 일반화된 데이터 접근 객체 패턴 ..............................................781

16.2.2 제네릭 CRUD 인터페이스 구현하기 .......................................784

16.2.3 엔티티 DAO 구현하기 ...............................................................787

16.2.4 데이터 접근 객체 사용하기 .......................................................788

16.3 커맨드 패턴 도입하기 ......................................................................... 793

16.3.1 기본 인터페이스 .........................................................................793

16.3.2 커맨드 객체 실행하기 ................................................................795

16.3.3 커맨드 패턴의 변종 ....................................................................798

16.4 EJB 3.0으로 애플리케이션 설계하기 ................................................ 800

16.3.1 상태 유지 빈을 사용해 컨버세이션 구현하기..........................800

16.4.2 EJB로 DAO 작성하기 ................................................................802

16.4.3 의존성 주입 활용하기 ................................................................804

16.5 테스트 .................................................................................................. 806

16.5.1 여러 종류의 테스트 이해하기 ...................................................806

16.5.2 TestNG 도입하기 .......................................................................808

16.5.3 영속화 계층 테스트 ....................................................................812

16.5.4 성능 벤치마크 고려하기 ............................................................822

16.6 정리 ...................................................................................................... 824

17장 제이보스 심 소개 82517.1 자바 EE 5.0 프로그래밍 모델 ............................................................ 826

17.1.1 JSF 살펴보기 ...............................................................................826

17.1.2 EJB 3.0 살펴보기 ........................................................................828

17.1.3 JSF와 EJB 3.0을 이용한 웹 애플리케이션 구축 .......................829

17.1.4 애플리케이션 분석하기 .............................................................841

17.2 심을 이용한 애플리케이션 개선 ........................................................ 844

17.2.1 심 설정하기 .................................................................................845

Page 18: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xviii

17.2.2 페이지와 상태유지 컴포넌트를 연동하기 ...............................847

17.2.3 심 애플리케이션 분석하기 ........................................................853

17.3 컨텍스트 기반 컴포넌트 이해하기 .................................................... 860

17.3.1 로그인 화면 작성하기 ................................................................860

17.3.2 컴포넌트 생성하기 .....................................................................862

17.3.3 컨텍스트 기반 변수의 별칭 만들기 ..........................................865

17.3.4 로그인/로그아웃 기능 완성하기 ...............................................867

17.4 입력 유효성 검증하기 ......................................................................... 871

17.4.1 하이버네이트 검증기 도입하기 ................................................872

17.4.2 등록 페이지 만들기 ....................................................................874

17.4.3 심을 이용한 국제화 ....................................................................883

17.5 심을 이용해서 영속성 처리 단순화하기 ........................................... 886

17.5.1 컨버세이션 구현하기 .................................................................888

17.5.2 심에 영속성 컨텍스트 관리 위임하기 ......................................896

17.6 정리 ...................................................................................................... 901

부록 A SQL 기초............................................................. 903

부록 B 매핑 레퍼런스..................................................... 907

찾아보기............................................................................ 908

Page 19: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xix

박찬욱

종종 하이버네이트를 사용했던 분이나, 현재 사용하고 있는 분을 만나 이야기해보면 하이버네

이트에 대해 긍정적인 반응을 보기가 의외로 쉽지 않습니다. 이유는 다양하겠지만 곰곰이 생각

해보면 그 원인은 '패러다임 변화에 대한 이해 부재'가 아닐까라는 생각이 들었습니다. 이 책에서

소개하는 하이버네이트와 JPA를 단순히 'SQL을 작성하지 않게 해주는 기술', 'DB에 대한 종속

성을 제거해주는 툴' 정도로 이해하고 받아들였기 때문입니다.

이러한 특징은 하이버네이트와 같은 ORM이 주는 '부산물'일 뿐이지 ORM이 달성하려는 '목

적'과는 부합하지 않습니다. ORM을 올바르게 적용하고 이용하려면 이 '패러다임의 변화'에 대해

먼저 이해해야 합니다. 그래야만 ORM이 주는 효과를 온전히 누릴 수 있습니다.

하이버네이트를 사용하는 데 학습 비용이 많이 든다는 의견도 많습니다. 그렇습니다. 배우기

쉽지 않습니다. 그러나 단순히 API 측면의 학습이 아닌, 이러한 변화에 따른 구현 전략에 학습의

초점을 맞춰야 합니다. 학습의 출발점을 정확히 잡으면 비용에 대한 산정 기준도 변하기 마련입

니다. 이 책이 '패러다임의 변화'와 그 전략에 대해 배울 수 있는 가장 현실적인 해답이라 확신합

니다.

책을 읽다 보면 곳곳에서 이런 변화를 설명하는 부분을 쉽게 찾을 수 있을 겁니다. 이제는 단

순히 SQL 작성 자체에 집중하기보다는 패러다임의 변화에 따른 애플리케이션과의 상호작용에

입각한 구현 전략에 집중하는 더욱 유연한 사고 방식이 필요한 때입니다.

물론 하이버네이트가 만능은 아닙니다. 그렇지만 하이버네이트를 유용하게 활용할 수 있는 분

야는 얼마든지 있습니다. Java EE의 표준으로 자리잡은 JPA와 더불어 하이버네이트는 자바 엔

터프라이즈 시스템 개발에 있어 그 역할이 더욱 더 증대될 것입니다.

국내에서도 이 책을 시작으로 기술 선택과 적용을 고민하고 있는 많은 분들이 더 윤택한 삶을

누릴 수 있는 기회가 되면 좋겠습니다.

마지막으로 항상 삶의 가장 큰 행복이 되어주는 내 아내 현주와 이제 세상의 빛을 본 사랑스런

서진이, 언제나 가장 큰 후원자인 부모님과 가족들에게 한결같은 고마움을 전합니다.

•옮긴이 글•

Page 20: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xx

또한 같은 역자로 번역 작업에 있어서 가장 큰 도움을 주신 이대엽 님께 고마움을 전하며, 직접

몇 번이나 찾아와 격려해주신 위키북스 박찬규 사장님, 그리고 책에 대한 조언을 아끼지 않은 많

은 분들에게도 이 자리를 통해 심심한 감사의 말을 전하고 싶습니다.

백기선

우선 자축부터 해야겠습니다. 드디어 이 책 번역을 마쳤습니다. 가장 먼저 이 홀가분한 기분을 함

께 번역한 역자분들과 나누고 싶습니다. 이대엽님 박찬욱님 수고하셨습니다. 오랜 번역 기간에도

불구하고 믿고 맡겨주신 위키북스 박찬규 사장님께도 감사드립니다. 베타리딩에 많은 도움을 주

신 고종봉님을 비롯한 봄싹 스터디에도 고맙다는 인사를 전합니다.

처음 하이버네이트를 학습할 때 생소한 개념과 용어로 인해 매우 당혹스러울 수 있습니다. 제

가 처음에 그랬습니다. 현재 다니고 있는 회사에 처음 입사했을 때부터 현재까지 모든 프로젝트

에서 하이버네이트를 사용했습니다. 다행히도 하이버네이트를 학습하기 위해 처음 손에 잡은 책

이 바로 이 번역서의 원서인 『Java Persistence With Hibernate』였습니다. ORM에 대한 기본적

인 설명과 필수 개념부터 하이버네이트와 JPA의 모든 기능과 옵션을 자세히 설명해주었기 때문

에 많은 궁금증을 해결할 수 있었습니다. 그 당시를 떠올리며 이 책을 학습하시는 분들께 몇 가

지 조언을 드리고자 합니다.

학습 목표를 구체적으로 세우기 바랍니다. 아무런 목표 없이 이 책을 처음부터 끝까지 정독할

수 있을 만큼 시간적 여유와 집중력이 있다면 말리진 않겠습니다. 하지만 그런 경우가 아니라면

이 책을 학습하기 전에 먼저 분명한 목표 세우기를 권장합니다.

하이버네이트를 처음 접하는 분들께는 "하이버네이트를 사용해서 도메인 클래스가 하나인 간

단한 CRUD 애플리케이션을 만들겠다."라는 목표를 권하고 싶습니다. 그 다음에는 "일대다 관계

에 있는 도메인 클래스 두 개를 매핑하고 두 도메인 클래스에 대한 CRUD 애플리케이션을 만들

겠다." 또는 "다형성을 지원하는 계층 구조의 도메인 클래스를 만든 뒤 그것에 대한 CRUD 애플

리케이션을 만들겠다." 등으로 점차 학습 범위를 넓혀갈 수 있겠습니다. 그러면서 자연스럽게 하

이버네이트 매핑과 기본적인 사용법 및 하이버네이트의 주요 개념을 파악할 수 있을 겁니다.

그렇게 하이버네이트를 사용하는 데 어느 정도 자신감이 생기면 하이버네이트가 지원하는

HQL와 Criteria를 사용하는 데 익숙해지기를 권하고 싶습니다. SQL과는 다른 문법과 접근법으

로 DB와 소통할 수 있는 방법입니다. 특히 Criteria는 객체지향 API를 사용하여 SQL과 똑같은

작업을 할 수 있습니다. 동적인 쿼리를 만들 때 그 유용함이 빛을 발하기 때문에 꼭 학습하기를

Page 21: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxi

권장합니다.

하이버네이트를 마스터하고 싶다는 생각을 가지고 계신 분이라면 다시 하이버네이트 기본으

로 돌아가, 하이버네이트가 제공하는 플러시, 페치, 캐시 기능의 다양한 옵션과 동작 원리를 깊

이 있게 공부하길 바랍니다. 이러한 것들을 마스터해야 하이버네이트를 사용한 대화형 요청 처

리를 구현하거나 성능 최적화를 할 수 있을 것입니다.

장황하게 설명했지만 사실 이 책의 구성과 크게 다르지 않습니다. 저 같은 경우에는 가장 먼저

9장에서 기본적인 개념을 살펴본 뒤 3장으로 돌아가 7장까지 매핑 방법을 학습하고 10장부터

12장까지 고급 기능을 학습했습니다. HQL이나 Criteria 사용법이 궁금할 때는 수시로 14, 15장

에서 목차와 소스 코드를 살펴봤습니다. 또한 ORM과 관련된 이론적인 배경 지식이 필요할 때는

1장을 정독했습니다. 당연한 이야기지만 학습 방법은 다 각자 다를 테니 반드시 제가 학습한 방

법을 따라할 필요는 없습니다.

끝으로, 사랑하는 나의 아내 이유진양의 환상적인 내조가 없었다면 마음 편히 번역에 매진할

수 없었을 것입니다. 이 자리를 빌어 항상 고맙고 매우 많이 사랑한다고 전하고 싶습니다.

이대엽

오랜 기간 동안 번역해온 『하이버네이트 완벽 가이드』가 드디어 세상의 빛을 보게 됐습니다. 우

선 함께 번역하느라 고생하신 박찬욱, 백기선 님께 감사드립니다. 용어 선정 과정도 쉽지 않았고

문장 하나하나를 옮길 때마다 저자의 의도가 충분히 반영됐는지 고민하느라 힘든 시간을 보냈

습니다만 이 책을 읽고 하이버네이트를 올바르게 현장에 적용하는 개발자가 늘어나는 것만으로

도 충분히 보람을 느낄 수 있으리라 생각합니다.

저는 이 책을 번역하면서 많은 내용을 소화하고 공부해야 했습니다. ORM이라는 생소한 개념

과 동작원리, 그리고 하이버네이트의 API 구성 등으로 처음 접했을 때 가파른 학습곡선을 거쳐

야 했습니다. 단순히 SQL 쿼리를 던져 처리할 수 있는 일을 하는데도 별도의 매핑이나 설정 과정

을 거쳐야 하기에 처음 접하는 분이라면 되려 거부감을 느끼실지도 모르겠습니다.

하지만 더 넓은 관점에서 보면 이런 학습 비용은 충분히 감내할 만한 가치가 있습니다. 오늘

날 거의 모든 기업에서 쓰는 관계형 데이터베이스와 엔터프라이즈 시장에서 위세를 떨치고 있는

자바 사이의 간극을 메워줄 수 있는 실용적인 대안은 현재 ORM이 유일합니다. 그리고 그러한

ORM 도구 가운데 오픈소스이면서 ORM이 반드시 제공해야 할 기능을 충실히 지원하고 있는

하이버네이트는 엔터프라이즈 시장에서 끊임없이 더 나은 개발 방법을 추구하는 자바 개발자라

Page 22: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxii

면 반드시 알고 있어야 한다고 생각합니다.

그런 점에서 이 책의 원서인 『Java Persistence With Hibernate』는 하이버네이트 프로젝트를

만든 장본인이 직접 쓴 책이기에 다른 어느 책에서도 볼 수 없는 내용을 담고 있습니다. 책 분량

이 만만치 않은 만큼 ORM을 비롯해서 하이버네이트, 자바 퍼시스턴스 표준에 대해 완벽하게 설

명하고 있습니다. 아울러 처음 ORM에 입문한 개발자라도 이 분야와 관련된 핵심 주제에 대해

폭넓고 깊이 있게 이해할 수 있을 겁니다.

처음 이 책을 번역해야 할지 많이 고민했습니다. 단순히 책을 읽는 거라면 쉽게 읽고 이해하고

넘어갈 수 있겠지만 원문을 우리말로, 그것도 가장 적확한 표현으로 옮긴다는 것은 결코 쉬운 일

이 아니기 때문입니다. 하지만 국내에 하이버네이트를 제대로 다룬 책이 없다는 점과 국내에 하

이버네이트를 소개하는 데 일조할 수 있으리라는 생각에서 결국 번역을 시작하게 되었습니다.

이 책이 나오기까지 많은 분께서 도와주셨습니다. 원고를 읽고 세심하게 지적해주신 손권남

님, 바쁘신 와중에도 베타리딩해주신 고종봉 님, 일정이 지연되는데도 너그러이 이해해주시고

아낌없는 지원을 해주신 위키북스 사장님과 팀장님께 감사드립니다.

끝으로 현장에서 ORM 기술을 어떻게 적용할지 고민하고 언제나 더 나은 개발 환경을 만드느

라 고군분투하시는 개발자분들께 미약하나마 이 책이 도움됐으면 좋겠습니다.

Page 23: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxiii

•개정판에 대한 추천의 글•

2년 전 <하이버네이트 인 액션>이 출간됐을 때 그 책은 곧바로 하이버네이트에 관한 결정판으로

여겨졌을 뿐 아니라 객체/관계형 매핑에 관한 결정판으로도 여겨졌다.

2년이라는 시간이 흘러가는 동안, 영속화라는 풍경은 엔터프라이즈 자바빈 3.0 명세

(Enterprise JavaBean 3.0 Speci�cation)의 일환으로 자바 커뮤니티 프로세스(Java Community

Process) 하에서 개발된 새로운 자바 EE 및 자바 SE용 객체/관계형 매핑 표준인 자바 퍼시스턴

스(Java Persistence) API가 출시되면서 바뀌었다.

자바 퍼시스턴스 API로 개발함에 있어 EJB 3.0 전문가 그룹은 이미 자바 커뮤니티에서 사용

되고 있는 O/R 매핑 프레임워크에서 얻은 경험을 상당수 활용할 수 있었다. 이러한 O/R 매핑 프

레임워크 가운데 선도적인 위치에 있는 하이버네이트는 자바 퍼시스턴스의 기술적인 방향성에

매우 중대한 영향을 주었다. 이는 개빈 킹(Gavin King)을 비롯한 다른 여러 하이버네이트 팀이

EJB 3.0의 표준화에 참여한 것도 있지만, 상당 부분은 O/R 매핑으로 나아가기 위해 하이버네이

트에서 취한 직접적이고도 실용적인 접근법과 단순함, 명확함, 하이버네이트 API의 강력함, 그리

고 결과적으로 이러한 것들이 자바 커뮤니티에 받아들여졌다는 사실에 기인한다.

저자들이 자바 퍼시스턴스에 기여한 것과 더불어 하이버네이트 개발자들은 이 책에서 설명하

는 하이버네이트 및 하이버네이트 3가 출시되는 데 크게 기여하기도 했다. 그들이 기여한 바 중

에는 많은 양의 데이터를 처리하기 위한 연산을 지원하는 것에서부터 추가적인 더 정교한 매핑

옵션(특히 레거시 데이터베이스를 다루기 위한), 데이터 필터, 데이터 변환 관리 전략, 심(Seam)

과의 통합, JSF와 EJB 3.0을 이용한 새로운 웹 애플리케이션 개발 프레임워크가 있다.

따라서 하이버네이트 완벽 가이드(Java Persistence with Hibernate)는 하이버네이트 인 액션

의 두 번째 판 그 이상이다. 이 책은 하이버네이트 3과 더불어 자바 퍼시스턴스 API에서 제공하

는 모든 기능을 포괄적으로 제시하고, 자바 퍼시스턴스 API와 하이버네이트 3에 관해서도 상세

히 비교 분석한다. 또한 이 책은 자바 퍼시스턴스 API를 구현하는 데 하이버네이트가 어떻게 쓰

였는가와 자바 퍼시스턴스에 대한 하이버네이트 확장을 활용하는 방법도 기술하고 있다.

Page 24: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxiv

더 중요한 건 하이버네이트와 자바 퍼시스턴스를 소개하는 내내 크리스찬 바우어와 개빈 킹은

객체/관계형 매핑 프레임워크를 설계하고 사용할 때 고려해야 할 근본적인 원칙과 설계 결정을

보여주고 설명해준다는 것이다. ORM의 기저에 놓인 쟁점에 대해 저자가 전해주는 통찰력은 이

책을 읽는 이에게 엔터프라이즈 기술의 하나로서 효과적인 ORM 애플리케이션을 깊이 있게 이

해할 수 있게 도와준다.

이처럼 하이버네이트 완벽 가이드는 자바 커뮤니티에서 최첨단을 달리고 있는 기술적 혁신의

산물인 하이버네이트에 관해 더욱 많이 배우기 위해 노력하는 다양한 개발자(객체/관계형 매핑

에 입문한 이에서 숙련된 개발자에 이르기까지)에게 다가서고 있다.

린다 드미쉘

명세 선도

엔터프라이즈 자바빈 3.0 및 자바 퍼시스턴스

썬 마이크로시스템즈

Page 25: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxv

•첫 번째 판에 대한 추천의 글•

관계형 데이터베이스는 명백히 현대 엔터프라이즈의 핵심이다.

자바를 포함해서 현대 프로그래밍 언어가 애플리케이션 차원의 비즈니스 엔티티에 대해 직관

적이고도 객체지향적인 관점을 제공하는 데 반해, 이러한 비즈니스 엔티티의 기저에 놓인 엔터프

라이즈 데이터는 굉장히 관계적인 면모를 지닌다. 뿐만 아니라 초기의 탐색적 모델, 그리고 이후

의 OODB 모델에 비해 관계형 모델의 주된 강점은 바로 설계상 다뤄야 할 데이터에 대한 프로그

램 방식의 조작과 애플리케이션 차원의 관점이 본질적으로 불가지론적이라는 데 있다.

그동안 관계형 기술과 객체지향 기술 사이에 가교를 놓거나, 하나가 다른 하나를 대체하기 위

한 수많은 시도가 있었지만 두 기술 사이에 놓인 틈은 오늘날 엔터프라이즈 컴퓨팅에서 처리하

기 힘든 부분에 속한다. 그것은 관계형 데이터와 자바 객체 사이에 다리를 놓는 것이며, 이것이

바로 하이버네이트가 객체/관계형 매핑(ORM)이라는 접근법을 통해 해결하려는 문제이기도 하

다. 하이버네이트는 이 문제에 매우 실용적이고 직접적이며 현실적인 방식으로 대처한다.

크리스찬 바우어와 개빈 킹이 이 책에서 보여주고 있듯 매우 간단한 엔터프라이즈 환경을 제외

하고 ORM 기술을 효과적으로 사용하려면 관계형 데이터와 객체간의 매개체가 어떻게 수행될

지에 관한 이해와 구성이 필요하다. 이는 개발자가 애플리케이션과 데이터베이스 요구사항, SQL

쿼리 언어, 관계형 저장소 구조, 관계형 기술이 제공하는 최적화 가능성 모두를 잘 알고 있어야

한다는 뜻이다.

하이버네이트는 이러한 요구사항에 부합하는 모든 기능을 갖춘 해법과 유연하고 구성 가

능한 아키텍처를 제공한다. 하이버네이트 개발자들은 모듈성(modularity), 접합가능성

(pluggability), 확장성(extensibility), 사용자 맞춤(user customization)을 염두에 두고 하이버네

이트를 설계했다. 그 결과 초기 배포판이 나온 지 몇 년 만에 하이버네이트는 빠르게 엔터프라이

즈 개발자들을 위한 선도 ORM 기술이 되었으며, 또 그럴만한 자격이 충분하다.

이 책은 하이버네이트에 관해 포괄적으로 소개한다. 또한 이 책은 하이버네이트의 연관 관계

및 상속을 모델링하기 위한 타입 매핑 기능 및 설비를 활용하는 방법과 하이버네이트 쿼리 언어

를 이용해서 효과적으로 객체를 가져오는 법, 관리 및 비관리 환경에서 하이버네이트를 사용하

Page 26: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxvi

기 위한 설정 방법, 그리고 하이버네이트에서 제공하는 도구를 사용하는 방법도 다룬다. 또한 이

책 전반에 걸쳐 저자들은 ORM의 기저에 놓여있는 쟁점과 하이버네이트 이면에 놓인 설계 결정

에 관한 통찰력도 제시한다. 이러한 통찰력은 이 책을 읽는 이들에게 엔터프라이즈 기술의 하나

로서 효과적인 ORM 애플리케이션을 깊이 있게 이해할 수 있게 도와준다.

하이버네이트 인 액션은 하이버네이트 사용과 오늘날 엔터프라이즈 컴퓨팅에서의 객체/관계

형 매핑에 대한 완벽한 지침서다.

린다 드미쉘

엔터프라이즈 자바빈의 선도 아키텍트

썬 마이크로시스템즈

Page 27: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxvii

•개정판에 대한 서문•

이 책보다 먼저 나온 <하이버네이트 인 액션>은 앤소니 버글라스(Anthony Berglas)가 말한 “잔

가지를 부지런히 땅에 쑤셔 넣을 수 있다고 해서 그게 장작을 모으는 가장 좋은 방법이라는 뜻은

아니다.”를 인용하는 것으로 시작했다. 그 후로 하이버네이트 프로젝트와 소프트웨어 개발자들

이 정보를 관리하는 데 이용하는 전략이나 개념은 진화했다. 하지만 근본적인 문제는 그대로 남

아있다. 우리가 매일 출근하는 모든 회사에서는 아직도 SQL 데이터베이스를 사용하고 있고, 자

바는 엔터프라이즈 애플리케이션을 개발하는 데 가장 먼저 택하는 플랫폼으로서 업계에서 자신

의 입지를 굳혔다.

관계형 시스템에서 테이블 형태로 데이터를 표현하는 건 여전히 객체지향 자바 애플리케이션

에서 사용하는 객체망과 근본적으로 다르다. 하지만 아직까지도 객체/관계형 패러다임 불일치와

이러한 불일치의 중요성과 비용이 과소평가되는 광경을 자주 본다.

한편, 이제는 이 같은 문제를 다루는 데 활용할 수 있는 다양한 도구와 솔루션을 사용할 수 있

게 되었다. 장작 모으기는 이제 끝났고, 라이터는 화염 방사기로 대체됐다.

하이버네이트는 이제 세 번째 주요 배포판을 내놓았다. 이 책은 하이버네이트 3.2를 기준으로

한다. 이전 버전의 하이버네이트와 비교해 봤을 때 이번 새 주요 배포판은 기능이 두 배로 많아졌

고, 덩달아 이 책도 하이버네이트 인 액션에 비해 분량이 거의 두 배로 늘어났다. 이러한 기능은

대부분 독자처럼 매일 하이버네이트를 사용하는 개발자들이 요청했던 것이다. 이따금 우리는 데

이터베이스 애플리케이션을 작성할 때 하이버네이트가 자바 애플리케이션 개발자들이 다뤄야

할 모든 문제의 90% 정도의 솔루션이라고 말하곤 했다. 그렇지만 최신 버전의 하이버네이트를

사용한다면 이 수치는 99%에 가까울 것이다.

하이버네이트가 성숙해지고 사용자 기반 및 커뮤니티의 규모가 커지면서 데이터 관리 및 데이

터베이스 애플리케이션 개발을 위한 자바 표준이 없음을 알게 되었다. 심지어 우리는 하이버네

이트 인 액션에서 EJB 2.x 엔티티 빈을 사용하지 말라고 언급하기도 했다.

이제 EJB 3.0과 새로운 자바 퍼시스턴스(Java Persistence) 표준으로 나아가자. 이들 새 산업

표준은 자바 개발자 커뮤니티에서 괄목할만한 성장을 이뤘다. 이것들은 경량이면서도 단순화한

Page 28: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxviii

프로그래밍 모델과 강력한 객체/관계형 영속화를 정의하고 있다. 새로운 표준에 포함된 핵심적

인 개념 중 다수는 하이버네이트와 그 밖의 성공적인 객체/관계형 영속화 솔루션에 따라 설계되

었다. 또한 최신 버전의 하이버네이트는 자바 퍼시스턴스 표준을 구현하고 있다.

온갖 용도로 활용 가능한 새로운 일체형(all-in-one) 하이버네이트와 더불어 이제 독자는 EJB

3.0 컴포넌트와 자바 EE 5.0 서비스가 있든 없든 하이버네이트를 여느 자바 퍼시스턴스 제공자처

럼 사용할 수 있다. 이처럼 하이버네이트를 풍부한 프로그래밍 모델과 근본적으로 통합할 수 있

다는 사실은 종전에는 직접 만들기 어려웠던 애플리케이션 기능을 설계하고 구현하는 일을 가

능하게 해준다.

이 책은 하이버네이트와 자바 퍼시스턴스(그리고 이와 관련된 모든 EJB 3.0 개념도)에 관한 완

전하고도 정확한 지침을 제시할 목적으로 쓴 것이다. 우리는 독자가 하이버네이트를 배우는 과

정을 즐기고 일상 업무를 할 때 이 바이블을 책상에 올려두고 언제든지 활용하길 바란다.

Page 29: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxix

•첫 번째 판에 대한 서문•

“잔가지를 부지런히 땅에 쑤셔 넣을 수 있다고 해서 그게 장작

을 모으는 가장 좋은 방법이라는 뜻은 아니다.”

―앤소니 버글라스

오늘날 수많은 소프트웨어 개발자가 기업 정보 시스템(EIS, Enterprise Information System)과

관련된 업무를 한다. 이러한 애플리케이션은 구조적인 정보를 만들어내고 관리하고 저장하며,

이러한 정보를 다수의 물리적인 위치에 존재하는 여러 사용자 사이에서 공유한다.

EIS 데이터를 저장하는 일은 굉장히 많은 SQL 기반 데이터베이스 관리 시스템을 수반한다. 우

리가 근무했던 회사에는 모두 SQL 데이터베이스가 최소한 하나씩 있었다. 대부분 핵심적인 비즈

니스는 관계형 데이터베이스 기술에 전적으로 의존한다.

지난 5년 동안 자바 프로그래밍 언어가 널리 쓰이면서 소프트웨어 개발에 있어 객체지향 패러

다임이 우세해졌다. 이제 개발자들은 객체지향이 가져다 주는 혜택에 매료되었다. 하지만 대다

수의 기업은 값비싼 관계형 데이터베이스 시스템에 대한 장기적으로 투자에 의존하고 있다. 특정

벤더의 제품이 입지를 굳히고 있는가 하면, 이미 존재하는 레거시 데이터를 새로운 객체지향 웹

애플리케이션에서도 사용할 수 있어야 한다.

관계형 시스템에서 테이블 형태로 데이터를 표현하는 것은 여전히 객체 지향 자바 애플리케이

션에서 사용하는 객체망과 근본적으로 다르다. 이러한 차이는 소위 객체/관계형 패러다임 불일

치를 낳았다.

전통적으로 이러한 불일치의 중요성과 비용은 과소평가됐으며, 불일치 해소를 위한 도구는 그

와 같은 불일치를 해소하기에는 역부족이었다. 그와 동시에 자바 개발자들은 이런 불일치를 관

계형 기술의 탓으로 돌린다. 반대로 데이터 전문가들은 객체지향 기술의 탓으로 돌린다.

객체/관계형 매핑(ORM)이라는 이름은 불일치 문제에 대한 자동화된 솔루션을 가리킬 때 쓴

다. 지겨운 데이터 접근 코드를 작성하는 데 싫증난 개발자에게 좋은 소식은 ORM이 전성기를

Page 30: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxx

구가하고 있다는 것이다. ORM 미들웨어를 통해 구축한 애플리케이션은 더 저렴하면서도 더욱

성능이 좋은, 그리고 데이터베이스 제공자에 덜 종속적이고 내부에 존재하는 객체나 기반 SQL

스키마의 변경에도 대처할 것으로 기대할 수 있다. 놀라운 점은 자바 개발자는 별다른 노력을 들

이지 않고도 이런 혜택을 누릴 수 있다는 사실이다.

개빈 킹은 2001년 후반 당시 인기 있는 영속화 솔루션인 CMP 엔티티 빈(CMP Entity Bean)이

데이터 모델이 복잡한 제법 규모가 있는 애플리케이션까지는 확장할 수 없음을 알고 난 후 하이

버네이트를 개발하기 시작했다. 그때부터 하이버네이트는 독립적이고 비상업적인 오픈 소스 프

로젝트로의 첫걸음을 내딛었다.

하이버네이트 팀(저자 포함)이 ORM을 배우는 건 매우 힘겨운 과정이었다. 즉, 사용자가 요청

하는 사항을 들어보고 그런 요청을 충족시키는 데 필요한 것을 구현했으니 말이다. 그렇지만 그

결과로 만들어진 하이버네이트는 개발자의 생산성과 기술적 주도를 강조하는 실용적인 솔루션

이 되었다. 현재 하이버네이트는 수만 명의 사용자가 사용하고, 수천 개에 달하는 제품 애플리케

이션에서 사용하고 있다.

하이버네이트에 쏟아야 할 시간을 주체할 수 없을 정도가 되었을 때, 하이버네이트 팀은 프로

젝트의 향후 성공(그리고 개빈 킹이 정상적인 생활을 계속할 수 있으려면)을 위해서는 하이버

네이트에 전념할 전문 개발자가 있어야 한다고 결론 내렸다. 하이버네이트는 2003년 후반 jboss.

org에 합류했고, 이제 상업적인 측면도 지니고 있다. 따라서 독자는 JBoss Inc.에 비용을 지불하

고 유료 지원 및 교육을 받을 수 있다. 하지만 하이버네이트를 배우는 데 유료로 교육을 받는 방

법밖에 없는 건 아니다.

많은, 아니 아마 대부분의 자바 프로젝트는 하이버네이트와 같은 ORM 솔루션을 사용하면

분명 혜택을 얻을 것이다. 비록 2년전에는 사실이 아니었지만 말이다! ORM 기술이 점차 주류로

자리를 잡으면서 하이버네이트의 무료 사용자 설명서와 같은 제품 문서로는 더는 충분하지 않았

다. 우리는 하이버네이트 커뮤니티와 신규 하이버네이트 사용자가 단순히 하이버네이트를 이용

해서 소프트웨어를 개발하는 것에 관한 것뿐만 아니라, 객체/관계형 불일치와 하이버네이트의

설계 이면에 놓인 철학에 관해서도 올바르게 인식하고 이해하기 위한 내용을 전부 소개하는 책

이 필요하다는 사실을 깨닫게 되었다.

독자가 손에 들고 있는 이 책은 우리가 1년 이상 대부분의 자투리 시간을 쏟아부은 어마어마

한 노력의 산물이다. 또한 이 책은 수많은 열띤 논쟁과 학습 경험의 원천이기도 하다. 우리는 이

책이 하이버네이트에 대한 훌륭한 지침서(아니면, 어떤 감수자분이 말씀하셨던 “하이버네이트

Page 31: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxi

바이블”으로서)이길 바라며, 가장 먼저 객체/관계형 불일치와 ORM에 관해 포괄적으로 다룬 문

서이길 바란다. 마지막으로 이 책이 도움이 되고 하이버네이트도 즐겁게 쓰길 바란다.

Page 32: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxii

•감사의 글•

이 책은 <하이버네이트 인 액션>의 2차 개정판으로 시작해서 상당한 분량으로 늘어났다. 많은

분의 도움이 없었다면 이 책은 세상에 나오지 않았을 것이다.

Emmanuel Bernard는 이 책의 기술적인 감수자로서 정말 훌륭한 일을 해주었다. 많은 시간

을 들여 잘못된 코드 예제를 수정해 주신 것에 감사한다. 그리고 이 책을 검토해 주신 Patrick

Dennis, Jon Skeet, Awais Bajwa, Dan Dobrin, Deiveehan Nallazhagappan, Ryan Daigle,

Stuart Caborn, Patrick Peak, TVS Murthy, Bill Fly, David Walend, Dave Dribin, Anjan

Bacchu, Gary Udstrand, Srinivas Nallapati에게 감사드린다. 특히 이 책을 비롯해 첫 번째 판의

추천사도 써준 린다 드미쉘에게 감사드린다.

Marijan Bace는 다시 한번 매닝 출판사에서 출판 팀을 뭉치게 해주었다. Sydney Jones는 형

편없는 원고를 편집해서 실제 책으로 탈바꿈시켜 주었다. Ti�any Taylor와 Elizabeth Martin,

Andy Carroll은 이 책의 모든 오탈자를 수정해서 읽을 수 있는 수준으로 만들어 주었다. Dottie

Marsico는 조판을 책임지고 이 책이 멋진 모양새를 갖추게 해주었다. Mary Piergies는 출판 과

정을 조율하고 조정해 주었다. 우리와 함께 일해 준 데 대해 감사드린다.

Page 33: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxiii

•이 책에 관해•

우리는 이 책을 세 가지 목표를 두고 썼으며, 이 책을 아래에 나열한 것으로 생각하고 읽어 주었

으면 한다.

하이버네이트와 자바 퍼시스턴스, EJB 3.0의 첫 단추를 꿰맬 수 있게 도와주는 자습서 ▒

객체/관계형 매핑과 객체 처리, 쿼리, 성능 최적화, 애플리케이션 설계를 위해 하이버네이 ▒

트가 제공하는 모든 기본적인 기능 및 고급 기능에 대한 지침서

하이버네이트 및 자바 퍼시스턴스 기능에 대해 완전하고도 기술적으로 정확한 정의가 필 ▒

요할 때마다 참고할 수 있는 참고서

보통 책은 자습서나 참조 지침서 중 하나로 내용을 구성하기 때문에 이 책처럼 책의 구성이 확

장되면 미리 감안해야 할 부분이 있다. 하이버네이트를 처음 접한다면 가장 먼저 자습서 성격을

띠고 있는 1장과 2장의 내용을 읽는 것으로 시작하길 권장한다. 또는 이전 버전의 하이버네이트

를 써본 적이 있다면 처음 두 장은 빨리 읽고 전체적인 그림을 잡은 다음 3장 중반부로 넘어가도

된다.

그리고 특정 절이나 주제가 필수적으로 짚고 넘어가야 하는 것이 아니거나, 참고 자료 정도에

그치는 경우라면 처음 읽는 경우에는 그냥 넘어가도 무방하다고 알려주겠다.

로드맵

이 책은 크게 세 부분으로 나뉜다.

1부에서는 객체/관계형 패러다임 불일치를 소개하고 객체/관계형 매핑 이면에 놓인 기본적인

사항을 설명한다. 그리고 실습 가능한 자습서를 통해 첫 번째 하이버네이트 및 자바 퍼시스턴스,

EJB 3.0 프로젝트를 시작한다. 또한 도메인 모델을 구성하기 위한 자바 애플리케이션 설계와 객

체/관계형 매핑 메타데이터를 생성하기 위한 옵션을 살펴본다.

Page 34: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxiv

2부에서는 자바 클래스와 프로퍼티를 SQL 테이블과 열로 매핑하는 내용을 중점적으로 다룬

다. 2부에서는 하이버네이트와 자바 퍼시스턴스의 모든 기본적인 매핑 옵션과 고급 매핑 옵션을

XML 매핑 파일과 자바 애노테이션으로 설정하는 방법을 살펴본다. 또한 상속, 컬렉션, 복잡한

클래스 연관 관계를 다루는 방법에 관해서도 보여준다. 마지막으로 레거시 데이터베이스 스키마

와의 통합과 특별히 사용하기 까다로운 몇몇 매핑 전략에 관해서도 논의한다.

3부에서는 객체 처리를 비롯해 하이버네이트와 자바 퍼시스턴스를 이용해서 데이터를 불러오

고 저장하는 방법에 관한 내용을 다룬다. 3부에서는 프로그래밍 인터페이스와 트랜잭션 처리가

가능하고 대화형 애플리케이션을 작성하는 방법과 쿼리를 작성하는 방법을 소개한다. 그 후에

는 계층적인 자바 애플리케이션을 올바르게 설계 및 구현하는 방법에 초점을 맞춘다. 또한 데이

터 접근 객체(DAO, Data Access Object) 및 EJB의 명령(Command) 패턴과 같이 하이버네이트

에서 가장 흔히 사용되는 디자인 패턴에 관해서도 논의한다. 또한 하이버네이트 애플리케이션을

손쉽게 테스트할 수 있는 방법과 객체/관계형 매핑 소프트웨어를 사용하고 있다면 이와 관련한

그 밖의 우수 사례(best practice)도 확인할 수 있다.

마지막으로는 제이보스 심(JBoss Seam) 프레임워크를 소개한다. 제이보스 심 프레임워크는 하

이버네이트가 지닌 여러 개념을 한 단계 더 끌어올려 손쉽게 대화형 웹 애플리케이션을 작성할

수 있게 해준다. 굳이 심을 사용할 계획이 없더라도 마지막 장도 재미있게 읽을 수 있을 것이다.

대상 독자

이 책의 독자는 기본적인 객체지향 소프트웨어 개발에 관한 지식을 갖추고 이를 실무에 적

용해 본 적이 있을 것이다. 애플리케이션 예제를 이해하기 위해서는 자바 프로그래밍 언어와

UML(Uni�ed Modeling Language)에 익숙해야 한다.

주요 대상 독자층은 SQL 기반의 데이터베이스 시스템을 이용해서 업무를 하는 자바 개발자로

구성돼 있다. 이 책에서는 어떻게 여러분이 ORM을 활용해서 생산성을 극적으로 향상시키는지

보여주겠다.

여러분이 데이터베이스 개발자라면 이 책은 객체지향 소프트웨어 개발에 관한 소개 자료로

활용할 수도 있다.

데이터베이스 관리자라면 ORM이 성능에 얼마나 영향을 주고 성능 목표치를 달성하기 위해

SQL 데이터베이스 관리 시스템과 영속화 계층의 성능을 어떻게 조정할 수 있는지에 관심이 있

을 것이다. 대부분의 자바 애플리케이션에서는 데이터에 접근하는 부분이 병목지점이므로 이 책

Page 35: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxv

에서는 성능 문제에 세심한 주의를 기울이고 있다. 수많은 DBA가 도구가 생성하는 SQL 코드에

성능을 맡기길 꺼리는 것은 당연하며, 우리는 그러한 두려움을 완화시키고 애플리케이션에서 도

구로 관리하는 데이터 접근을 쓰지 말아야 하는 경우를 밝히는 데도 노력을 기울이고 있다. 여러

분은 우리가 ORM이 모든 문제에 대한 최적의 해법이라고 주장하지는 않는다는 사실을 안다면

좀 더 안심이 될지도 모르겠다.

코드 표기

이 책은 풍부한 예제를 제공하며, 여기엔 하이버네이트 애플리케이션을 구성하는 자바 코드나

하이버네이트 설정 파일, XML 매핑 메타데이터 파일이 모두 포함된다. 예제나 본문에서 제시한

소스 코드는 고정폭 글꼴(�xed-width font)로 다른 본문과 구분했다. 더불어 본문에서 제시하

는 자바 메서드명, 컴포넌트 매개변수, 객체 프로퍼티, XML 요소 및 속성도 고정폭 글꼴로 표현

했다.

자바, HTML, XML은 모두 책에서 설명하는 핵심적인 내용과는 무관한 내용으로 가득 찰 수

가 있다. 많은 경우 원본 소스 코드(온라인에서 내려받을 수 있다)를 다시 정리해서 책에 수록했

다. 즉, 소스 코드에 줄바꿈을 하고 들여쓰기 작업을 다시 해서 지면에 맞게 조정했다는 뜻이다.

간혹 이렇게 하는 것도 모자란 경우에는 예제에 줄이어짐 표시를 집어 넣었다. 또한 소스 코드의

주석은 본문에서 코드를 설명할 경우 예제에서 제거했다.

어떤 소스 코드 예제에는 주석을 달아 중요한 개념을 강조했다. 또 어떤 경우에는 번호를 매긴

목록으로 예제에 대한 설명을 덧붙였다.

소스 코드 내려받기

하이버네이트는 LGPL로 배포되는 오픈소스 프로젝트다. 하이버네이트 패키지는 www.

hibernate.org/ 에서 소스나 바이너리 형태로 내려받을 수 있다.

이 책에서 제시하는 Hello World와 CaveatEmptor 예제의 소스 코드는 http://caveatemptor.

hibernate.org/에서 자유 (BSD와 유사한) 라이선스로 내려받을 수 있다. CaveatEmptor 예제

애플리케이션의 코드는 각자 기호에 맞게(이를테면, 네이티브 하이버네이트나 자바 퍼시스턴

스, 제이보스 심에 맞춰) 해당 웹 사이트에서 내려받을 수 있다. 또한 출판사 웹 사이트인 www.

manning.com/bauer2에서도 이 책의 예제에 대한 소스 코드를 내려받을 수 있다.

Page 36: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxvi

저자 약력

크리스찬 바우어는 하이버네이트 개발 팀의 일원이다. 바우어는 레드햇(Red Hat)의 제이보스 부

서에서 근무하고 있으며 하이버네이트, EJB 3.0, 제이보스 심(JBoss Seam)의 교육, 컨설팅, 제품

관리를 담당한다. 개빈 킹과 함께 하이버네이트 인 액션을 집필한 바 있다.

개빈 킹은 하이버네이트와 제이보스 심 프로젝트를 만들었고, EJB 3.0(JSR 220) 전문가 그룹

의 일원이다. 하이버네이트 개념과 제이보스 심, JSF, EJB 3.0 관련 표준화를 위한 Web Beans

JSR 299를 이끌고 있다. 레드햇의 제이보스 부서에서 선도 개발자로 근무하고 있다.

온라인 포럼

하이버네이트 완벽 가이드를 구매하면 매닝 출판사에서 운영하는 사설 웹 포럼에 무료로 접근

할 수 있으며, 이 포럼에서 책에 관한 평을 올리거나 기술적인 질문을 올려 저자를 비롯한 다른

사용자의 도움을 얻을 수 있다. 포럼에 접근하여 포럼에 올라오는 내용을 구독하려면 웹 브라우

저에서 www.manning.com/bauer2을 확인한다. 이 페이지에는 포럼에 등록했을 때 포럼에 들

어가는 방법이나 어떤 종류의 도움을 얻을 수 있는지, 그리고 포럼 활동과 관련한 규칙이 기재돼

있다.

독자에 대한 매닝의 의무는 개별 독자끼리, 그리고 독자와 저자 간에 의미 있는 소통이 이뤄질

수 있는 장을 마련해 주는 것이다. 이런 의무는 저자의 일부가 어느 정도 정해진 부분까지 참여

해야 한다는 뜻은 아니며, 저자의 온라인 포럼에 대한 기여는 순전히 자발적으로 이뤄진다(별다

른 보상은 없다). 우리는 여러분이 저자에게 매우 힘든 질문을 해서 저자들이 꾸준히 흥미를 갖

고 포럼에 기여할 수 있게 해주길 바란다!

온라인 포럼과 이전에 논의한 내용은 이 책이 출간되어 있는 한 계속해서 출판사 웹 사이트에

서 유지될 것이다.

표지 설명

이 책의 표지는 런던의 올드 본드 가에 살았던 윌리엄 밀러가 1802년 1월 1일에 출판한 “오스만

제국 복식 모음집”에서 따왔다. 그 책의 표지는 분실되어 아직까지 찾지 못한 상태다. 책의 목차

에는 영어와 프랑스어로 삽화를 설명하고 있고, 각 삽화에는 해당 삽화를 그린 두 화가의 이름이

기재돼 있다. 아마 두 화가 모두 자신이 그린 그림이 컴퓨터 프로그래밍 책의 표지를 장식하고 있

다는 사실을 알면 놀라움을 금치 못할 것이다. 그것도 200년이라는 시간이 지난 후에 말이다.

Page 37: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxvii

"오스만 제국 복식 모음집"은 매닝 출판사의 한 편집자가 맨해튼 서부 26번가에 위치한 "차고"

라는 골동품 벼룩 시장에서 구입한 것이다. 이 책을 판 사람은 터키 앙카라에 사는 미국인이었

고, 편집자는 그가 막 그날 장사를 끝내려는 차에 이 책을 구입하게 되었다. 그런데 편집자는 그

책을 구입하기에는 가지고 있던 돈이 모자랐다. 벼룩시장에서 성사되는 거래였기에 신용카드로

도 지불할 수 없는 상황이었다. 더군다나 판매자는 그날 저녁 앙카라로 돌아가야 했기에 상황은

비관적으로 흘러갔다. 그들은 이 난감한 문제를 어떻게 해결했을까? 그들은 악수와 함께 구두로

약속하는 구식 방법으로 이 상황을 매듭지었다. 판매자는 모자란 돈만 온라인으로 송금해 달라

고 제안했고, 편집자는 종이에 은행 계좌 번호를 받아적고 “오스만 제국 복식 모음집”을 품에 안

고 돌아왔다. 말할 것도 없이 우리는 바로 다음날 책값을 송금했고, 처음 본 사람인데도 불구하

고 신뢰해 준 고마움과 감명은 지금까지도 남아있다. 이처럼 이 책은 아주 오래전에 일어났던 일

을 떠올리게 한다.

이 책의 표지에 사용한 그림처럼 “오스만 제국 복식 모음집”에 실린 그림은 2세기 전 복식의 다

양함과 풍부함을 생생히 되살려 준다. 그 그림들은 지극히 역동적인 현재를 제외한 다른 모든 역

사적 시대 사이의 분절과 거리감을 느끼게 해준다.

그 시대 이후로 의상 코드는 바뀌어서 당시에는 그렇게 풍부했던 지역적 다양성은 이제는 찾

아볼 수 없다. 이제는 한 대륙에 사는 사람과 다른 대륙에 사는 사람을 구분하기도 힘들다. 그와

같은 현상을 긍정적으로 보려고 노력해 보면 우리는 문화적, 시각적 다양성을 더 다양한 개인의

삶과 맞바꿨다고 할 수 있다. 혹은 더 다양하고 흥미로운, 지적이고 기술적인 삶과 맞바꿨다고 할

수도 있다.

우리 매닝 출판사는 이 모음집으로부터 생생하게 되살아난, 2세기 전 각 지역의 풍부한 다양

성을 보여주는 그림을 책 표지에 사용해서 독창성과 진취성, 그리고 컴퓨터 산업의 즐거움을 온

세상에 알리고자 한다.

Page 38: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

xxxviii

HIB

ER

NA

TE

JAV

A P

ER

SIS

TE

NC

E W

ITH

Page 39: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1

제1부

하이버네이트와 EJB 3.0 시작하기

1부에서는 객체 영속화가 왜 그리 복잡한 주제인지, 그리고 실제로 어떤 해

결책을 사용할 수 있는지 살펴보겠다. 1장은 객체/관계형 패러다임 불일치

와 주요 객체/관계형 매핑(ORM, object/relational mapping)으로 그러한 불

일치를 다루는 전략을 몇 가지 소개한다. 2장에서는 하이버네이트, 자바 퍼

시스턴스, EJB 3.0을 사용하는 튜토리얼을 단계적으로 제공한다. 이 과정에

서 "Hello World" 예제를 다양한 방법으로 구현하고 테스트할 것이다. 또

한 3장에서는 복잡한 비즈니스 도메인 모델을 자바에서 어떻게 구현하는

지, 그리고 어떤 매핑 메타데이터 옵션을 사용할 수 있는지 익힐 것이다.

1부를 읽고 나면 왜 객체/관계형 매핑이 필요하고, 하이버네이트, 자바

퍼시스턴스, EJB 3.0을 어떻게 활용할 수 있는지 이해하게 될 것이다. 1부에

서는 첫 프로젝트를 진행해 나가면서 더 복잡한 문제를 해결할 준비를 할

것이다. 또한 현실 세계의 비즈니스 주체를 자바 도메인 모델로 어떻게 구

현할 수 있고, 객체/관계형 매핑 메타데이터를 다룰 때 어떤 형식을 선호하

는지 이해할 수 있을 것이다.

Page 40: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2

Page 41: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

3

01객체/관계형 영속화 이해하기

우리가 참여하는 모든 소프트웨어 프로젝트에서 데이터를 저장하고 관리하는 방법을 결정하

는 일은 설계 과정에서 가장 핵심적인 부분이다. 자바 애플리케이션에서 데이터베이스에 데이터

를 저장하는 일은 새롭거나 낯선 작업이 아니며, 이미 안정화된 여러 비슷한 영속화 솔루션 중

에서 그냥 아무거나 써도 되길 바랄 것이다. 웹 애플리케이션 프레임워크(스트럿츠Struts, 웹워

크WebWork)나 GUI 컴포넌트 프레임워크(스윙Swing, SWT), 또는 템플릿 엔진(JSP와 벨로서티

Velocity)의 경우를 생각해보라. 서로 경쟁 관계에 있는 솔루션들은 각기 장단점은 다르지만, 모

두 동일한 문제 영역에 대한 전반적인 접근법을 공유한다. 불행히도 아직 영속화 기술의 경우는

그렇지 못하기에 동일한 영속화 문제를 가지고 각 솔루션마다 굉장히 다르게 해결하는 광경을

보게 된다.

지난 몇 년 동안 자바 커뮤니티에서 영속화는 주요 논쟁거리였다. 하지만 개발자들 사이에서

는 문제 범위에 대한 협의조차도 이루어지지 않았다. 영속화는 관계형 데이터베이스 기술과 저

장 프로시저(stored procedure)와 같은 확장 기능을 통해 이미 해결된 문제인가? 아니면 EJB 엔티

티 빈과 같은 자바 컴포넌트로 해결해야 할 문제라고 생각하는가? 심지어 가장 기본적인 CRUD

연산조차 SQL과 JDBC를 이용해서 손으로 직접 작성해야 하는 걸까? 혹은 이런 일은 자동으로

이뤄져야 할까? 만일 모든 데이터베이스 관리 시스템(DBMS, Database Management System)이 자

신만의 고유한 SQL 문법을 갖고 있다면 어떤 방법으로 이식성(portability)을 보장받을 수 있을

까? SQL을 완전히 금지하고 객체형 데이터베이스 시스템(object database system)과 같은 다른 데

■ SQL 데이터베이스를 사용한 객체 영속화

■ 객체/관계형 패러다임 불일치

■ 객체 지향 애플리케이션의 영속화 계층

■ 객체/관계형 매핑의 배경 지식

Page 42: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

4 l 하이버네이트 완벽 가이드

이터베이스 기술을 적용해야만 하는가? 이와 같이 다양한 논의 속에서, 최근 객체/관계형 매핑

5이라는 솔루션이 널리 보급되고 있다. 하이버네이트(Hibernate)는 오픈소스 ORM 서비스 구현

이다.

하이버네이트는 자바에서 영속 데이터를 매핑하는 문제에 대한 완벽한 해법이 되길 지향하

는 야심 찬 프로젝트다. 하이버네이트는 애플리케이션에서 관계형 데이터베이스와 상호작용하

는 것을 중재하여 개발자가 비즈니스 문제에 집중할 수 있게 해준다. 하이버네이트는 비침습적인

(nonintrusive) 솔루션이다. 따라서 비즈니스 로직과 영속적인 클래스를 작성할 때 하이버네이트

에 특화된 규칙이나 디자인 패턴을 반드시 따를 필요가 없다. 게다가 하이버네이트는 대부분의

신규 애플리케이션이나 기존 애플리케이션에도 매끄럽게 통합될 수 있어 기존 애플리케이션을

근본적으로 바꿔야 할 필요도 없다.

이 책은 하이버네이트의 기본 개념부터 고급 요소까지 전부 다루며, 하이버네이트를 이용한

애플리케이션 개발 방법도 설명한다. 이 책에서 권장하는 기법들은 하이버네이트에 국한된 것이

아닐 수도 있다. 어떤 경우에는 하이버네이트라는 문맥 안에서 우리가 생각하는 최선의 영속 데

이터 처리방법을 제시하는 것이기도 할 것이다. 이 책은 개정된 EJB 3.0 명세의 일부로 자바 영속

화에 대한 새로운 표준으로 추가된 자바 퍼시스턴스(Java Persistence)에 대한 것이기도 하다. 하

이버네이트는 자바 퍼시스턴스의 구현 중 하나로, 모든 표준 매핑과 쿼리, API를 구현하고 있다.

여러분은 하이버네이트를 시작하기 전에 객체 영속화와 객체/관계형 매핑과 관련된 핵심적인 문

제에 관해 이해할 필요가 있다. 이 장에서는 EJB 3.0과 자바 퍼시스턴스와 같은 명세와 그것을 구

현한 하이버네이트와 같은 도구가 필요한 이유를 설명하겠다.

우선 객체지향 애플리케이션에서 영속 데이터를 관리하는 방법을 정의하고, SQL, JDBC, 자

바 등 하이버네이트가 기반을 두고 있는 기술 및 표준의 관계에 관해 살펴보겠다. 그러고 나서

관계형 데이터베이스를 가지고 객체지향적으로 소프트웨어를 개발하는 과정에서 부딪치게 되

는 객체/관계형 패러다임 불일치(object/relational paradigm mismatch)와 포괄적인 문제에 관해

논의한다. 이러한 문제를 통해 애플리케이션에서 영속화와 관련된 코드 작성에 들이는 시간을

최소화시켜줄 도구와 패턴이 필요하다는 것이 분명해 질 것이다. 다양한 도구와 영속성 처리 과

정을 비교해보면 ORM이 다양한 시나리오에 적용될 수 있는 최선의 솔루션이라는 것을 확신하

게 될 것이다. 이 장에서 소개하는 ORM의 장점과 단점에 대한 논의는 현재 진행하는 프로젝트

에서 영속성 처리를 위한 솔루션을 선택할 때 최선의 결정을 내리는 데 필요한 충분한 배경 지

식이 될 것이다.

또한 다양한 하이버네이트 모듈도 살펴보고, 하이버네이트만 쓰는 경우를 비롯해서 자바 퍼시

Page 43: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 5

스턴스 및 EJB 3.0과 호환되는 기능을 하이버네이트와 함께 쓰는 방법도 살펴볼 것이다.

하이버네이트를 배우기 위해 이 책을 순서대로 읽을 필요는 없다. 지금 당장 하이버네이트를

사용하고 싶다면 이 책의 두 번째 장으로 넘어가서 "Hello World" 예제를 살펴보고 프로젝트를

구성한다. 하지만 이 책의 나머지 부분을 읽는 동안 언젠가는 반드시 이 장으로 되돌아오기를 권

하고 싶다. 그래야만 이 책의 나머지 부분을 읽어나가는 데 필요한 모든 배경 지식을 갖추게 될

것이다.

1.1 영속화란 무엇인가?

대부분의 애플리케이션은 장기간 유지되는 영속 데이터가 필요하다. 영속화(persistence)는 애플

리케이션 개발의 기본 개념 중 하나다. 전원이 나갔을 때 정보시스템이 데이터를 계속 보존하지

않았다면 그 시스템은 별로 쓸모가 없을 것이다. 자바에서 영속화를 이야기할 때는 일반적으로

SQL을 이용해서 관계형 데이터베이스에 데이터를 저장하는 것을 가리킨다. 여기서는 먼저 이러

한 영속화와 관련한 기술과 그러한 기술을 자바에서 사용하는 방법을 살펴보겠다. 그와 같은 지

식을 갖추고 나면 영속화를 비롯해 객체지향 애플리케이션에서 영속화를 구현하는 방법에 관한

논의를 이어가겠다.

1.1.1 관계형 데이터베이스

다른 여느 개발자처럼 아마 여러분도 관계형 데이터베이스를 사용하고 있을 것이다. 대부분의

개발자들은 매일 관계형 데이터베이스를 사용한다. 관계형 기술은 매우 잘 알려져 있고, 그 사실

하나만으로도 많은 조직에서 관계형 데이터베이스를 선택해야 할 이유는 충분하다. 그러나 관계

형 데이터베이스는 단지 많이 알려져 있다는 것 이상의 가치가 있다. 관계형 데이터베이스는 유

연하고 견고한 데이터 관리 기능을 바탕으로 현재의 지위를 확보했다. 관계형 데이터 모델의 완

전하고 일관성 있는 이론적 배경으로 인해 관계형 데이터베이스는 여러 매력적인 특징 가운데서

도 데이터 무결성(data integrity)을 효과적으로 보장하고 보호할 수 있다. 심지어 어떤 사람들은

30년도 더 전에 E.F.Codd(Codd, 1970)가 처음 소개한 데이터 관리를 위한 관계형 이론이 전산

학에서 가장 큰 발명이라 말하기도 한다.

관계형 데이터베이스 관리 시스템은 자바에 종속적이지 않으며, 관계형 데이터베이스도 특정

애플리케이션에 종속적이지 않다. 이것은 데이터 독립성(Data Independency)이라고 알려진 중요

한 원칙이다. 바꿔 말해서, 아무리 강조해도 지나치지 않은 한 가지 사실은 데이터는 그 어떤 애

Page 44: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

6 l 하이버네이트 완벽 가이드

플리케이션보다 더 오래 존속한다는 것이다. 관계형 기술은 서로 다른 애플리케이션 또는 같은

애플리케이션을 구성하는 여러 기술(예를 들면, 트랜잭션 엔진이나 리포팅 엔진과 같은)이 데이

터를 공유하는 방법을 제공한다. 관계형 기술은 다양한 시스템이나 플랫폼에서 공통적으로 사

용하고 있다. 그렇기 때문에 관계형 데이터 모델이 종종 비즈니스 요소(business entity)를 전사적

인 차원에서 표현한 것이 되기도 한다.

관계형 데이터베이스 관리 시스템은 SQL 기반의 애플리케이션 프로그래밍 인터페이스(API,

application programming interface)를 가지고 있기 때문에 관계형 데이터베이스 제품을 SQL 데

이터베이스 관리 시스템이라고 부르거나, 특정 시스템을 언급할 때는 SQL 데이터베이스라고도

한다.

SQL 데이터베이스의 특징을 자세히 알아보기에 앞서 중요한 사안을 하나 짚어보자. 관계형이

라는 이름을 붙여 시장에 출시되고 있긴 하지만 SQL 인터페이스만 제공하는 데이터베이스는 실

제로 진정한 의미의 관계형 데이터베이스는 아니며 원래의 개념과도 여러모로 차이가 있다. 자

연스레 이로 인한 혼란이 발생한다. SQL을 사용하는 사람들은 SQL 언어가 지닌 단점 때문에 관

계형 데이터 모델을 비난하고, 관계형 데이터 관리 전문가들은 SQL 표준이 관계형 모델과 그것

의 개념을 제대로 구현하지 못했다고 비난한다. 애플리케이션 개발자들은 그 사이에 끼여 실제

로 동작하는 무언가를 만들어 내려고 고군분투하고 있다. 이 책에서는 위와 같은 문제에 대해 현

실적으로 중요한 측면을 집중적으로 조명할 것이다. 이 문제와 관련된 배경 지식이 궁금하다면

파비안 파스칼(Fabian Pascal)이 쓴 Practical Issues in Database Management: A Reference for

the �inking Practitioner(Pascal, 2000)를 읽어보길 권한다.

1.1.2 SQL 이해하기

하이버네이트를 효율적으로 사용하려면 먼저 관계형 모델과 SQL을 이해해야 한다. 데이터 무결

성을 보장하려면 관계형 모델을 비롯해 정규화(normalization) 기법과 같은 주제도 알아야 하며,

하이버네이트 애플리케이션의 성능을 최적화하기 위해 SQL 튜닝과 관련된 지식이 필요해질 때

도 있다. 하이버네이트는 반복적인 코딩을 상당 부분 자동으로 처리해주지만, 최신 SQL 데이터

베이스의 모든 기능을 활용하고자 한다면 반드시 하이버네이트에 국한되지 않는 영속화 기술에

대해 이해의 폭을 넓혀야 한다. 지식의 폭을 넓히는 궁극적인 목표는 영속 데이터를 견고하고 효

과적으로 관리하기 위해서라는 사실을 기억해두기 바란다.

이 책에서 사용하는 몇 가지 SQL 용어를 간략하게 살펴보자. 우선 SQL을 데이터베이스 스키

마를 생성하기 위한 CREATE나 ALTER 같은 데이터 정의 언어(DDL, Data De�nition Language)

Page 45: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 7

로 쓴다. 테이블(인덱스, 시퀀스 등)을 생성하고 나서, SQL을 데이터를 가져오고 조작하기 위

한 데이터 조작 언어(DML, Data Manipulation Language)로도 사용한다. 이러한 조작에는 삽입

(insertion), 갱신(update) 그리고 삭제(deletion)와 같은 연산이 있다. 데이터를 가져올 때는 한정

(restriction), 프로젝션(projection), 그리고 (카테시안 곱Cartesian product을 포함한) 조인(join)과

같은 쿼리를 실행한다. 효율적인 보고서 작성을 위해 SQL을 써서 그룹화(group), 정렬(order), 집

계(aggregate)를 하기도 한다. 심지어 SQL 문을 다른 SQL 문 안에 중첩해서 쓸 수도 있는데, 이때

하위 쿼리(subselect)와 같은 기법을 사용한다.

여러분은 오랫동안 SQL을 사용해 왔고 SQL로 작성된 문장과 기초적인 연산에 익숙할 것이다.

하지만 지금까지의 경험으로 미뤄봤을 때 SQL은 때때로 기억하기가 힘들고 어떤 용어는 쓰임이

매우 다양하다. 이 책에서는 위에서 언급한 것과 동일한 용어와 개념을 사용하므로 앞서 언급된

용어 가운데 새롭거나 낯선 용어가 있다면 부록 A를 읽어보기 바란다.

만약 성능 측면에서 특별히 자세히 알고 싶거나 SQL이 어떻게 실행되는지 알고 싶다면 댄

타우(Dan Tow)가 쓴 SQL Tuning(Tow, 2003)이라는 훌륭한 책을 참고하기 바란다. 또한 관

계형 데이터베이스의 이론과 개념, 목표 등에 대해서는 크리스 데이트(Chris Date)가 쓴 An

Introduction to Database Systems(Date, 2003)도 읽었으면 한다. 특히 이 책은 데이터베이스나

데이터 관리에 대한 모든 질문에 대한 좋은 참고 자료가 될 것이다.

관계형 데이터베이스가 ORM의 일부를 차지하고 있다면 당연히 다른 한 쪽은 자바 애플리케

이션에서 SQL을 사용해 데이터베이스로 영속화하거나 데이터베이스에서 불러오는 객체로 구성

돼 있다.

1.1.3 자바에서 SQL 사용하기

자바 애플리케이션에서 SQL 데이터베이스를 사용할 때 자바 코드에서는 JDBC API를 통해

SQL 문을 실행한다. SQL을 직접 작성해서 자바 코드 내에 포함하든 자바 코드로 생성하든 모두

JDBC API를 사용하여 인자 값을 미리 작성해둔 쿼리(prepared query)의 매개변수로 매핑하고,

쿼리를 실행하고, 쿼리 결과 테이블을 스크롤(scroll)하고, 결과 집합(Result Set)으로부터 데이터

를 가져온다. 이러한 일은 저수준(low-level) 데이터 접근 작업이다. 애플리케이션 개발자라면 데

이터 접근이 필요한 비즈니스 문제에 더 관심이 있을 것이다. 우리가 진정 원하는 바는 저수준 코

드를 작성하는 고통으로부터 벗어나 객체(클래스의 인스턴스)를 직접 데이터베이스에 저장하거

나 데이터베이스로부터 직접 객체를 가져오는 것이다.

데이터 접근 작업은 대개 지루하기 때문에 다음과 같은 의문이 생길 수밖에 없다. 객체지향 애

Page 46: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

8 l 하이버네이트 완벽 가이드

플리케이션에서 관계형 데이터 모델과 (특히) SQL을 사용하는 것이 과연 옳은 선택인가? 이 질

문에 대해 우리는 망설임 없이 "그렇다"고 대답한다. 현재 SQL 데이터베이스가 컴퓨팅 산업 전반

에 걸쳐 영향을 끼치고 있는 데는 여러 가지 이유가 있다. 관계형 데이터베이스 관리 시스템은 유

일하게 검증된 데이터 관리 시스템이며, 대부분의 자바 프로젝트에서 이것을 필요로 하기 때문

이다.

하지만 지난 15년간 개발자들은 "패러다임 불일치"에 대해 이야기해왔다. 그러한 불일치는 엔

터프라이즈급 프로젝트에서 영속화와 관련된 영역에 왜 그렇게 막대한 노력을 쏟아 왔는지를 설

명해 준다. 여기서 패러다임은 객체 모델링과 관계형 모델링, 또는 객체지향 프로그래밍과 SQL을

가리킨다.

영속화가 객체지향 애플리케이션 개발에서 가지는 의미를 살펴보는 것으로 불일치 문제에 대

한 탐구를 시작해보자. 먼저 이 절을 시작하면서 설명했던 영속화에 대한 간단한 정의를 더 넓게

확장하여 어떤 것들이 영속적인 데이터를 사용하거나 관리하는 데 관련돼 있는지를 더 깊이 있

게 이해할 것이다.

1.1.4 객체지향 애플리케이션에서의 영속화

객체지향 애플리케이션에서는 영속화를 통해 객체가 자신을 만들어 낸 프로세스를 벗어나서도

존재할 수 있다. 객체의 상태는 디스크에 저장될 수 있으며, 추후 필요할 때 같은 상태를 갖는 객

체로 다시 생성될 수 있다.

이것은 단지 어떤 객체 하나에만 국한되는 것이 아니라 그것과 상호 연관된 객체망 모두 영속

적인 상태(persistent)가 될 수 있고 추후 새로운 프로세스에서 다시 만들어 질 수 있다는 뜻이

다. 대부분의 객체는 영속적인 상태가 아닌데, 비영속(transient) 객체는 자신을 생성한 프로세

스의 생명주기에 국한된 제한적인 수명을 갖는다. 거의 대부분의 자바 애플리케이션에는 영속

(persistent) 객체와 비영속(transient) 객체가 혼재돼 있어 영속적인 데이터를 관리해줄 서브시스

템이 필요하다.

오늘날 관계형 데이터베이스는 영속적인 데이터에 대해 조작과 정렬, 검색, 그리고 데이터의 합

계 등을 가능하게 하는 구조적인 표현법을 제공한다. 데이터베이스 관리 시스템은 데이터의 무

결성과 동시성 관리를 책임진다. 즉 다수의 사용자 및 애플리케이션 간에 데이터를 공유할 수 있

게 해줘야 한다는 뜻이다. 또한 이런 시스템은 제약 조건으로 구현된 무결성 규칙에 의해 데이터

의 무결성을 보장하며 데이터 차원의 보안을 제공한다. 앞으로 이 책에서 영속화에 대해 논의할

때는 다음과 같은 사항을 모두 고려할 것이다.

Page 47: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 9

구조적인 데 ▒ 이터의 저장, 구성, 조회

동시성과 데이터 무결성 ▒

데이터 공유 ▒

그리고 특히 이러한 문제들을 도메인 모델을 사용하는 객체지향 애플리케이션 맥락에서 생각

해 보겠다.

도메인 모델을 사용하는 애플리케이션은 직접적으로 비즈니스 엔티티에 대해 테이블 형태의

표현을 직접 사용하지 않고, 비즈니스 엔티티를 객체지향적으로 표현한 자체적인 모델을 갖고 있

다. 예를 들어 온라인 경매 시스템의 데이터베이스에 ITEM과 BID라는 테이블이 있다면 자바

애플리케이션에서는 Item과 Bid라는 클래스를 정의하는 식이다.

그리고 SQL 결과 집합의 행(row)과 열(column)을 직접 이용하는 대신, 비즈니스 로직은 이러한

객체지향 도메인 모델과 그것을 실행시간에 실현화(realization)한 상호 연관된 객체망과 상호작

용한다. Bid에 대한 각 인스턴스는 경매 Item을 참조하며, 각 Item은 여러 Bid 인스턴스에 대한

참조를 컬렉션으로 가지고 있을 것이다. 비즈니스 로직은 DB(SQL 저장 프로시저로)에서 실행되

지 않고, 애플리케이션 단에 구현돼 있다. 이렇게 하면 비즈니스 로직이 상속과 다형성과 같은 세

련된 객체지향 개념을 활용할 수 있다. 예를 들어, 다형적(polymorphic) 메서드 호출에 의존하는

전략(Strategy), 중재자(Mediator), 복합체(Composite) 패턴 같이 잘 알려진 디자인 패턴(Gamma

and others, 1995)을 활용할 수도 있다.

한 가지 주의할 점은 모든 자바 애플리케이션이 이런 방식으로 설계되지는 않을뿐더러 꼭 그래

야 하는 것도 아니라는 것이다. 간단한 애플리케이션은 도메인 모델을 사용하지 않고 설계하는

편이 훨씬 좋다. 애플리케이션이 복잡하다면 기존 저장 프로시저를 재사용해야 할지도 모른다.

SQL과 JDBC API는 완벽하게 DB 테이블에 직접 접근하여 데이터를 제어할 수 있으며, JDBC

RowSet은 CRUD 연산을 훨씬 간편하게 만들어 준다. 영속적인 데이터에 대한 표(tabular) 형태

의 표현을 직접 이용하는 편이 간단하고 이해하기도 쉽다.

하지만 복잡한 비즈니스 로직을 다루는 애플리케이션이라면 도메인 모델을 사용하는 접근법

이 코드의 재사용성과 유지보수성을 향상시켜준다. 현장에서는 이 두 가지 전략 모두 흔히 사용

되고 있고, 또 필요하기도 하다. 대부분의 애플리케이션은 상당히 많은 양의 데이터를 수정하고

처리하는 프로시저를 실행할 필요가 있다. 한편으로 애플리케이션 모듈은 일반적인 온라인 트랙

잭션 처리 로직을 애플리케이션 단에서 처리하는 객체지향 도메인 모델의 혜택을 얻을 수 있다.

따라서 영속적인 데이터를 애플리케이션 코드로 가져오는 효율적인 방법이 필요하다.

Page 48: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

10 l 하이버네이트 완벽 가이드

SQL과 관계형 데이터베이스를 다시 생각해보면 두 패러다임 사이의 불일치를 발견할 수 있다.

프로젝션이나 조인 같은 SQL 연산은 결과 데이터를 표 형태로 가져온다. (이처럼 어떤 릴레이션

[relation]에 대한 연산의 결과도 항상 릴레이션인 것을 추이적 닫힘transitive closure이라 한다.)

하지만 이것은 자바 애플리케이션에서 비즈니스 로직을 실행할 때 사용하는 상호 연관 관계에

있는 객체망과는 상당히 다른 형태다. 단지 같은 모델을 표현하는 방법만 다른 것이 아니라 근본

적으로 전혀 다른 모델인 것이다.

이러한 내막을 알고 있어야 두 가지 데이터 표현법(객체지향 도메인 모델과 영속적인 릴레이션

모델)을 모두 사용하는 애플리케이션이 반드시 해결해야만 하는 문제가 보이기 시작할 것이다.

자, 그럼 패러다임의 불일치 문제에 관해 좀 더 자세히 알아보자.

1.2 패러다임 불일치

객체/관계형 패러다임 불일치는 여러 부분으로 나누어 볼 수 있으며, 각각 하나씩 살펴보자. 먼저

이러한 문제가 없는 간단한 예제로 시작해서 이 예제를 점점 확장하다 보면 패러다임의 불일치

를 발견할 수 있을 것이다.

예제로 온라인 전자상거래 애플리케이션을 설계해서 구현하고 있다고 가정하자. 그림 1.1에 나

온 것처럼 사용자(user)를 표현하는 클래스와 사용자의 지불 상세(billing details)를 표현하는 클

래스가 필요하다.

이 다이어그램에서 User는 여러 개의 BillingDetails를 가지고 있음을 알 수 있다. 또한 각 관계

는 서로 양방향으로 연관 관계를 맺고 있으므로 어느 쪽에서든지 상대 클래스에 접근할 수 있다.

이러한 엔티티를 표현하는 클래스는 매우 간단하다.

public class User {

private String username;

private String name;

private String address;

private Set billingDetails;

// 접근 메서드(접근자/설정자), 비즈니스 메서드 등.

...

}

public class BillingDetails {

private String accountNumber;

Page 49: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 11

private String accountName;

private String accountType;

private User user;

// 접근 메서드(접근자/설정자), 비즈니스 메서드 등.

...

}

그림 1.1 | User와 BillingDetails 엔티티에 대한 간단한 UML 다이어그램

여기서는 영속화해야 할 엔티티의 상태에만 관심이 있으므로 getUsername()이나

billAuction()과 같은 비즈니스 메서드나 프로퍼티 접근자의 구현은 생략했다.

이런 경우라면 깔끔한 SQL 스키마 설계가 금방 떠오를 것이다.

create table USERS (

USERNAME varchar(15) not null primary key,

NAME varchar(50) not null,

ADDRESS varchar(100)

)

create table BILLING_DETAILS (

ACCOUNT_NUMBER varchar(10) not null primary key,

ACCOUNT_NAME varchar(50) not null,

ACCOUNT_TYPE varchar(2) not null,

USERNAME varchar(15) foreign key references USERS

)

두 엔티티 사이의 관계는 BILLING_DETAILS 테이블의 USERNAME이라는 외래키(foreign

key)로 표현돼 있다. 이처럼 간단한 도메인 모델에서는 패러다임의 불일치를 찾아볼 수 없기 때

문에 직접 JDBC 코드로 사용자와 지불 상세에 관한 insert, update, delete 문을 작성하기는 어

렵지 않다.

자, 이제 현실적인 문제를 생각해 봤을 때 어떻게 될지 살펴보자. 이 애플리케이션에 엔티티와

연관 관계를 조금 더 추가하면 패러다임의 불일치를 발견할 수 있다.

현재 구현한 애플리케이션에서 가장 확연히 드러나는 문제는 주소를 하나의 String 값으로 설

계했다는 것이다. 대부분의 시스템에서는 주소를 읍/면/동, 시/군, 도, 국가, 우편번호 등의 정보

로 각각 분리해서 저장할 필요가 있다. 물론 User 클래스에 각 속성을 직접 추가할 수도 있지만

Page 50: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

12 l 하이버네이트 완벽 가이드

다른 클래스에서도 주소 정보를 사용하고자 하기 때문에 Address 클래스를 따로 분리해서 생성

하는 편이 더 낫다. 그림 1.2에 이러한 사항이 반영돼 있다.

자, 그럼 이제 데이터베이스에 ADDRESS 테이블을 따로 만들어 줘야 할까? 꼭 그렇지는 않다.

보통은 User 테이블에 개별 행으로 주소 정보를 보관한다. 이렇게 설계하면 한 번의 쿼리로 User

와 Address에 대한 정보를 테이블 간의 조인을 하지 않고도 가져올 수 있기 때문에 성능 면에서

좀 더 나은 방법이다. 가장 좋은 방법은 주소를 표현하는 사용자 정의(user-de�ned) SQL 데이터

타입을 만들어서 USER 테이블에 여러 개의 열을 새로 추가하는 대신 새로 만든 SQL 타입의 열

하나로 주소를 표현하는 것이다.

이처럼 어떤 정보를 여러 개의 열로 표현하는 방법과 새로운 SQL 데이터 타입을 사용해서

하나의 열로 표현하는 방법 중에서 하나를 선택해야 한다. 이것이 바로 세밀함의 불일치 문제

(problem of granularity)다.

그림 1.2 | User는 Address를 가진다.

1.2.1 세밀함의 불일치 문제

세밀함(Granularity)은 이용하는 타입의 상대적인 크기를 가리킨다.

다시 예제로 돌아가보자. Address라는 자바 객체를 단 하나의 열로 저장할 수 있게 데이터베이

스에 새로운 데이터 타입을 하나 추가하는 것이 가장 좋은 방법인 듯 하다. 물론 이때는 자바에

서 새로 생성한 Address 클래스와 새로 생성한 ADDRESS SQL 데이터 타입 간에 상호 호환성

이 보장돼야 한다. 하지만 현재 사용하고 있는 SQL 데이터베이스의 UDT(사용자 정의 데이터 타

입) 지원을 확인해 보면 많은 문제점을 발견할 수 있다.

UDT 지원은 이른바 전통적인 SQL을 확장한 객체 관계형 기능 중 하나다. 하지만 이 용어는

데이터베이스 관리 시스템에서 세련된 데이터 타입 시스템(데이터를 관계형 방식으로 처리할 수

있는 시스템을 구입했다면 당연히 탑재돼 있으리라 여기는)을 탑재하고 있거나 그것을 지원하리

라는 의미를 주기 때문에 혼동을 준다. 불행히도 UDT 지원은 대부분의 SQL 데이터베이스 관리

시스템이 분명하게 지원하도록 되어 있는 기능도 아니고 확실히 다른 시스템으로 이식하지도 못

한다. 게다가 SQL 표준이 사용자 정의 데이터 타입을 지원하기는 하지만 매우 미흡한 실정이다.

Page 51: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 13

이러한 제약 사항은 관계형 데이터 모델의 잘못이 아니다. 이는 1990년대 중반 벤더들 사이의

객체 관계형 데이터베이스 전쟁으로 인해 이런 중요한 기능을 표준화하는 데 실패한 것에서 기인

한다. 오늘날 대부분의 개발자들은 별다른 의문 없이 이렇게 타입 제한이 있는 SQL 제품을 받

아들인다. 우리가 사용하고 있는 SQL 데이터베이스가 복잡한 UDT를 지원하게 되더라도 여전

히 자바에서 새로운 타입을 작성하면 SQL에서도 다시 타입 선언을 중복해서 해야 한다. 이런 문

제를 해결하기 위해 자바 진영에서 SQLJ와 같은 해법을 모색하기도 했지만 아쉽게도 크게 성공

을 거두지는 못했다.

이런저런 이유로 UDT의 사용 및 SQL 데이터베이스에서 자바 타입을 사용하는 것은 현장에

정착하지 못했고, UDT를 사용하여 확장한 레거시(legacy) 스키마를 만날 가능성 또한 희박하

다. 때문에 새로운 Address 클래스의 인스턴스를 자바에서 사용한 것과 같이 데이터 타입을 새

로운 열 하나에 저장할 수도 없고 그렇게 하지도 않을 것이다.

현실적인 해결 방안은 각 벤더의 고유한 SQL 타입(불린, 숫자, 문자열 데이터 타입과 같은)을

사용해서 여러 개의 열로 구성하는 것이다. 보통 USERS 테이블은 다음과 같이 정의할 수 있다.

create table USERS (

USERNAME varchar(15) not null primary key,

NAME varchar(50) not null,

ADDRESS_STREET varchar(50),

ADDRESS_CITY varchar(15),

ADDRESS_STATE varchar(15),

ADDRESS_ZIPCODE varchar(5),

ADDRESS_COUNTRY varchar(15)

)

도메인 모델의 클래스는 User와 같이 구성 단위가 큰 엔티티 클래스에서부터 Address와 같

은 더 세밀한 클래스, 그리고 zipcode와 같은 String 타입의 프로퍼티(property)에 이르기까지 다

양하게 세밀함을 정할 수 있다. 반면 SQL 데이터베이스는 USERS 같은 테이블과 ADDRESS_

ZIPCODE 같은 열처럼 두 단계의 세밀함만 지원한다.

단순한 영속화 기술은 상당수 이러한 불일치를 반영하지 못해 결국 객체 모델에 덜 유연한

SQL 표현법을 쓰게끔 한다. 이러한 예로 우리는 zipcode라는 프로퍼티를 포함하는 User 클래스

를 무수히 볼 수 있었다.

하지만 세밀함의 불일치 문제가 특별히 해결하기 힘든 종류의 문제는 아닌 것으로 드러났다.

현존하는 너무나도 많은 시스템에서 세밀함의 불일치 문제를 볼 수 없었더라면 아예 세밀함의

Page 52: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

14 l 하이버네이트 완벽 가이드

불일치 문제에 대해서는 논의조차 하지 않았을 것이다. 4장 4.4절, "구성 단위가 세밀한 모델과

매핑"에서 이 문제의 해결책을 논의하겠다.

전자상거래 애플리케이션에서 사용자가 여러 가지 방법으로 결제할 수 있게끔 객체지향 설계

의 특징 가운데 하나인 상속(inheritance)을 사용하는 도메인 모델을 고려하면 훨씬 복잡하고 흥

미로운 문제가 발생한다.

1.2.2 하위 타입 문제

자바에서는 상위 클래스와 하위 클래스를 이용해 상속을 구현한다. 이것이 왜 패러다임의 불

일치가 될 수 있는지 설명하기 위해 전자상거래 애플리케이션에 자금 이체뿐 아니라 신용카

드와 은행계좌도 쓸 수 있게 해보자. 모델에 이러한 변화를 반영하는 가장 자연스런 방법은

BillingDetails 클래스를 상속하는 것이다.

즉, BillingDetails 추상 상위 클래스와 CreditCard, BankAccount 등과 같은 하위 클래스를

작성하는 것이다. 각 하위 클래스에는 약간씩 다른 데이터와 해당 데이터에 대해 전혀 다른 기능

을 하는 메서드를 정의한다. 그림 1.3은 이런 모델을 UML로 보여준다.

그림 1.3 | 상속을 이용해 다양한 지불 수단을 지원

아마 SQL도 상위 테이블과 하위 테이블을 표준으로 지원할 것이다. 그것은 사실상 그 부모로

부터 특정 열을 상속받아 테이블을 생성할 수 있게 하는 것이다. 하지만 이러한 특징은 여전히

의문스럽다. 왜냐하면 이 같은 특징은 원래 테이블에 가상 열(virtual column)과 같은 새로운 개

념을 도입하기 때문이다. 보통 뷰(view)라고 하는 가상 열은 오직 가상 테이블에만 있으리라 예상

한다. 더욱이 이론적인 수준에서 자바에서 적용하려고 하는 상속은 타입 상속(type inheritance)

인데 테이블은 타입이 아니다. 그런 이유로 상위 테이블이나 하위 테이블이라는 개념이 미심쩍

다는 것이다. 어떤 경우든 우리는 여기서 편한 방법을 택할 수 있으며, SQL 데이터베이스 제품이

일반적으로 타입 상속이나 테이블 상속을 구현하지 않으며, 설사 타입 상속이나 테이블 상속을

Page 53: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 15

구현하더라도 표준 문법을 따르지 않아 (뷰가 갱신 가능해질 경우 무결성 규칙에 제약이 생기는

등) 데이터 무결성 문제에 노출될 수 있음을 확인할 수 있다.

5장 5.1절, "클래스 상속 매핑하기"에서는 하이버네이트와 같은 ORM 솔루션이 클래스 계층

구조를 데이터베이스의 단일 테이블 또는 여러 테이블로 영속화하는 문제를 어떻게 해결하는지

논의할 것이다. 이제는 관련 커뮤니티에서도 이 문제를 잘 이해하고 있고 ORM 솔루션도 대부분

비슷한 기능을 탑재하고 있다.

하지만 아직 상속 문제가 완전히 해결된 것은 아니다. 모델에 상속을 도입함으로써 다형성

(polymorphism)의 가능성도 덩달아 생기기 때문이다.

User 클래스는 상위 클래스인 BillingDetails와 연관 관계를 맺고 있는데 이것이 바로 다형적

인 연관 관계(polymorphic association)다. 실행 시에 User 객체는 BillingDetails의 특정 하위 클

래스의 인스턴스를 참조할지도 모른다. 이와 유사하게 BillingDetails를 참조하지만 그것의 하위

클래스의 인스턴스를 반환하는 다형적 쿼리를 작성하고자 할 수도 있다.

SQL 데이터베이스는 다형적인 연관 관계를 표현하는 명시적인 방법뿐 아니라 표준화된 방법

도 충분히 제공하지 않는다. 외래키 제약 조건에 의한 관계는 정확히 하나의 대상 테이블에 대해

서만 가능하고 여러 테이블을 참조하는 외래 키를 정의하기란 쉽지 않은 일이다. 이런 종류의 무

결성 제약 조건을 보장하려면 절차적 제약 조건을 써야 한다.

이처럼 하위 타입의 불일치는 결과적으로 상속 전략을 제공하지 않는 SQL 데이터베이스에

상속 구조를 저장해야 하는 문제를 야기한다. 다행히도 5장에서 보게 될 세 가지 상속 매핑 전

략은 다형적인 연관 관계의 표현을 가능케 하고 다형적 쿼리를 효율적으로 실행할 수 있게끔 설

계돼 있다.

객체/관계형 불일치 문제의 다음 측면은 객체 동일성에 관한 문제다. 앞서 논의한 애플리케이

션에서 USERS 테이블의 주키를 USERNAME으로 정의한 것은 알고 있을 것이다. 이것은 과연

잘한 선택이었을까? 자바에서는 객체의 동일성을 어떻게 다룰까?

1.2.3 동일성 문제

처음에는 객체 동일성 문제가 뚜렷이 드러나지 않지만 전자상거래 시스템을 확장하거나 규모를

키우는 과정에서 두 객체가 같은지 검사해야 할 때 흔히 이 문제를 접하게 된다. 이 문제에는 세

가지 해법이 있는데, 그 중 두 가지는 자바 쪽에 있고 하나는 SQL 데이터베이스 쪽에 있다. 예상

했겠지만 약간만 신경 쓰면 세 가지 방법 모두 효과가 있을 것이다.

Page 54: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

16 l 하이버네이트 완벽 가이드

자바 객체는 동일하다는 것에 대해 아래와 같이 두 가지 다른 정의를 가지고 있다.

▒ 객체 동일성 (메모리상 위치가 같은지 확인하는 것이며, a==b의 형식으로 검사함)

equals() 메서드 구현에 의해 결정되는 ▒ 동등함 ('값에 의한 동등함'이라고도 함)

반면 데이터베이스 행의 동일성은 주키 값으로 표현한다. 9장 9.2절, "객체 동일성과 동등성"

에서도 살펴보겠지만, 주키 값으로 비교하는 것이 equals() 메서드나 ==로 비교하는 것보다 더

정확하다. 왜냐하면 여러 개의 다른 객체가 동시에 데이터베이스의 같은 행을 나타낼 수도 있

기 때문이다(이를테면, 동시에 실행되는 애플리케이션 스레드 상에서). 게다가 영속 클래스의

equals() 메서드를 올바르게 구현하는 것과 관련된 몇 가지 미묘한 문제도 있다.

데이터베이스 식별자와 연관이 있는 다른 문제에 관해 예를 들어 논의해 보자. USERS 테이블

에서는 USERNAME을 주키(primary key)로 사용한다. 불행히도 이런 식으로 주키를 결정하면

username을 변경하기 어렵다는 문제가 발생한다. 또한 USERS 테이블에 있는 USERNAME 열

뿐만 아니라, 이를 참조하는 BILLING_DETAILS의 외래키 역시 변경하기 힘들다. 이 문제를 해

결하기 위해 이 책의 후반부에 소개하겠지만, 아주 적합한 자연키(natural key)가 없다면 대리키

(surrogate key) 사용을 권한다. 그리고 어떻게 좋은 키 값을 찾아낼 수 있는지에 대해서도 논의하

겠다. 대리키 열은 사용자에게 아무런 의미도 나타내지 않는 주키 열이다. 무슨 말인가 하면 사

용자에게는 아무것도 나타내주지 못하지만 소프트웨어 시스템 내에서는 데이터를 구별할 수 있

게끔 해준다는 뜻이다. 그 예로 아래와 같이 테이블 정의를 변경해 보았다.

create table USERS (

USER_ID bigint not null primary key,

USERNAME varchar(15) not null unique,

NAME varchar(50) not null,

...

)

create table BILLING_DETAILS (

BILLING_DETAILS_ID bigint not null primary key,

ACCOUNT_NUMBER VARCHAR(10) not null unique,

ACCOUNT_NAME VARCHAR(50) not null,

ACCOUNT_TYPE VARCHAR(2) not null,

USER_ID bigint foreign key references USERS

)

USER_ID와 BILLING_DETAILS_ID 열의 값은 시스템이 생성한 값이다. 이러한 열은 순수

하게 데이터 모델의 장점을 살릴 목적으로 도입된 것인데, 그럼 이것들을 도메인 모델에서 어떻

Page 55: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 17

게 표현해야 할까? 이 주제에 대해서는 4장 4.2절, "식별자가 있는 엔티티 매핑하기”에서 논의하고

ORM을 이용한 해결책을 모색해 볼 것이다.

영속성 맥락에서 이야기하자면 동일성은 시스템이 캐싱과 트랜잭션을 다루는 방법과 밀접한

관련이 있다. 여러 영속화 솔루션은 각자 다른 전략을 취하며, 따라서 이것은 지금까지 혼란스러

운 부분으로 계속 여겨져 왔다. 이러한 흥미로운 주제와 그것들이 어떻게 연관돼 있는가에 관해

서는 10장과 13장에서 살펴보겠다.

지금까지 설계한 전자상거래 애플리케이션의 기본 구조만으로도 세밀함, 하위 타입, 객체 동일

성과 관련한 불일치 문제를 식별해 낼 수 있었다. 이제 애플리케이션의 다른 부분을 논의할 준비

가 거의 됐다. 하지만 먼저 연관 관계라는 중요한 개념을 논의할 필요가 있다. 어떻게 클래스 간의

연관 관계를 매핑하고 다뤄야 할까? 데이터베이스의 외래키만으로 충분할까?

1.2.4 연관 관계와 관련된 문제

도메인 모델에서 연관 관계는 엔티티 간의 관계를 나타낸다. User, Address, BillingDetails 클래

스는 모두 연관 관계를 맺고 있다. Address와 달리 BillingDetails는 독립적으로 존재한다. 연관

관계 매핑과 엔티티 간의 연관 관계를 관리하는 일은 모든 객체 영속화 솔루션에서 중요한 개념

이다.

여러 객체지향 언어는 객체 참조를 이용하여 연관 관계를 표현하지만 관계형 데이터베이스는

외래키 열로 연관 관계를 표현하며, 외래키는 키 값의 사본과 무결성을 보장하기 위한 제약 조건

을 가지고 있다. 이 두 가지 표현방법에는 큰 차이점이 있다.

객체 참조는 근본적으로 하나의 객체에서 다른 객체로의 방향성을 가진 연관 관계다. 만약 두

객체 간의 연관 관계가 양방향으로 탐색할 수 있는 관계라면 연관 관계를 연관 관계가 있는 클래

스마다 각각, 즉 두 번씩 맺어줘야 한다. 이미 이 같은 연관관계를 도메인 모델 클래스에서 본 적

이 있을 것이다.

public class User {

private Set billingDetails;

...

}

public class BillingDetails {

private User user;

...

}

Page 56: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

18 l 하이버네이트 완벽 가이드

반면 외래키 연관 관계에는 방향성이 없다. 방향성을 이용한 객체간 탐색(navigation)은 관계형

데이터 모델에서는 아무런 의미가 없다. 그 이유는 사용자가 임의로 테이블 조인(table join)이나

프로젝션 등으로 데이터의 연관 관계를 만들어 낼 수 있기 때문이다. 진짜 다루기 힘든 문제는 완

전히 개방적인 데이터 모델(데이터를 이용하는 애플리케이션과 독립된)과 애플리케이션에 종속

적인 탐색 모델(이러한 특정 애플리케이션에 필요한 연관 관계를 제한적으로 바라보는)을 연계

하는 것이다.

단지 자바 클래스만 살펴봐서는 단방향 연관 관계의 다중성(multiplicity)을 알아내기 어렵다.

자바에서는 다중성이 다대다(many-to-many)인 연관 관계를 맺을 수 있다. 이를테면, 다음과 같

이 작성할 수 있다.

public class User {

private Set billingDetails;

...

}

public class BillingDetails {

private Set users;

...

}

반면 데이터베이스 테이블의 연관 관계는 항상 일대다(one-to-many) 또는 일대일(one-to-

one)이다. 외래키 정의를 살펴보면 다중성을 즉시 파악할 수 있다. 일대다 연관 관계에 있는

BILLING_DETAILS 테이블의 외래키 선언은 다음과 같다.

USER_ID bigint foreign key references USERS

일대일 연관 관계는 다음과 같이 표현한다.

USER_ID bigint unique foreign key references USERS

BILLING_DETAILS_ID bigint primary key foreign key references USERS

만약 관계형 데이터베이스에서 다대다 연관 관계를 표현하고 싶다면 연결 테이블(link table)이

라고 하는 새로운 테이블을 도입해야 한다. 이 테이블은 도메인 모델에는 나타나지 않는다. 예를

들어 사용자와 지불 정보를 다대다 연관 관계로 간주하면 연결 테이블은 다음과 같이 정의한다.

create table USER_BILLING_DETAILS (

USER_ID bigint foreign key references USERS,

BILLING_DETAILS_ID bigint foreign key references BILLING_DETAILS,

PRIMARY KEY (USER_ID, BILLING_DETAILS_ID)

)

Page 57: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 19

더 자세한 연관 관계나 컬렉션 매핑은 6장과 7장에서 다루겠다.

지금까지 살펴본 문제는 주로 구조에 관한 것이다. 시스템을 정적인 관점에서 바라보면 이러한

문제를 볼 수 있다. 하지만 객체 영속화에 관한 복잡한 문제는 대부분 동적인 문제다. 그와 같은

문제는 연관 관계와 관련되어 있으며, 해결책에 관해서는 1.1.4절, "객체지향 애플리케이션에서

의 영속화"에서 테이블 조인과 객체망(object network) 탐색 사이의 차이점을 비교해 보았을 때 이

미 힌트를 찾을 수 있었다. 이제 이 중요한 불일치 문제에 관해 좀 더 깊이 있게 논의해 보자.

1.2.5 데이터 탐색 문제

자바에서 데이터에 접근하는 방법과 관계형 데이터베이스에서 데이터에 접근하는 방법은

근본적으로 다르다. 자바에서 사용자 지불 정보에 접근하려면 aUser.getBillingDetails().

getAccountNumber() 같은 방법을 사용한다. 이러한 방법은 객체지향 데이터에 접근하는 자연

스러운 방법이며, 보통 객체망을 통한 순회라고 설명하기도 한다. 이 방식으로는 인스턴스 간의

객체 참조를 따라 한 객체에서 다른 객체로 이동한다. 아쉽게도 이 방법은 SQL 데이터베이스에

서 데이터를 가져올 때는 효율적이지 않다.

데이터에 접근하는 코드의 성능을 향상시킬 수 있는 가장 중요한 방법은 데이터베이스로 전해

지는 요청 수를 최소화하는 것이다. 이를 실천하는 가장 분명한 방법은 SQL 쿼리의 수를 줄이는

것이다. (물론 그 다음 단계로 더 복잡한 방법도 있다.)

때문에 SQL을 사용하여 관계형 데이터에 접근하는 효율적인 방법은 대상 테이블 간의 조인을

사용하는 것이다. 데이터를 조회할 때 조인의 대상이 되는 테이블의 수는 메모리 상에서 탐색이

가능한 객체망의 깊이(depth)에 따라 결정된다. 예를 들어 User 정보는 필요하지만 해당 User의

지불 정보는 필요하지 않다면 다음과 같은 간단한 쿼리를 작성하면 된다.

select * from USERS u where u.USER_ID = 123

반면 User 정보를 가져올 때 연관 관계에 있는 BillingDetails 인스턴스(사용자의 신용카드 사

용 내역이라 하자)에 순차적으로 접근하고 싶다면 위의 쿼리와는 조금 다른, 아래와 같은 쿼리를

작성하면 된다.

select *

from USERS u

left outer join BILLING_DETAILS bd on bd.USER_ID = u.USER_ID

where u.USER_ID = 123

Page 58: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

20 l 하이버네이트 완벽 가이드

보다시피 조인을 효율적으로 활용하려면 객체망에 대해 잘 알아야 하며, 최초로 User를 조회

할 때 어떤 식으로 접근할 것인지에 대한 계획을 세워야 한다. 그리고 이런 계획은 객체망을 탐색

하기 전에 세워야 한다.

반면 어떤 객체 영속화 솔루션은 연관된 객체의 데이터를 그 객체에 처음 접근하는 시점에 가

져오는 기능을 제공하기도 한다. 하지만 관계형 데이터베이스에서 이러한 방식으로 데이터를 가

져오는 것은 근본적으로 비효율적인 방법이다. 그 이유는 위와 같은 방법은 접근하는 객체망의

각 노드나 컬렉션에 대해 각각 한 문장씩 실행해야 하기 때문인데, 이런 방법은 n+1 조회 문제를

야기한다.

자바와 관계형 데이터베이스에서 객체에 접근하는 방법의 불일치는 자바 애플리케이션에

서 일어나는 성능 문제의 가장 공통적인 원인일 것이다. 너무 빈번하게 데이터를 가져오는 것

과 불필요한 정보까지 메모리로 가져올 정도로 너무 큰 규모로 데이터를 가져오는 것 사이에서

고민에 빠지게 된다. 하지만 셀 수 없이 많은 책과 잡지의 기사에서 문자열(String)을 연결할 때

StringBu�er를 사용하라는 조언을 얻을 수는 있지만 n+1 선택 문제를 피할 수 있는 전략을 다

루는 글은 찾아볼 수가 없다. 다행히도 하이버네이트는 데이터베이스에서 객체망을 효율적이고

투명하게 가져와 애플리케이션이 사용하게끔 하는 정교한 기능을 제공한다. 이 기능에 대해서는

13, 14, 15장에서 논의하겠다.

1.2.6 패러다임 불일치에 따르는 비용

앞서 여러 객체/관계형 불일치 문제를 알아 봤는데 (여러분도 이미 경험을 통해 알고 있을지도 모

르지만) 그러한 문제에 대한 해결책을 찾는 것은 굉장히 (시간과 노력을 요하는) 소모적인 일이

다. 종종 이 비용은 과소평가되기도 하는데, 우리는 바로 이 점이 많은 소프트웨어 프로젝트가

실패하는 주 원인이라 생각한다. 경험상(우리와 이야기를 나눠본 다른 개발자를 통해 자주 확인

한 바에 따르면) 자바 애플리케이션에서 30%에 달하는 소스 코드가 지루한 SQL/JDBC를 다루

고 손수 객체/관계형 패러다임 불일치를 처리하는 데 쓰인다. 이런 노력에도 불구하고 결과는 그

렇게 좋지 않다. 결국 프로젝트의 데이터베이스 추상화 계층이 복잡해지고 유연함을 갖추지 못

해서 거의 실패 직전에 다다른 프로젝트를 본 적이 있다. 또한 자바 개발자(와 DBA)가 새로 시작

하는 프로젝트에서 사용할 영속화 전략에 대한 설계 결정을 내릴 때 금방 자신감을 잃는 모습도

본 적이 있다.

모델링은 가장 비용이 많이 드는 분야 중 하나다. 관계형 모델이나 도메인 모델은 둘 모두 동일

한 비즈니스 엔티티를 대상으로 하지만 순수 객체지향주의자는 경험이 많은 관계형 데이터 모델

Page 59: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 21

러와는 다른 방법으로 비즈니스 엔티티를 모델링한다. 이 문제에 대한 일반적인 해결책은 도메인

모델과 구현한 클래스를 변형해 가면서 SQL 데이터베이스 스키마에 맞추는 것이다. (이는 데이

터 독립성 원칙을 따르는 것으로 분명 장기적으로 안전한 방법이다.)

이렇게 하면 문제는 성공적으로 해결할 수 있겠지만 객체지향의 장점을 포기해야 한다. 여기서

관계형 모델링은 관계형 이론에 근거를 두고 있음을 다시 한 번 상기하기 바란다. 객체지향은 이

론적으로 엄격한 수학적 정의나 이론적인 체계는 없다. 즉, 두 패러다임 사이의 차이를 어떻게 이

을지 설명해주는 수식 같은 것을 기대할 수 없으며, 현재로서는 둘 사이를 이어줄 어떠한 우아한

방법도 없다. (자바와 SQL을 모두 사용하지 않고, 처음부터 시작하는 것은 좋은 방법이 아니다.)

더 많은 비용을 들게 하는 생산성의 악화와 유연성 없는 시스템이 만들어지는 것이 도메인 모

델링의 불일치 때문만은 아니다. 더 중요한 원인은 JDBC API 자체에 있다. JDBC나 SQL은 SQL

데이터베이스에서 데이터를 옮길 때 문장 중심(statement-oriented), 즉 명령중심적인(command-

oriented)의 접근법을 사용한다. 조회를 하거나 데이터를 다룰 때 그와 관련된 테이블이나 열은

최소한 세 번(insert, update, select)은 기술해야 하고, 이것은 설계와 구현에 그만큼 시간이 늘어

난다는 것을 의미한다. 또한 모든 SQL 데이터베이스마다 각기 다른 방언들도 위와 같은 상황을

더 좋게 만들지는 못한다.

객체 영속화에 대한 이해를 마무리하고 적용 가능한 솔루션을 살펴보기 전에 전형적인 애플

리케이션 설계에서 영속화 계층(persistence layer)의 역할과 애플리케이션 아키텍처(application

architecture)에 대해 논의할 필요가 있다.

1.3 영속화 계층과 대안

중간 또는 대규모 애플리케이션에서는 관심사에 따라 클래스를 구성하는 것이 일반적이다. 영속

화는 그러한 관심사 중 하나이며, 이 밖에도 표현(presentation), 작업흐름(work�ow), 비즈니스 로

직(business logic) 1 과 같은 관심사가 있다. 전형적인 객체지향 아키텍처에서는 위와 같은 관심사

를 나타내는 코드 계층을 포함한다. 이처럼 계층화된 시스템 구조에서 영속화를 담당하는 모든

클래스와 컴포넌트를 묶어 영속화 계층으로 나누는 것은 평범하면서도 확실히 좋은 방법이다.

이 절에서는 이 같은 유형의 아키텍처 계층을 살펴보고 왜 그런 것들을 사용해야 하는지 알아

보겠다. 그러고 나서 논의하고자 하는 영속화 계층을 구현하는 방법을 집중적으로 살펴보자.

1  (옮긴이) 횡단 관심사(cross-cutting concerns)라는 것도 있다. 횡단 관심사는 일반적으로 프레임워크 코드로 구현하며, 전형적

인 횡단 관심사로는 로깅(logging), 권한 부여(authorization), 트랜잭션 경계 설정(transaction demarcation) 등이 있다.

Page 60: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

22 l 하이버네이트 완벽 가이드

1.3.1 계층형 아키텍처

계층형 아키텍처(layered architecture)에서는 한 관심사의 구현 방법이 바뀌더라도 다른 계층의

코드에 심각한 영향을 주지 않게끔 다양한 관심사를 구현하는 코드 사이에 인터페이스를 정의

한다. 또한 계층화(layering)는 계층 간의 의존성을 다음과 같이 규정하고 있다.

계층은 위에서 아래로 의사소통한다. 한 계층은 바로 아래에 있는 계층에만 직접적으로 ▒

의존한다.

각 계층은 바로 아래에 있는 계층을 제외한 다른 계층의 존재를 알아서는 안 된다. ▒

각 시스템은 관심사를 각기 다른 형태로 묶기 때문에 시스템마다 서로 다른 계층을 정의한다.

고수준 애플리케이션 아키텍처는 세 개의 계층으로 구성돼 있다. 이는 그림 1.4에 보이는 표현,

비즈니스 로직, 영속화 계층이다.

아래 다이어그램에 니온 계층과 속성을 좀 더 자세히 살펴보자.

표현 계층

비즈니스 계층

영속화 계층

데이터베이스

인터셉터,유틸리티,

도우미 클래스

그림 1.4 | 영속화 계층은 계층형 아키텍처에서 기반이 되는 계층이다.

▒ 표현 계층: 사용자 인터페이스 로직은 최상단에 위치한다. 표현 계층의 핵심 역할은 페이

지나 화면 이동을 담당하는 것이다.

▒ 비즈니스 계층: 이 계층의 정확한 형태는 애플리케이션에 따라 다양하다. 비즈니스 계층

의 역할은 사용자가 문제 영역으로 인식하는 시스템 요구 사항이나 비즈니스 규칙을 구

현하는 것이다. 이 계층은 어떤 비즈니스 로직을 언제 호출해야 할지 알고 있는 코드인, 일

종의 제어 컴포넌트를 포함한다. 어떤 시스템에서는 이 계층에 비즈니스 도메인 엔티티를

표현하는 별도의 클래스를 두기도 하고, 다른 영속화 계층에서 정의한 모델을 재사용하

Page 61: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 23

기도 한다. 이 문제에 대해서는 3장에서 다시 다룬다.

▒ 영속화 계층: 영속화 계층은 하나 또는 여러 개의 데이터 저장소로부터 데이터를 가져오

거나 데이터 저장소로 데이터를 저장하는 일을 담당하는 클래스나 컴포넌트의 그룹이다.

이 계층은 반드시 비즈니스 도메인 엔티티(비록 메타데이터 모델이더라도)를 포함한다.

▒ 데이터베이스: 데이터베이스는 자바 애플리케이션 밖에 존재한다. 데이터베이스는 실제

시스템 상태에 대한 영속적인 표현이다. SQL 데이터베이스를 사용한다면 해당 데이터베

이스에는 관계형 스키마와 저장 프로시저가 포함돼 있을 것이다.

▒ 도우미(Helper) 및 유틸리티 클래스: 모든 애플리케이션은 애플리케이션의 모든 계층에서

사용하는 도우미나 유틸리티 클래스(오류 처리에 필요한 Exception 클래스 등과 같은)를

기본적으로 포함하고 있다. 이러한 기반 요소는 계층을 형성하지 않기 때문에 계층 간 의

존성 규칙을 따르지 않는다.

이제 자바 애플리케이션에서 영속화 계층을 구현하는 다양한 방법에 대해 간략하게 알아보

자. 걱정할 필요는 없다. ORM과 하이버네이트도 곧 살펴볼 것이다. 다른 접근법을 살펴보는 것

만으로도 배울 점이 많다.

1.3.2 SQL과 JDBC를 이용해서 직접 작성한 영속화 계층

자바 영속화에 접근하는 가장 흔한 방법은 애플리케이션 프로그래머들이 직접 SQL과 JDBC를

다루는 것이다. 결국 그렇게 해서 모든 개발자들이 관계형 데이터베이스 관리 시스템에 익숙해졌

다. 개발자들은 SQL을 이해하고 테이블과 외래키를 어떻게 사용해야 하는지 알고 있다. 더 나아

가 언제든 잘 알려져 있고 널리 쓰는 데이터 접근 객체(DAO, data access object) 패턴을 사용하

여 복잡한 JDBC 코드와 이식이 불가능한 SQL을 비즈니스 로직으로부터 감추는 방법을 사용할

수 있다.

DAO 패턴은 훌륭한 패턴이다. 그래서 ORM을 쓰더라도 DAO 패턴을 쓰는 것을 종종 권장하

기도 한다. 하지만 도메인 클래스에 대한 영속화 과정을 직접 코딩한다는 것은 상당히 고된 일이

다. 특히 여러 벤더를 지원하는 SQL을 작성해야 할 때는 더욱 그렇다. 결국 해당 작업은 개발 공

수 중 상당 부분을 차지하게 된다. 더 나아가 요구사항이 바뀌면 직접 작성한 솔루션은 더 많은

주의와 유지보수 노력이 필요하다.

프로젝트의 특별한 요구사항에 맞는 간단한 매핑 프레임워크를 구현해 보는 것은 어떤가? 적

어도 이러한 노력의 결과는 다음 프로젝트에서 재사용할 수 있을 것이다. 많은 개발자들이 이러

Page 62: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

24 l 하이버네이트 완벽 가이드

한 접근법을 취해왔다. 그 결과 오늘날 자체 개발한 객체/관계형 영속화 계층이 상당수 존재한

다. 하지만 이러한 접근법은 권하고 싶지 않다. 이미 훌륭한 솔루션이 여럿 존재한다. 여기엔 상

용 벤더가 판매하는 (엄청나게 비싼) 제품뿐 아니라 무료 라이선스로 쓸 수 있는 오픈소스 프로

젝트도 있다. 우리는 여러분의 비즈니스와 기술 요구사항 모두에 적합한 솔루션을 찾을 수 있으

리라 확신한다. 그러한 솔루션이 제한된 시간 안에 직접 만들어야 하는 솔루션보다 훨씬 더 많은

일을 더 잘 처리해 줄 것이다.

모든 기능을 완전히 갖춘 ORM 솔루션을 개발하기란 수많은 개발자들이 몇 달을 노력해야 될

지도 모르는 일이다. 예를 들면 하이버네이트는 일반적인 애플리케이션 코드보다 훨씬 어려운

약 80,000 줄의 코드로 되어있고 이와 함께 25,000 줄의 단위 테스트 코드로 이루어져 있다. 이

는 분명 여러분이 작성한 애플리케이션 코드보다 더 많은 양일 것이다. 적어도 저자의 경험으로

봤을 때 대규모 프로젝트의 경우에는 중요한 세부 사항들이 간과되기 쉽다. 이미 존재하는 솔루

션이 여러분이 원하는 두세 가지 요구사항을 전부 구현하지는 않았더라도 손수 솔루션을 만드

는 것보다는 더 나은 선택이다. 모든 ORM 소프트웨어는 생산성을 떨어뜨리는 지루하고 반복적

인 공통 사항을 처리해줄 것이다. 특별한 경우라면 손수 코딩하는 것도 무방하다. 물론 그렇게

특이하게 구성된 애플리케이션은 거의 없겠지만 말이다.

1.3.3 객체 직렬화 사용하기

자바에는 영속화 메커니즘인 직렬화(serialization)가 내장돼 있다. 직렬화는 객체망의 스냅샷(애

플리케이션의 상태)을 바이트 스트림(byte stream)으로 출력할 수 있는 기능이다. 그렇게 생성

한 바이트 스트림은 파일이나 데이터베이스에 영속화할 수 있다. 또한 자바의 원격 메서드 호출

(RMI, remote procedure call)에서도 직렬화를 활용하여 복잡한 객체에 대한 값에 의한 전달(pass-

by value)을 가능케 한다. 또 다른 직렬화의 용도는 분산 환경에서 노드 간에 애플리케이션의 상

태를 복제하는 데 있다.

그럼 직렬화를 영속화 계층에서 사용해보는 것은 어떨까? 아쉽게도 직렬화한 상호 연관 객체

망은 오직 전체로서만 접근 가능하다. 즉 전체 스트림을 역직렬화(deserialize)하지 않는 이상 해

당 스트림에서 특정 데이터만 가져오는 것은 불가능하다는 뜻이다. 따라서 바이트 스트림은 임

의 검색이나 상당히 많은 양의 데이터 집합을 집계하는 데는 적합하지 않다. 게다가 단일 객체나

소규모 객체 집합에 독립적으로 접근하거나 수정하는 것도 불가능하다. 고도의 동시성을 제공하

게끔 설계한 시스템이라면 이렇게 매 트랜잭션마다 전체 객체망을 불러오거나 갱신하는 것은 결

코 좋은 방법이 아니다.

Page 63: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 25

현재 기술로는 직렬화를 고도의 동시성을 요구하는 웹 또는 엔터프라이즈 애플리케이션의 영

속화 메커니즘으로 쓰기에는 부적합하다. 그대신 일종의 틈새 시장 격으로 데스크톱 애플리케이

션의 영속화 메커니즘으로는 적합하다.

1.3.4 객체지향 데이터베이스 시스템

자바에서는 객체로 작업하기 때문에 객체 모델을 그대로 유지하면서 데이터베이스에 해당 객체

를 저장할 수 있다면 그것이 가장 이상적인 방법일 것이다. 1990년대 중반, 객체지향 데이터베이

스 시스템이 주목을 받은 적이 있다. 객체지향 데이터베이스 시스템은 네트워크 데이터 모델에

기반했는데, 이 모델은 관계형 데이터 모델이 등장하기 이전 10년 동안 널리 채택되었다. 기본적

인 아이디어는 모든 객체 포인터(pointer)와 노드(node)를 포함해서 객체망을 저장했다가 나중에

동일한 형태로 메모리에서 재생성하는 것이었다. 그런 작업은 다양한 메타데이터와 환경 설정을

통해 최적화할 수 있었다.

객체지향 데이터베이스 관리 시스템(OODBMS, object-oriented database management system)은

외부의 데이터 저장소라기보다는 애플리케이션 환경의 확장 기능 같았다. OODBMS는 다중 티

어(multitiered) 구현이라는 특징이 있다. 즉 뒷단의 데이터 저장소, 객체 캐시, 클라이언트 애플리

케이션이 강하게 결합되어 있고 서로 OODBMS 전용 네트워크 프로토콜을 이용하여 상호작용

하는 것이다. 객체 노드는 메모리 페이지 상에서 유지되며, 데이터 저장소로부터 또는 데이터 저

장소로 전송된다.

객체지향 데이터베이스 개발은 프로그래밍 언어에 영속화 능력을 추가하는 호스트 언어 연동

(host language binding)을 하향식으로 정의하는 것으로 시작한다. 따라서 객체형 데이터베이스

는 객체지향 애플리케이션 환경에 자연스럽게 통합할 수 있다. 이것이 오늘날 중계 역할을 하는

언어(SQL)을 사용해서 데이터베이스와 상호작용하고 특정 애플리케이션으로부터 데이터를 독

립시키는 것이 주요한 관심사인 관계형 데이터베이스 모델과의 차이점이다.

객체지향 데이터베이스에 관해 더 알고 싶다면 "An Introduction to Database Systems(Date,

2003)"를 참고하길 권한다.

왜 객체지향 데이터베이스 기술이 널리 쓰이지 않았는가에 대해서는 자세히 들여다보지 않겠

다. 객체형 데이터베이스는 현재 널리 사용되고 있지 않으며 가까운 미래에도 그럴 것 같기 때문

이다. 현재 정치적인 현실(이미 정의돼 있는 배포 환경)과 데이터 독립성에 대한 공통적인 요구사

항을 생각했을 때 압도적으로 많은 수의 개발자들은 관계형 데이터베이스 기술을 접할 기회가

더 많으리라 확신한다.

Page 64: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

26 l 하이버네이트 완벽 가이드

1.3.5 그 밖의 대안

물론 다른 종류의 영속화 계층도 있다. XML 영속화는 객체 직렬화의 또 다른 변종이다. 이 방법

은 표준화된 도구 인터페이스를 통해 데이터에 쉽게 접근하는 것을 가능하게 해줌으로써 바이

트 스트림 직렬화의 제약 사항 중 일부를 해결해 준다. 하지만 데이터를 XML로 관리하는 것도

객체/상속 불일치를 야기한다. 또한 XML 자체로는 별 다른 장점을 얻을 수 없다. 왜냐하면 단순

히 또 다른 텍스트 파일 형식일 뿐 데이터 관리에 필요한 기능을 원래부터 갖고 있진 않기 때문

이다. 저장 프로시저(심지어 때로는 자바로 작성하는)를 사용하여 문제를 데이터베이스 티어로

옮길 수 있다. 소위 객체 관계형 데이터베이스라고 하는 솔루션도 판매되고 있지만 단지 더 복잡

한 데이터 타입 시스템을 제공할 뿐 문제를 전부 해결하진 못한다(그리고 용어도 좀 더 혼란스럽

다). 이와 비슷한 솔루션들이 굉장히 많을 것으로 생각하지만 그것들 중 어떤 것도 빠른 시일 내

에 호응을 얻기는 어려울 것으로 보인다.

정치·경제적인 제약(SQL 데이터베이스에 오랫동안 투자해 온)과 데이터 독립성, 그리고 소중

한 레거시 데이터에 접근할 수 있어야 한다는 요구사항은 다른 접근법의 등장을 요구한다. ORM

이 바로 이런 문제에 가장 실용적인 해결책일지도 모른다.

1.4 객체/관계형 매핑

지금까지 객체 영속화의 여러 대안 기법을 살펴보았다. 이제 최선의 선택이라고 생각하는 해결책

을 소개하겠다. 바로 우리가 하이버네이트를 통해 이용하고 있는 ORM이다. 오랜 역사에도 불구

하고(첫 번째 연구 논문이 1980년대 말에 발간되었다.), 개발자들은 ORM이라는 용어를 다양한

의미로 사용한다. 어떤 사람들은 객체 관계형 매핑(object relational mapping)으로 부르기도 하고,

또 어떤 사람들은 간단한 객체 매핑(object mapping)이라는 표현을 선호하기도 한다. 여기서는 객

체/관계형 매핑(object/relational mapping)과 그것의 약어인 ORM이라는 표현만 쓰겠다. 객체와

관계형 사이의 슬래시는 두 패러다임 사이에 발생하는 불일치 문제를 강조한 것이다.

이 절에서는 ORM이란 무엇인가를 살펴보겠다. 그리고 좋은 ORM 솔루션이 반드시 해결해야

하는 문제들을 나열하겠다. 마지막으로 ORM이 제공하는 일반적인 장점과 이러한 해결책을 권

장하는 이유를 논의하겠다.

Page 65: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 27

1.4.1 ORM이란 무엇인가?

간략하게 이야기하자면, 객체/관계형 매핑은 객체와 데이터베이스 사이의 매핑을 정의한 메타데

이터를 이용하여 자바 애플리케이션 내의 객체를 관계형 데이터베이스의 테이블로 자동으로(그

리고 투명하게) 영속화하는 것이다.

본질적으로, ORM은 데이터를 어떤 표현방식에서 다른 표현방식으로 (그리고 그 반대로도)

변환함으로써 동작한다. 이 때문에 약간의 성능 저하가 야기될지도 모른다. 하지만 ORM을 미들

웨어로 구현한다면 직접 영속화 계층을 코딩할 때는 얻기 힘든 최적화를 할 기회가 많이 생긴다.

변환 작업에 필요한 메타데이터를 만들고 관리하는 것이 개발 기간에 부담이 될 수도 있지만 직

접 만든 솔루션 유지보수에 필요한 비용보다는 적을 것이다. (그리고 심지어 객체 데이터베이스

도 상당한 양의 메타데이터를 필요로 한다.)

FAQ? ORM은 비지오(Visio) 플러그인 아닌가요? ORM이란 약어는 object role modeling

을 뜻하기도 하는데 이 용어는 객체/관계형 매핑이라는 개념이 생기기 이전에

나온 개념이다. 이것은 데이터 모델링에서 사용하는 정보 분석 방법을 말하

며, 주로 그래픽 모델링 도구인 마이크로소프트의 Visio가 제공한다. 데이터

베이스 전문가들은 이 용어를 대체재 또는 좀 더 유명한 개체 관계 모델링에

추가적인 도구를 가리킬 때 사용한다. 하지만 자바 개발자와 ORM에 대해 이

야기를 나눈다면 보통 이 단어는 객체/관계형 매핑이라는 뜻이다.

ORM 솔루션은 아래의 네 부분으로 구성돼 있다.

영속 클래스 객체에 대한 기본적인 CRUD를 수행하는 API ▒

클래스 자체와 해당 클래스의 프로퍼티를 참조하는 쿼리를 작성할 수 있는 언어나 API ▒

▒ 매핑 메타데이터 작성을 위한 기반 시설

ORM 구현이 트랜잭션을 적용한 객체와 상호작용하여 ▒ 변경 감지(dirty checking), 지연 연

관 관계 페치(lazy association fetching) 등의 최적화를 수행하도록 돕는 기법

여기서는 ORM이 메타데이터 기반의 설정으로부터 SQL이 자동으로 생성되는 모든 영속화

계층을 포함할 때 완전한(full) ORM이라는 용어를 쓴다. 객체/관계형 매핑 문제를 개발자들이

직접 JDBC로 SQL을 하드 코딩해서 해결하는 영속화 계층은 완전한 ORM에 해당하지 않는다.

ORM을 사용하면 애플리케이션은 ORM API와 도메인 모델 클래스와 상호작용하며, 기반하는

SQL/JDBC로부터 추상화한 형태가 된다. 또한 ORM 엔진은 그것의 특징이나 구현 방법에 따라

Page 66: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

28 l 하이버네이트 완벽 가이드

낙관적 잠금(optimistic locking)과 캐싱(caching) 같은 사안을 책임지기도 하여 애플리케이션을

그러한 관심사로부터 해방시켜준다.

ORM을 구현하는 다양한 방법을 살펴보자. ORM 분야에서 활동하는 개발자인 마크 퍼셀

(Mark Fussel, 1997)은 ORM의 품질을 아래의 네 단계로 정의했다. 여기서는 그가 설명한 내용

을 조금 수정하여 오늘날의 자바 애플리케이션 개발에 맞게 다시 적어 보았다.

순수 관계형

사용자 인터페이스를 포함한 전체 애플리케이션이 관계형 모델과 SQL 기반의 관계형 연산으로

구성돼 있다. 이 접근법은 대규모 시스템에는 부적합하지만 코드 재사용 수준이 낮아도 크게 상

관 없는 작은 규모의 애플리케이션에는 탁월한 해결책이 될 수 있다. 직접적인 SQL은 모든 측면

에서 세밀하게 조정할 수 있지만, 이식성이나 유지보수성이 떨어진다는 단점이 있고, 특히 장기적

으로는 더욱 그러하다. 이런 부류에 속하는 애플리케이션은 종종 저장 프로시저를 과도하게 써

서 비즈니스 계층에서 해야 할 일을 데이터베이스로 옮겨 놓기도 한다.

가벼운 객체 매핑

엔티티를 클래스로 표현하고 이 클래스를 직접 관계형 테이블로 매핑한다. 손수 작성한 SQL/

JDBC 코드는 잘 알려진 디자인 패턴을 활용하여 비즈니스 계층으로부터 분리하여 감춰진다. 이

접근법은 굉장히 널리 퍼져 있으며 엔티티 개수가 적거나 애플리케이션에 일반화된, 메타데이터

기반의 데이터 모델이 있을 때 알맞은 접근법이다. 이런 종류의 애플리케이션에도 저장 프로시저

가 있을 수 있다.

중간 정도의 객체 매핑

애플리케이션을 객체 모델을 중심으로 설계한다. SQL은 빌드 시에 코드 생성 도구를 이용해 생

성되거나, 프레임워크 코드에 의해 실행 시에 생성된다. 객체 간의 연관 관계는 영속화 메커니즘

이 지원하며, 쿼리를 객체지향적인 표현 언어를 사용하여 기술할 수 있다. 객체가 영속화 계층에

의해 캐싱된다. 여러 훌륭한 ORM 제품과 직접 만든 영속화 계층이라면 최소한 이 정도 수준의

기능을 제공한다. 이 접근법은 트랜잭션이 복잡하고, 특히 여러 데이터베이스 간의 이식성이 중

요한 중소규모의 애플리케이션에 적합하다. 이런 애플리케이션은 보통 저장 프로시저를 사용하

지 않는다.

Page 67: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 29

완전한 객체 매핑

완전한 객체 매핑은 복잡한 객체 모델링을 지원한다. 즉 컴포지션(composit ion), 상속

(inheritance), 다형성(polymorphism), 접근성을 따른 영속화(persistence by reachability)를 지원한

다. 이러한 영속화 계층은 투명한 영속화(transparent persistence)라는 특징이 있다. 이는 영속 클

래스가 어떤 특정 기본 클래스를 상속하거나 특정 인터페이스를 구현하지 않아도 된다는 뜻이

다. 또한 효율적인 페치 전략(lazy, eager, prefetching)과 캐싱 전략이 애플리케이션에 투명하게

구현돼 있다. 이 정도 수준의 기능은 손수 개발한 영속화 계층으로는 달성하기가 상당히 힘들다.

개발 기간만 수년에 맞먹는다. 많은 상용 도구와 오픈소스 자바 ORM 도구가 이런 수준의 품질

을 달성한 바 있다.

바로 이 수준이 이 책에서 말하는 ORM의 정의다. 자, 그럼 이제 완전한 수준의 객체 매핑을 달

성한 도구가 해결해줄 문제를 살펴보자.

1.4.2 일반적인 ORM 문제

아래 목록은 ORM 문제라고 부르는 것을 나열한 것이다. 자바 환경에서는 완전한 객체/관계형

매핑이 이러한 기본적인 질문들을 해결해준다. 특정 ORM 도구가 (이를테면, 적극적인 캐싱 같

은) 추가적인 기능을 제공할지도 모르지만 여기서는 개념적인 문제와 객체/관계형 매핑에 해당

하는 질문만 나열했다.

영속 클래스는 어떻게 생겼는가? 영속화 도구는 얼마나 투명하게 동작하는가? 비즈니스 1.

도메인을 나타내는 클래스에 특정한 프로그래밍 모델이나 규약을 채택해야 하는가?

매핑 메타데이터를 어떤 형태로 정의할 것인가? 객체/관계형 사이의 변형은 전적으로 메2.

타데이터에 의해 이루어진다. 따라서 메타데이터의 형식과 정의가 중요하다. 그렇다면

ORM 도구는 메타데이터를 그래픽 화면을 통해 조작할 수 있게 GUI 인터페이스를 제공

해야 하는가? 아니면 메타데이터를 정의하는 또 다른 좋은 접근법은 없을까?

어떤 방법으로 객체 동일성3. (identity)이나 동등성(equality)을 그와 연관된 데이터베이스

(주키) 식별자와 연관지을 것인가? 어떻게 특정 클래스의 객체를 특정 테이블의 행으로

매핑할 것인가?

클래스 상속 계층 구조를 어떻게 매핑할 것인가? 이 문제엔 몇 가지 표준 전략이 있다. 다4.

형적인 연관 관계, 추상 클래스, 인터페이스는 어떻게 할 것인가?

Page 68: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

30 l 하이버네이트 완벽 가이드

영속화 로직이 실행 시에 비즈니스 도메인 객체와 어떻게 상호작용하는가? 이 문제는 제5.

네릭 프로그래밍[w1]과 관련이 있는 문제로, 소스 생성, 런타임 리플렉션, 런타임 바이트

코드 생성, 빌드타임 바이트코드 조작을 비롯한 여러 해결 방안이 있다. 이 문제에 대한

해결 방안은 빌드 과정에 영향을 줄지도 모른다(반면 사용자에게는 영향을 주지 않는 것

이 바람직하다).

영속 객체의 생명주기란 무엇인가? 어떤 객체의 생명주기가 연관 관계에 있는 다른 객체6.

의 생명주기에 의존적인가? 객체의 생명주기를 데이터베이스 행의 생명주기로 어떻게 해

석할 것인가?

정렬, 검색, 집계 연산과 관련하여 어떤 기능이 제공되는가? 애플리케이션은 이러한 것들7.

을 메모리에서 처리할 수도 있지만 관계형 기술을 효율적으로 사용하려면 이러한 작업을

데이터베이스에서 수행해야 할 때가 있다.

연관 관계에 있는 데이터를 어떻게 효율적으로 가져올 것인가? 관계형 데이터에 효율적으8.

로 접근하는 방법은 보통 테이블 조인을 사용하는 것이다. 객체지향 애플리케이션은 보통

객체망을 통해 데이터에 접근한다. 이 두 가지 데이터 접근 방법을 사용할 때는 n+1 조회

문제와 (한번의 조회로 너무 많은 데이터를 가져오는) 카테시안 곱(Cartesian product) 문제

를 조심해야 한다.

ORM 아키텍처와 설계 시에 근본적인 제약을 가하는 다음의 두 가지 문제는 모든 데이터 접

근 기술에서 공통적으로 나타나는 문제다.

▒ 트랜잭션과 동시성

▒ 캐시 관리(및 동시성)

보다시피 객체/관계형 매핑 도구는 상당히 많은 문제를 해결할 수 있어야 한다. 이제 ORM의

가치를 살펴보고 ORM 솔루션을 사용하여 얻을 수 있는 혜택을 살펴보겠다.

1.4.3 왜 ORM인가

ORM 구현은 복잡하다. 애플리케이션 서버보다는 덜 복잡하지만 스트러츠나 태피스트리 같은

웹 애플리케이션 프레임워크보다는 훨씬 복잡하다. 왜 우리는 이런 복잡한 물건을 시스템의 기

반 시설로 사용하려는 걸까? 그럴만한 가치가 있는 건가?

이 책의 상당 부분은 이러한 질문에 대한 답을 하는 데 할애하겠지만, 이 절에서는 가장 주목

Page 69: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 31

할 만한 혜택에 대해서만 간략하게 살펴보겠다. 하지만 먼저 ORM이 주는 혜택이 아닌 것부터

간략히 살펴보자.

소위 말하는 ORM의 이점은 성가신 SQL로부터 개발자를 보호해 준다는 것이다. 이러한 관

점은 객체지향 개발자가 SQL이나 관계형 데이터베이스를 잘 이해하지 않아도 되고, 개발자들이

SQL을 어떻게든 불쾌하게 여긴다고 간주한다. 하지만 이와는 반대로 ORM을 사용하려면 자바

개발자도 반드시 관계형 모델링과 SQL에 충분히 익숙해야 한다. ORM은 그런 힘든 방법을 이미

경험해본 개발자들이 사용하게끔 만든 고급 기법에 속한다. 하이버네이트를 효율적으로 사용하

려면 SQL을 보고 해당 문장이 야기할 수 있는 문제와 잠재적인 성능을 이해할 수 있어야 한다.

자 이제 ORM과 하이버네이트가 주는 혜택을 살펴보자.

생산성

영속화와 관련된 코드는 아마도 자바 애플리케이션에서 가장 지루한 코드일 수 있다. 하이버네

이트는 (기대 이상으로) 그러한 썩 내키지 않는 작업을 없애주고 비즈니스 문제에 집중할 수 있게

해준다.

도메인 모델로부터 시작하는 하향식이든 기존의 데이터베이스 스키마로부터 시작하는 상향

식이든, 어떤 애플리케이션 개발 전략을 선호하든 간에 하이버네이트와 적절한 도구를 함께 활

용하면 개발에 소요되는 시간을 크게 줄일 수 있다.

유지보수성

적은 코드 줄 수(LOC, line of code)를 유지할수록 시스템을 쉽게 이해할 수 있다. 왜냐하면 반복

적인 코드보다 비즈니스 로직이 좀 더 명확히 드러나기 때문이다. 더 중요한 점은 코드 줄 수가

적을수록 시스템을 리팩터링하기가 더 쉽다는 것이다. 자동화된 객체/관계형 영속화는 LOC를

상당히 줄여준다. 물론 코드의 줄 수로 애플리케이션의 복잡도를 측정하는 것은 논란의 여지가

있다.

하지만 하이버네이트가 유지보수하기 좋은 다른 이유도 있다. 직접 작성한 영속화 시스템에서

는 관계형 표현과 도메인을 구현한 객체 모델 사이에 필연적으로 어떤 긴장감이 존재한다. 즉, 어

떤 것을 변경하면 다른 쪽도 항상 바꿔야 하기 때문에 한쪽의 표현법을 설계할 때 다른 쪽의 존

재를 수용하기 위한 타협점을 내포하고 있어야 한다. (현장에서는 도메인의 객체 모델을 절충해

야 할 일이 비일비재하다.) ORM은 이 두 모델 사이의 완충지대를 제공하여 자바 쪽에서 더욱

우아한 객체지향을 가능케 하고 각 모델은 상대방의 자잘한 변경사항으로 인해 바뀌지 않아도

된다.

Page 70: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

32 l 하이버네이트 완벽 가이드

성능

직접 만든 영속화 솔루션이 자동화 영속화 솔루션보다 최소한 더 빠르다고 주장하는 이들도 많

다. 이것은 다음과 같은 주장과 다르지 않다. 자바 코드보다 어셈블리 코드가 최소한 더 빠르다.

그리고 직접 작성한 파서(parser)가 YACC나 ANTLR로 생성한 파서보다 역시 더 빠르다. 즉 달리

이야기하면 핵심을 벗어나고 있다는 뜻이다. 이 주장이 내포하고 있는 것은 직접 만든 영속화 시

스템이 최소한 실제 애플리케이션에서도 잘 동작한다는 것이다. 이러한 가정도 직접 만든 영속화

솔루션을 구현하는 데 드는 노력이 자동화된 영속화 솔루션을 도입하는 데 드는 비용과 비슷할

때만 받아들일 수 있다. 진짜로 흥미로운 질문은 시간과 예산까지 고려해 봤을 때 어떠한가이다.

영속화 작업에는 여러 가지 최적화가 가능하다. 어떤 최적화 방법(쿼리 힌트와 같은)은 SQL/

JDBC를 사용하여 직접 작성하는 편이 훨씬 더 쉽다. 하지만 대부분의 최적화는 자동화된 ORM

을 쓰는 것이 훨씬 쉽다. 시간적인 제약이 있는 프로젝트에서는 손수 작성한 영속화 솔루션을 써

서 몇 가지 최적화를 꾀할 수 있다. 하지만 하이버네이트는 항상 그보다 많은 최적화를 가능하게

해준다. 게다가 자동화된 영속화 솔루션은 개발자의 생산성을 높여주기 때문에 얼마 남지 않은

병목 현상을 처리하는 데 더 많은 시간을 투자할 수 있다.

마지막으로 ORM 소프트웨어를 구현하는 사람은 성능 최적화 방법을 찾기 위해 여러분보다

더 많은 시간을 들인다. 예를 들어, 혹시 PreparedStatement 인스턴스의 풀링(pooling) 결과가

DB2 JDBC에서는 높은 성능 향상을 가져오지만, 반대로 InterBase JDBC 드라이버에서는 문제

가 생긴다는 사실을 알고 있는가? 테이블에서 변경 사항이 있는 열만 변경하는 것이 특정 데이터

베이스에서는 더 빠르게 처리될 수 있지만 다른 데이터베이스에서는 더 느려질 수도 있다는 점을

알고 있는가? 여러분이 직접 만든 솔루션에서는 이런 다양한 전략의 효과를 시험하는 것이 얼마

나 쉬운가?

벤더 독립성

ORM은 기반 SQL 데이터베이스나 SQL 방언(dialect)으로부터 애플리케이션을 추상화한다.

ORM이 다양한 데이터베이스를 지원한다면(실제로도 그렇게 하고 있다) 이는 애플리케이션이

일정 수준의 이식성을 확보할 수 있다는 뜻이다. 그렇다고 한번 작성해서 어디서든 사용할 수 있

다(write-once/run-anywhere)는 건 아니다. 왜냐하면 데이터베이스의 능력과 성능은 각기 다르며,

완벽한 이식성을 보장하려면 더욱 강력한 플랫폼의 장점을 희생해야 하기 때문이다. 그럼에도

불구하고 ORM을 이용하면 플랫폼 이식성을 갖춘 애플리케이션을 개발하기가 훨씬 쉽다. 심지

어 플랫폼 이식성이 필요 없다고 하더라도 ORM은 벤더에 종속되어 발생할 수 있는 위험 요소를

제거해준다.

Page 71: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 33

게다가 데이터베이스 독립성은 개발 시에는 개발자들이 로컬에서 가벼운 데이터베이스를 사

용하고 실제 환경에서는 다른 데이터베이스로 배포하는 것을 가능케 한다.

언젠가는 하나의 ORM 제품을 선택해야 할 날이 올 것이다. 적절한 결정을 내리려면 이용 가

능한 소프트웨어 모듈과 표준으로 어떤 것이 있는지 알아둘 필요가 있다.

1.4.4 하이버네이트, EJB3, JPA 소개

하이버네이트는 완전한 객체/관계형 매핑 도구로서 앞서 나열한 ORM이 가져다 주는 이점을 모

두 제공한다. 하이버네이트에서는 하이버네이트 개발자가 설계한 고유 API를 쓴다. 쿼리 인터페

이스와 쿼리 언어, 객체/관계형 매핑 메타데이터를 정의하는 방법도 마찬가지다.

처음으로 하이버네이트를 사용하는 프로젝트를 시작하기에 앞서 EJB 3.0 표준과 그것의 하위

명세(subspeci�cation)에 해당하는 자바 퍼시스턴스도 고려해봐야 한다. 과거를 거슬러 올라가 어

떻게 이러한 표준이 나오게 됐는지 살펴보자.

많은 자바 개발자들은 EJB 2.1의 엔티티 빈을 영속화 계층을 구현하는 기술 중 하나로 여겼다.

모든 EJB 프로그래밍과 영속화 모델은 현장에 폭넓게 적용됐으며, 그 결과 그것이 (현재 자바 EE

라고 부르는) J2EE 성공의 주요 요인이 되었다.

하지만 최근 몇 년간 개발자 커뮤니티에서 EJB를(특히 엔티티 빈과 영속화와 관련해서) 비판

하는 사람들의 목소리가 점차 커짐에 따라 많은 단체에서 EJB 표준을 개선해야 한다는 사실을

깨닫게 되었다. J2EE를 이끌어 가는 썬은 정비가 필요하다는 것을 인식하고 2003년 초 EJB 단순

화를 목표로 하는 새로운 자바 명세 요구서(JSR, Java speci�cation request)을 시작했다. 이 새

로운 JSR인 엔터프라이즈 자바빈 3.0(JSR 220)은 많은 관심을 받았다. 일찌감치 하이버네이트 팀

의 개발자들이 전문자 그룹에 합류해서 새로운 명세를 구성하는 데 일조했다. 자바 업계의 여러

대규모 및 소규모 벤더들도 크게 기여했다. 새로운 명세에 있어 중요한 결정 사항은 기존의 성공

적인 제품과 프로젝트의 개념과 아이디어를 취함과 동시에 현업에서 사용할 수 있는 것을 명시

하고 표준화하는 것이었다. 따라서 성공적인 데이터 영속화 솔루션으로서 하이버네이트는 새로

운 표준의 영속화 부문에서 중요한 역할을 수행하고 있다. 그렇다면 하이버네이트와 EJB 3은 정

확히 어떤 관계일까? 그리고 자바 퍼시스턴스란 무엇일까?

표준 이해하기

먼저, 명세서와 제품을 비교하는 것은 (불가능하지 않다면) 어려운 일이다. 좀 더 정확히 질문한

다면 다음과 같다. "하이버네이트가 EJB 3.0 명세를 구현했는가? 그로 인해 프로젝트는 어떤 영

향을 받을 것인가? 반드시 둘 중 하나를 사용해야 하는가?"

Page 72: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

34 l 하이버네이트 완벽 가이드

새로운 EJB 3.0 명세는 여러 부분으로 구성돼 있다. 첫 번째 부분은 새로운 EJB 프로그래

밍 모델로 세션 빈(session bean)과 메시지 구동형 빈(message-driven bean), 그리고 배포 규칙

(deployment rule) 등이 있다. 명세서의 두 번째 부분은 영속화에 관한 내용만 다루며 엔티티, 객

체/관계형 매핑 메타데이터, 영속성 관리 인터페이스와 쿼리 언어를 다룬다. 이 두 번째 부분

을 자바 퍼시스턴스 API, 즉 JPA(Java Persistence API)라 부르는 이유는 인터페이스가 javax.

persistence 패키지에 들어 있기 때문이다. 우리는 이 용어를 책 전반에 걸쳐 사용할 것이다.

EJB 3.0 제품에서도 이렇게 구분한다. 몇몇은 완전한 EJB 3.0 컨테이너를 구현하여 명세 전체

를 지원하는 반면, 다른 몇몇 제품은 자바 영속화 기술 부분만 구현한다. 새로운 표준에는 아래

의 두 가지 중요한 설계 원칙이 고려돼 있다.

JPA 엔진은 동적으로 교체 가능(pluggable)해야 한다. 즉, 계속해서 동일한 EJB 3.0 컨테 ▒

이너나 자바 EE 5.0 애플리케이션 서버를 사용하고 싶더라도 원한다면 한 제품을 빼고 그

것을 다른 것으로 교체할 수 있어야 한다는 것이다.

JPA 엔진은 EJB 3.0(또는 다른) 런타임 환경을 벗어나서 컨테이너 없이 일반 표준 자바 환 ▒

경에서도 실행될 수 있어야 한다.

이 두 설계 원칙은 개발자와 아키텍트에게 더욱 다양한 선택권을 부여해 준다. 따라서 여러 제

품 사이에서 경쟁을 유도하고, 그 결과 제품의 전반적인 품질이 향상된다. 물론 실제 제품은 (성

능 튜닝을 목적으로, 또는 벤더가 특정 분야의 문제 영역에 집중하기 때문에) 표준에 포함되지

않은 특화된 확장 기능을 제공하기도 한다.

하이버네이트는 자바 퍼시스턴스를 구현하며, JPA 엔진은 동적으로 교체가 가능하기 때문에

새롭고 흥미로운 소프트웨어 조합이 가능하다. 사용자는 다양한 하이버네이트 소프트웨어 모

듈을 선택할 수 있고 그것들을 프로젝트에서 쓰는 기술과 비즈니스 요구사항에 맞게 조합할 수

있다.

하이버네이트 코어

하이버네이트 코어(Hibernate Core)는 하이버네이트 3.2.x나 하이버네이트로 알려져 있다. 하이

버네이트 코어는 고유 API와 XML 파일에 저장되는 매핑 메타데이터와 함께 기본 영속화 서비

스에 해당한다. 그리고 HQL(SQL과 거의 비슷함)이라는 쿼리 언어를 비롯해서 프로그램 방식의

쿼리 인터페이스인 Criteria와 Example 쿼리를 포함하고 있다. 하이버네이트 코어는 다른 모든

모듈이 구축되는 실질적인 기반이자 플랫폼으로서 모든 부문에 걸쳐 수백 가지에 이르는 옵션과

기능을 제공해 준다.

Page 73: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 35

하이버네이트 코어는 다른 프레임워크 또는 특정한 모든 JDK 실행 환경과는 별개로 독립적으

로 사용할 수 있다. 하이버네이트 코어는 모든 자바 EE/J2EE 애플리케이션 서버, 스윙(Swing) 애

플리케이션, 간단한 서블릿 컨테이너 등에서 사용할 수 있다. 하이버네이트에서 쓸 데이터 소스

를 설정할 수만 있다면 바로 쓸 수 있다. (영속화 계층에 있는) 애플리케이션 코드는 하이버네이

트 API와 쿼리를 사용할 것이며, 매핑 메타데이터는 하이버네이트 고유의 XML 파일로 작성될

것이다.

이 책에서는 우선적으로 하이버네이트 고유의 API와 쿼리, XML 매핑 파일을 살펴보겠다. 그

리고 모든 코드 예제에서 이것들을 먼저 설명하겠다. 이는 하이버네이트 코어에 탑재된 기능이,

사용 가능한 모든 옵션에 대한 상위 집합(superset)에 해당하기 때문이다

하이버네이트 애노테이션

JDK 5.0부터 애플리케이션 메타데이터를 정의하는 새로운 방법을 사용할 수 있게 되었다. 이 새

로운 방법이란 타입 안전이 보장되는(type-safe) 애노테이션(annotation)으로서 자바 소스 코드에

직접 포함된다. 많은 하이버네이트 사용자는 자바독(Javadoc) 메타데이터 속성과 컴파일 시의 전

처리(하이버네이트의 경우에는 XML 매핑 파일을 생성)를 지원하는 XDoclet 소프트웨어로 인

해 이미 이런 개념에 익숙할 것이다.

하이버네이트 코어를 기반으로 하는 하이버네이트 애노테이션 패키지를 사용하면 기존의 하

이버네이트 XML 매핑 파일을 타입에 안전한 JDK 5.0 메타데이터로 교체할 수 있다. 하이버네이

트 XML 매핑 파일과 하나하나 비교해 보면 매핑 애노테이션의 문법과 의미에 금방 익숙해질 것

이다. 하지만 기본 애노테이션은 하이버네이트에서만 쓰는 것은 아니다.

JPA 명세는 JDK 5.0 애노테이션으로 기초적인 메커니즘을 지원하는 객체/관계형 매핑 메타데

이터의 문법과 의미를 정의하고 있다. (그렇다. 자바 EE 5.0과 EJB 3.0을 사용하려면 JDK 5.0을

사용해야 한다.) 그에 따라 하이버네이트에도 자연스레 JPA 표준을 구현한 기본 애노테이션이

포함돼 있으며, 그보다 더 심화된 특별한 하이버네이트 매핑과 튜닝에 필요한 확장 애노테이션도

있다.

하이버네이트 코어와 하이버네이트 애노테이션을 사용하면 기존의 XML 파일에 비해 매핑 메

타데이터 코드의 양을 줄일 수 있으며, 애노테이션의 리팩터링 기능에 대해 더 호감을 갖게 될지

도 모른다. 또는 JPA 애노테이션만 사용하거나, 혹은 완벽한 호환성을 갖추는 것이 주된 관심사

가 아니라면 하이버네이트 확장 애노테이션을 추가할 수도 있다. (현장에서 어떤 것을 선택하면

그것의 존재를 항상 부정하기 보다는 포용해야 한다.)

Page 74: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

36 l 하이버네이트 완벽 가이드

또한 개발 공정에 애노테이션이 미치는 영향에 대해 논하고 그것을 매핑에서 어떻게 사용하는

지 하이버네이트 XML 매핑 예제를 통해 이 책 전반에 걸쳐 살펴볼 것이다.

하이버네이트 EntityManager

JPA 명세에는 프로그래밍 인터페이스, 영속 객체에 대한 생명주기 규칙, 쿼리 기능도 정의돼 있

다. JPA의 이 부분에 대한 하이버네이트 구현으로 하이버네이트 EntityManager가 있다. 이것은

하이버네이트 코어 위에 올려 놓을 수 있는 또 다른 부가적인 모듈이다. 필요하다면 일반 하이버

네이트 인터페이스를 사용하거나 JDBC Connection을 사용할 수도 있다. 하이버네이트의 기본

기능은 모든 면에서 JPA 영속화 기능을 포함한다. (EntityManager는 JPA 호환성을 제공하는

하이버네이트 코어를 감싸는 작은 구현체다.)

표준화된 인터페이스와 쿼리 언어를 사용하면 JPA와 호환 가능한 영속화 계층을 모든 EJB 3.0

호환 애플리케이션 서버에서 사용할 수 있다는 장점이 있다. 또는 JPA를 표준화된 특정 런타임

환경이 아닌 일반 자바에서도 사용할 수 있다(하이버네이트 코어를 사용할 수 있는 곳이면 어디

서든 쓸 수 있다는 뜻이다).

하이버네이트 애노테이션을 하이버네이트의 EntityManager와 함께 사용하는 것을 고려해봐

야 한다. 애플리케이션 코드를 JPA 인터페이스와 JPA 쿼리로 작성하면서도 매핑 대부분에 JPA

애노테이션을 쓰지 않는 건 통례가 아니다.

자바 EE 5.0 애플리케이션 서버

이 책에서 EJB 3.0을 전부 다루지는 않는다. 당연히 초점은 영속화에 있으며, 그렇기 때문에 명세

에서 JPA에 관한 부분만 살펴보겠다. (물론 애플리케이션 구조와 설계를 언급할 때 관리형 EJB

컴포넌트와 더불어 다양한 기법도 보여주겠다.)

하이버네이트는 J2EE 1.4의 구현체이자 (조만간) 자바 EE 5.0의 구현체인 제이보스 애플리케

이션 서버(JBoss Application Server)의 일부가 될 것이다. 2 하이버네이트 코어, 하이버네이트 애노

테이션, 하이버네이트 EntityManager 조합은 애플리케이션 서버의 영속화 엔진을 구성한다. 따

라서 독립적으로 사용할 수 있는 모든 것들을 세션 빈, 메시지 주도 빈과 기타 자바 EE 서비스와

같은 EJB 3.0의 모든 장점을 지닌 애플리케이션 서버 내에서도 이용할 수 있다.

이를 잘 이해하려면 자바 EE 5.0 애플리케이션 서버가 더는 J2EE 1.4 영역에서 유일한 해결책

이 아니라는 사실도 알아야 한다. 사실 제이보스 EJB 3.0 컨테이너 또한 다른 곳에 내장되어 실

2   (옮긴이) 현재 JBoss AS는 이미 JEE 5 구현체로 확인받은 상태다.

Page 75: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

1장 객체/관계형 영속화 이해하기 l 37

행될 수 있는 버전을 제공하며, 심지어 톰캣, 단위 테스트(unit test), 스윙 애플리케이션에서도 실

행할 수 있다. 다음 장에서는 EJB 3.0 컴포넌트를 이용하는 프로젝트를 준비하고 손쉬운 통합 테

스트를 위해 제이보스 서버를 설치할 것이다.

보다시피 하이버네이트의 고유 기능들은 필요할 때 부가적인 기능을 제공하는 명세나 벤더 확

장의 중요 부분을 구현하고 있다.

작성한 코드가 JPA를 사용하는지 하이버네이트를 사용하는지 알 수 있는 간단한 방법을 알려

주겠다. 만약 javax.persistence.* 패키지만 쓰고 있다면 명세에 따라 작업하고 있는 것이다. 만약

hibernate.* 패키지도 쓰고 있다면 하이버네이트 고유 기능을 사용하고 있는 것이다. 차후에 벤

더 종속적인 코드에서 이식성과 관련된 부분을 깔끔하게 분리하는 데 도움이 되는 몇 가지 방법

을 보여주겠다.

FAQ? 하이버네이트의 미래는 어떤가? 하이버네이트 코어는 EJB 3.0이나 JPA 명세

보다 빠르고 독립적으로 개발될 것이다. 하이버네이트는 항상 그래왔듯 새로

운 아이디어를 시험하는 실험장이 될 것이다. 하이버네이트 코어에서 개발된

새로운 기능은 그 즉시, 그리고 자동으로 하이버네이트 애노테이션을 사용하

는 자바 퍼시스턴스와 하이버네이트 EntityManager에서 사용할 수 있다. 훗

날 특정 개념이 유용하지 않다고 증명되면 EJB 개정안이나 자바 퍼시스턴스

명세의 미래 표준에 관련된 전문가 그룹 구성원들과 함께 하이버네이트 개발

자들이 작업에 참여할 것이다. 따라서 빠르게 진화하는 표준에 관심이 있다면

하이버네이트 고유의 기능을 사용하길 권장하며 각 전문가 그룹에 피드백을

주기 바란다. 완전한 이식성을 추구하면서 벤더 확장에 대한 요구를 거부한

것이 EJB 1.x와 2.x이 보여준 정체현상의 주요 원인이었다.

ORM과 하이버네이트에 대한 칭찬을 마치고 이제는 실제 코드를 봐야 할 때다. 이제 이러한

이론을 가지고 첫 번째 프로젝트를 구성해보자.

1.5 정리

이 장에서는 객체 영속화 개념과 그 구현 기술로 ORM의 중요성을 논의했다.

객체 영속화는 개별 객체가 애플리케이션 프로세스 범위를 벗어나서도 유지될 수 있게 하는

것을 의미한다. 따라서 각 객체를 데이터 저장소에 저장할 수 있고 나중에 특정 시점에 다시 생성

Page 76: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

38 l 하이버네이트 완벽 가이드

할 수도 있다. 객체/관계형 불일치는 데이터 저장소가 SQL 기반 관계형 데이터베이스 관리 시스

템일 경우에 발생한다. 예를 들어 객체망은 데이터베이스 테이블에 저장할 수 없다. 반드시 객체

를 분해해서 이식 가능한 SQL 데이터 타입의 열로 영속화해야 한다. 이 문제에 대한 좋은 해결책

은 객체/관계형 매핑(ORM)이며, 자바 도메인 모델을 주로 사용할 때 특히 유용하다.

도메인 모델은 자바 애플리케이션에서 사용하는 비즈니스 주체를 나타낸다. 계층형 시스템 아

키텍처에서는 비즈니스 계층(데이터베이스가 아니라 자바에서)에서 비즈니스 로직을 실행할 때

도메인 모델을 사용한다. 이 같은 비즈니스 계층은 그 밑에 있는 영속화 계층과 의사소통하여 도

메인 모델에 대한 영속 객체를 저장하고 읽어온다. ORM은 영속화를 관리하는 영속화 계층의

미들웨어에 해당한다.

ORM은 모든 영속화 작업에 대한 해결책은 아니다. ORM의 역할은 개발자를 도와 여러 테이

블 조인을 하는 복잡한 SQL 문이나 JDBC 결과 집합에서 객체 또는 객체 그래프로 복사하는 객

체 영속화 작업을 줄이는 것이다. 모든 기능을 갖춘 ORM 미들웨어 솔루션은 데이터베이스 이식

성을 제공하며, 캐싱 같은 특정 최적화 기법을 비롯해 SQL과 JDBC로는 짧은 시간 안에 직접 만

들기 쉽지 않은 유용한 기능들을 제공한다.

언젠가는 ORM보다 좋은 기술이 등장할 것이다. 우리(를 비롯한 많은 사람들)는 아마도 이미

알고 있던 SQL, 퍼시스턴스 API 표준, 애플리케이션 통합에 대해 전부 다시 생각해봐야 할지도

모른다. 오늘날 객체지향과 매끄럽게 통합되는 진정한 관계형 데이터베이스 시스템으로의 진화

는 순전히 추측으로만 남아있다. 하지만 우리는 그때까지 기다릴 수도 없고, 조만간 이런 문제들

이 개선될 기미도 보이지 않는다(수십억 달러 규모의 산업은 그렇게 기민하지 않다). ORM은 현

재 이용할 수 있는 최선의 해결책이며, 매일 객체/관계형 불일치에 직면한 개발자의 시간을 절약

해준다. 그리고 마침내, EJB 3.0을 통해 자바 업계에서 채택한 완전한 객체/관계형 매핑 소프트웨

어에 대한 명세도 드디어 사용할 수 있게 되었다.

Page 77: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

39

02프로젝트 시작하기

이제 하이버네이트와 자바 퍼시스턴스를 쓰기 시작해서 단계적인 예제를 통해 그것들을 배워보

고 싶을 것이다. 또 두 영속화 API를 살펴보고 하이버네이트의 고유 기능과 표준 JPA가 주는 혜

택을 어떻게 얻을 수 있는지 알고 싶을 것이다. 이 장에서는 간단한 "Hello World" 애플리케이션

을 통해 그러한 내용을 살펴보겠다.

하지만 훌륭하고 완벽한 튜토리얼이 이미 하이버네이트 레퍼런스 문서에 공개되어 있기 때문

에 여기서 다시 반복하기보다는 하이버네이트 통합과 설정 방법에 관해 자세히 설명하겠다. 한

시간 안에 볼 수 있는 더 간단한 튜토리얼로 시작하고 싶다면 하이버네이트 레퍼런스 문서를 권

장한다. 레퍼런스 문서는 간단하고 독립적인 자바 애플리케이션에서 하이버네이트를 사용하는

것부터 시작해서 가장 중요한 매핑 개념을 살펴보고 최종적으로 톰캣에 배포하는 하이버네이트

웹 애플리케이션을 보여준다.

이 장에서는 하이버네이트와 통합되는 일반 자바 애플리케이션용 프로젝트 기반을 구성하는

방법과 하이버네이트를 그러한 환경에 어떻게 설정할 수 있는지 살펴보겠다. 또한 관리형 환경

(자바 EE 서비스를 제공하는 환경)에서의 하이버네이트 설정과 통합에 관해 논의하겠다.

"Hello World" 프로젝트 빌드 도구로서 앤트(Ant)를 소개하겠다. 앤트를 이용하면 프로젝트

를 컴파일하고 실행하는 것뿐만 아니라 하이버네이트 도구를 활용하는 빌드 스크립트도 작성할

수 있다. 개발 프로세스에 따라 하이버네이트 도구를 사용하여 데이터베이스 스키마를 자동 생

■ 하이버네이트와 자바 퍼시스턴스를 사용한 "Hello World"

■ 순공학 및 역공학 도구

■ 하이버네이트 설정과 통합

Page 78: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

40 l 하이버네이트 완벽 가이드

성할 수도 있고 심지어 역공학으로 기존 (레거시) 데이터베이스 스키마(schema)를 가지고 완전한

애플리케이션을 만들 수도 있다.

실제 하이버네이트 프로젝트를 시작하기에 앞서 모든 훌륭한 엔지니어들이 그러하듯 자신이

사용할 도구를 준비하고 어떤 개발 프로세스를 사용할지 결정해야 한다. 또한 선택한 프로세스

에 따라 자연스럽게 선호하는 도구가 달라질 것이다. 이러한 준비 단계와 선택 가능한 안이 무엇

인지 살펴본 후 하이버네이트 프로젝트를 시작하자.

2.1 하이버네이트 프로젝트 시작하기

어떤 프로젝트에서는 비즈니스 도메인을 객체지향 측면에서 분석하는 개발자가 애플리케이션

개발을 주도하곤 한다. 그외 다른 것들은 레거시 데이터베이스 또는 전문 데이터 모델러가 설계

한 새로운 스키마 같은 기존의 관계형 데이터 모델의 영향을 많이 받는다. 프로젝트에는 결정해

야 할 사항이 많이 있는데, 시작하기에 앞서 먼저 다음 질문들에 답할 필요가 있다.

새로운 비즈니스 요구사항에 대한 명료한 설계를 가지고 맨 처음부터 시작할 수 있는가? ▒

아니면 레거시 데이터나 레거시 애플리케이션 코드가 존재하는가?

기존 산출물에서 필요한 어떤 부분을 자동 생성할 수 있는가(예를 들면, 기존 데이터베이 ▒

스 스키마를 가지고 자바 소스를)? 자바 코드와 하이버네이트 매핑 메타데이터를 가지고 데이터베이스 스키마를 생성할 수 있는가?

이러한 작업을 하기 위해 어떤 도구를 이용할 수 있는가? 전체 개발 주기를 지원하는 도 ▒

구는 어떤가?

이어지는 절에서 기초적인 하이버네이트 프로젝트를 구성할 때 이러한 질문에 관해 논의하겠

다. 로드맵은 다음과 같다.

개발 프로세스를 선택한다. 1.

프로젝트 기반 구조를 구성한다. 2.

애플리케이션 코드와 매핑을 작성한다.3.

하이버네이트를 설정하고 시작한다.4.

애플리케이션을 실행한다.5.

다음 절을 읽고 나면 프로젝트에 적합한 접근법을 준비할 수 있으며, 이 장의 후반부에서 다룰

좀 더 복잡한 시나리오에 적합한 배경지식을 갖출 수 있을 것이다.

Page 79: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 41

2.1.1 개발 프로세스 선택하기

먼저 사용 가능한 도구와 해당 도구의 구성 요소와 결과물에 대해 개괄적으로 살펴보자. 그림

2.1은 앤트 태스크에 대한 다양한 입출력 태스크를 보여준다. 또한 이 모든 기능은 이클립스용

하이버네이트 도구 플러그인으로 사용할 수 있다. 이 장을 읽는 동안 이 다이어그램을 참조하기

바란다. 1

AndroMDA

<hbm2ddl><jdbcconfiguration>

<hbm2hbmxml><configuration>

<hbm2cfgxml> <hbmtemplate>

<hbm2java> <hbm2dao><annotationconfiguration>

<hbm2java> <hbm2doc>

UML 모델 XML/MXI

영속 클래스 자바 소스 매핑 메타데이터 애노테이션 데이터 접근 객체 자바 소스 HTML 문서

하이버네이트 메타모델

데이터베이스 스키마 매핑 메타데이터 XML 설정 XML 프리마커 템플릿

그림 2.1 | 하이버네이트 개발에 사용하는 도구의 입출력

참고 이클립스 IDE용 하이버네이트 도구는 이클립스 IDE에서 사용할 수 있는 하

이버네이트 도구 플러그인이다(이것은 이클립스용 제이보스 IDE의 일부이며,

제이보스 IDE는 마법사, 편집기, 별도의 이클립스 뷰를 제공하여 EJB3, 하이

버네이트, 제이보스 심[JBoss Seam] 및 제이보스 미들웨어를 기반으로 하는

자바 애플리케이션 개발을 도와준다). 순공학과 역공학 관련 기능은 앤트 기

반 도구와 동일하다. 부가적인 하이버네이트 콘솔 뷰는 임시(ad hoc) 하이버네

이트 쿼리(HQL과 Criteria)를 데이터베이스에서 실행하고 그 결과를 시각적

으로 확인할 수 있다. 하이버네이트 도구 XML 편집기는 매핑 파일에서 클래

스, 프로퍼티(property), 심지어 테이블과 열 이름에 대해서도 자동 완성을 지원

한다. 그래픽 도구는 현재 개발 중이고 본 책을 저술하는 중에는 베타 버전을

사용해 볼 수 있을 것이다. 2

1   AndroMDA는 UML 다이어그램 파일에서 POJO 소스 코드를 생성하는 도구다. 엄밀히 말해서 일반 하이버네이트 도구 집합

에 포함되지는 않는다. 따라서 이 장에서는 다루지 않는다. 하이버네이트 웹사이트의 커뮤니티에서 AndroMDA 관련 하이버

네이트 모듈에 대한 더 자세한 정보를 참조한다.

2  (옮긴이) 현재는 http://boss.bekk.no/boss/middlegen/에 이런 툴이 있다.

Page 80: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

42 l 하이버네이트 완벽 가이드

하지만 이 중 일부 스크린샷은 소프트웨어를 배포할 때의 모습과 다를 수 있다. 하이버네이트

도구에 대한 문서에는 스크린샷도 많이 포함돼 있고 자세한 프로젝트 설치 방법도 담고 있으므

로 첫 예제인 "Hello World" 프로그램을 이클립스 IDE에서 작성하는 데 도움될 것이다.

일반적인 개발 시나리오는 다음과 같다.

▒ 하향식: 하향식 개발에서는 먼저 기존의 자바로 구현된 도메인 모델을 가지고 시작하기 때

문에 (이상적으로) 데이터베이스 스키마에 구애받지 않는다. 매핑 메타데이터는 반드시

XML 파일이나 자바 소스에 애노테이션을 추가해서 작성해야 한다. 그런 다음 부가적으

로 하이버네이트의 hbm2ddl 도구를 이용해 데이터베이스 스키마를 생성할 수도 있다.

기존의 데이터베이스 스키마가 없다는 점에서 대부분의 자바 개발자에게 가장 편한 개발

스타일이다. 심지어 개발 중에 하이버네이트 도구를 이용해 매번 애플리케이션을 재시작

할 때마다 데이터베이스 스키마를 자동으로 갱신할 수도 있다.

▒ 상향식: 반대로 상향식 개발은 기존의 데이터베이스 스키마과 데이터 모델을 가지고 시작

한다. 이 경우 가장 쉬운 방법은 역공학 도구를 이용해 데이터베이스에서 메타데이터를

뽑아내는 것이다. 이 메타데이터는 hbm2hbmxml을 사용하여 XML 매핑 파일을 생성하

는 데 사용할 수 있다. hbm2java를 사용하면 하이버네이트 매핑 메타데이터를 사용하여

자바 영속 클래스를 생성할 수 있고, 심지어 데이터 접근 객체도 만들 수 있다. 즉, 자바 영

속화 계층의 뼈대를 만들 수 있다는 뜻이다. 그리고 XML 매핑 파일을 작성하는 대신 자

바 소스 코드(EJB 3.0 엔티티 클래스)에 애노테이션을 추가한 형태도 도구를 이용해서 생

성할 수 있다. 하지만 이 방법은 SQL 데이터베이스 스키마로부터 클래스 연관 관계에 관

한 세부사항과 자바에 관련된 메타 정보를 전부 생성할 수는 없다. 따라서 약간의 추가

작업을 예상해야 한다.

▒ 선 매핑: 하이버네이트 XML 매핑 메타데이터는 데이터베이스 스키마를 완벽하게 추론하

고 애플리케이션의 영속화 계층에 해당하는 자바 소스 코드를 생성하는 데 필요한 충분

한 정보를 제공한다. 또한 XML 매핑 문서는 심하게 장황하지도 않다. 따라서 몇몇 아키

텍트와 개발자들은 선 매핑 방식의 개발을 선호한다. 이 방식은 직접 하이버네이트 XML

매핑 파일을 작성한 다음 hbm2ddl로 데이터베이스 스키마를 생성하고 hbm2java로 자

바 클래스를 생성한다. 개발 중에 하이버네이트 XML 매핑 파일을 지속적으로 수정하고

다른 구성물은 이 XML 매핑 파일을 이용해서 자동 생성한다. 추가적인 비즈니스 로직이

나 데이터베이스 객체는 상속과 보조 DDL로 추가한다. 이러한 개발 방법은 오직 노련한

하이버네이트 전문가에게만 권장한다.

Page 81: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 43

▒ 후 매핑: 가장 난해한 시나리오는 기존의 자바 클래스와 데이터베이스 스키마를 조합하

는 것이다. 이 경우 하이버네이트 도구 집합이 도와줄 수 있는 바가 거의 없다. 물론 임의

의 자바 도메인 모델을 특정 스키마로 매핑하는 것이 불가능하지는 않다. 따라서 이 시나

리오에서는 최소한 약간의 자바 클래스나 데이터베이스 스키마, 또는 둘 모두를 리팩터링

할 필요가 있다. 매핑 메타데이터는 (자바 클래스와 데이터베이스 스키마가 비슷하다면

애노테이션을 사용할 수도 있지만) 거의 대부분 직접 XML 파일로 작성해야 할 것이다.

무척 힘든 시나리오지만 다행히 극도로 보기 드문 경우다.

이제 도구와 설정 옵션을 더 자세히 살펴보고 일반적인 하이버네이트 애플리케이션 개발 환경

을 구성하겠다. 지시사항을 단계별로 차근차근 따라해서 동일한 개발 환경을 만들 수도 있고, 앤

트 빌드 스크립트와 같은 필요한 부분만 참조할 수도 있다.

첫 번째 개발 프로세스는 하향식이다. 따라서 기존의 데이터 스키마나 자바 코드가 없는 상태

로 하이버네이트 프로젝트를 진행한다. 그러고 나서 코드를 JPA와 EJB 3.0으로 이전(migrate)하

고 역공학으로 기존의 데이터베이스 스키마에서 상향식으로 프로젝트를 시작해보겠다.

2.1.2 프로젝트 구성하기

미리 http://www.hibernate.org/에서 하이버네이트 최신 배포판을 받아 압축을 풀었다고 가정

하겠다. 또한 현재 사용중인 개발 환경에 아파치 앤트도 설치해야 한다. 그리고 http://hsqldb.

org/에서 HSQLDB 최신 버전을 다운로드하고 패키지 압축을 푼다. 테스트할 때 이 데이터베이

스를 사용할 것이다. 이미 다른 데이터베이스 관리 시스템을 설치하여 사용하고 있다면 그와 관

련된 JDBC 드라이버만 구하면 된다.

여기서는 나중에 개발할 복잡한 애플리케이션이 아니라 "Hello World" 예제로 시작하겠다.

그렇게 해야 하이버네이트의 세부사항으로 인해 주의를 흐트리지 않고 개발 프로세스에 집중할

수 있다. 우선 프로젝트 디렉터리를 먼저 구성해보자.

작업 디렉터리 만들기

시스템에 새로운 디렉터리를 만든다. 마이크로소프트 윈도(Microsoft Windows)에서 작업

중이라면 C:\helloworld로 만드는 것이 좋겠다. 뒤에서 살펴 볼 예제에서는 이 디렉터리를

WORKDIR로 부를 것이다. 하위 디렉터리로 lib와 src를 만들고 필요한 모든 라이브러리(library)

를 복사한다.

Page 82: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

44 l 하이버네이트 완벽 가이드

WORKDIR

+lib

antlr.jar

asm.jar

asm-attrs.jars

c3p0.jar

cglib.jar

commons-collections.jar

commons-logging.jar

dom4j.jar

hibernate3.jar

hsqldb.jar

jta.jar

+src

라이브러리 디렉터리에서 볼 수 있는 라이브러리는 하이버네이트 배포판에서 찾을 수 있으며,

일반 하이버네이트 프로젝트에서 대부분 필요한 것들이다. hsqldb.jar 파일은 HSQLDB 배포판

에서 찾을 수 있다. 다른 데이터베이스 관리 시스템을 사용하고 있다면 그것 대신 다른 드라이버

JAR로 교체한다. 이 책을 쓰는 시점보다 더 최신 버전의 하이버네이트를 사용하고 있다면 여기

서 본 라이브러리 중 일부는 아마도 꼭 필요하지 않은 것일 수도 있다. 올바른 라이브러리를 정확

하게 확인하려면 하이버네이트 배포 패키지에 있는 lib/README.txt 파일을 항상 확인한다. 이

파일에는 하이버네이트가 사용하는 필수 라이브러리와 부가적인 서드파티(third-party) 라이브

러리의 최신 목록이 들어있다. 실행 시에는 필수 라이브러리만 있으면 된다.

"Hello World" 애플리케이션에서는 데이터베이스에 메시지를 저장하고 그것을 데이터베이스

에서 불러올 것이다. 이제 이런 비즈니스 상황에 필요한 도메인 모델을 만들어야 한다.

도메인 모델 만들기

하이버네이트 애플리케이션에서는 데이터베이스 테이블로 매핑할 영속 클래스를 정의한다. 이

클래스들은 비즈니스 도메인 분석을 바탕으로 정의한다. 따라서 이러한 클래스는 해당 도메인의

모델인 셈이다. "Hello World" 예제는 클래스 하나와 그것에 대한 매핑으로 구성된다. 간단한 영

속 클래스가 어떻게 생겼으며, 매핑을 어떻게 만드는지, 그리고 하이버네이트에서 영속 클래스의

객체를 사용하여 무엇을 할 수 있는지 살펴보자.

이 예제의 목적은 데이터베이스에 메시지를 저장하고 그것을 가져와서 화면에 보여주는 것

이다. 이 예제에는 출력가능한 메시지를 나타내는 간단한 영속 클래스인 Message가 있다.

Message 클래스는 코드 2.1에서 확인할 수 있다.

Page 83: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 45

예제 2.1 Message.java: 간단한 영속 클래스

package hello;

public class Message {

private Long id; 식별자 속성

private String text; 메시지 텍스트

private Message nextMessage; 다른 Message 인스턴스에 대한 참조

Message() {

}

public Message(String text) {

this.text = text;

}

public Long getId() {

return id;

}

private void setId(Long id) {

this.id = id;

}

public String getText() {

return text;

}

public void setText(String text) {

this.text = text;

}

public Message getNextMessage() {

return nextMessage;

}

public void setNextMessage(Message nextMessage) {

this.nextMessage = nextMessage;

}

}

Page 84: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

46 l 하이버네이트 완벽 가이드

Message 클래스에는 식별자 속성, 메시지 텍스트, 또 다른 Message 객체에 대한 참조로 속성

이 세 개 포함돼 있다. 애플리케이션에서는 식별자 속성을 사용해서 영속 객체의 데이터베이스

식별자인 주키에 접근할 수 있다. 만약 두 Message 인스턴스의 식별자 값이 같다면 두 인스턴스

는 데이터베이스에서 같은 행을 나타낸다.

이 예제는 식별자 속성으로 Long 타입을 사용하지만 꼭 그래야 하는 것은 아니다. 하이버네이

트는 가상적으로 어떠한 식별자 타입도 허용한다. 이 주제에 대해서는 나중에 살펴보겠다.

Message 클래스의 모든 속성에 대한 자바빈즈(JavaBeans) 형식의 접근 메서드가 있음을 확

인할 수 있다. 또한 이 클래스에는 매개변수(parameter)가 없는 생성자도 하나 포함돼 있다. 예

제에서 보여준 영속 클래스는 항상 이것과 비슷할 것이다. 인자가 없는 생성자는 필수 사항이다

(하이버네이트 같은 도구는 이 생성자를 대상으로 리플렉션 3 을 써서 객체를 생성한다).

Message 클래스의 인스턴스를 하이버네이트가 관리(영속화)할 수도 있지만 꼭 그래야 하는

것은 아니다. Message 객체는 어떠한 하이버네이트 관련 클래스나 인터페이스도 구현하지 않으

므로 일반 자바 클래스처럼 사용할 수 있다.

Message message = new Message("Hello World");

System.out.println( message.getText() );

이 코드는 "Hello World" 애플리케이션에서 하리라 예상하는 일을 정확히 하고 있다. 콘솔에

Hello World를 출력하는 일 말이다. 우리가 장난치는 것처럼 보일지도 모르지만, 사실 여기서 하

이버네이트가 다른 영속화 솔루션과는 다른 중요한 차이점을 볼 수 있다. 바로 영속 클래스를 어

떠한 실행 문맥(execution context)에서도 쓸 수 있다는 것이다. 즉, 아무런 특별한 컨테이너도 필

요하지 않다는 뜻이다. 또한 이러한 특징은 일반 자바 객체를 사용하는 새로운 JPA 엔티티의 장

점 중 하나이기도 하다.

소스 폴더에 hello라는 디렉터리이자 패키지에 Message 클래스 코드를 저장한다.

클래스를 데이터베이스 스키마와 매핑하기

객체/관계형 매핑이라는 마법을 부리려면 하이버네이트가 정확히 어떻게 Message 클래스를 영

속화해야 할지에 대한 정보가 더 필요하다. 즉, 하이버네이트가 어떻게 클래스의 객체를 저장하

고 읽어올지 알고 있어야 한다는 뜻이다. Message 클래스의 속성을 MESSAGES 테이블의 열로

매핑하는 등의 정보를 정의하는 메타데이터는 XML 문서로 작성할 수 있다. 예제 2.2의 매핑 문

서를 살펴보자.

3  (옮긴이) 자바 리플렉션 API를 말한다. http://java.sun.com/docs/books/tutorial/reflect/index.html 참조.

Page 85: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 47

예제 2.2 간단한 하이버네이트 XML 매핑

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD//EN"

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

<hibernate-mapping>

<class

name="hello.Message"

table="MESSAGES">

<id

name="id"

column="MESSAGE_ID">

<generator class="increment" />

</id>

<property

name="text"

column="MESSAGE_TEXT" />

<many-to-one

name="nextMessage"

cascade="all"

column="NEXT_MESSAGE_ID"

foreign-key="FK_NEXT_MESSAGE" />

</class>

</hibernate-mapping>

매핑 문서를 통해 하이버네이트는 Message 클래스를 MESSAGES 테이블로 영속화해야 한

다는 점을 알 수 있다. 이때 식별자 속성은 MESSAGE_ID라는 열로, text 속성은 MESSAGE_

TEXT 열로 매핑하고 다대일 연관 관계인 nextMessage 프로퍼티는 NEXT_MESSAGE_ID라

는 외래키 열로 매핑한다. 또한 하이버네이트는 직접 FK_NEXT_MESSAGE라는 이름으로 데이

터베이스 카탈로그에 외래키 제약 조건을 추가할 수 있게끔 데이터베이스 스키마를 생성해준다.

XML 문서는 이해하기 어렵지 않으며, 손쉽게 작성하고 관리할 수 있다. 애노테이션을 사용하

여 매핑 정보를 소스 코드에 직접 정의하는 방법은 나중에 살펴보겠지만 어떤 방법을 택하든 하

이버네이트는 Message 클래스의 인스턴스를 추가하고, 수정하고, 삭제하고, 가져올 때 필요한 모

Page 86: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

48 l 하이버네이트 완벽 가이드

든 SQL 문을 생성하는 데 필요한 정보를 충분히 가지고 있다. 더는 직접 SQL을 작성할 필요가

없다.

Message.hbm.xml이라는 파일을 만들고 예제 2.2의 내용을 작성한 뒤 Message.java 파일이

위치한 hello 소스 패키지에 둔다. hbm이라는 접미사는 하이버네이트 커뮤니티에서 채택한 작명

규약(naming convention)이며, 대부분의 개발자는 도메인 클래스의 소스 코드 옆에 매핑 파일을

두는 형식을 선호한다.

이제 "Hello World"의 주요 코드에서 객체를 불러오고 저장해보자.

객체 저장하고 불러오기

이제 하이버네이트를 써서 새로운 메시지를 데이터베이스에 저장해보자(예제 2.3).

예제 2.3 "Hello World"의 주요 애플리케이션 코드

package hello;

import java.util.*;

import org.hibernate.*;

import persistence.*;

public class HelloWorld {

public static void main(String[] args) {

// 첫 번째 작업 단위

Session session = HibernateUtil.getSessionFactory().openSession();

Transaction tx = session.beginTransaction();

Message message = new Message("Hello World");

Long msgId = (Long) session.save(message);

tx.commit();

session.close();

// 두 번째 작업 단위

Session newSession = HibernateUtil.getSessionFactory().openSession();

Transaction newTransaction = newSession.beginTransaction();

Page 87: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 49

List messages =

newSession.createQuery("from Message m order by m.text asc").list();

System.out.println(messages.size() + " message(s) found:");

for (Iterator iter = messages.iterator(); iter.hasNext();) {

Message loadedMsg = (Message) iter.next();

System.out.println(loadedMsg.getText());

}

newTransaction.commit();

newSession.close();

// 애플리케이션 종료

HibernateUtil.shutdown();

}

}

이 코드를 프로젝트 소스 폴더의 hello 패키지에 있는 HelloWorld.java 파일에 작성한다. 이제

코드를 살펴보자.

이 클래스에는 표준 자바 main() 메서드가 포함돼 있으므로 명령줄(command line)에서 직접

main() 메서드를 호출할 수 있다. 주요 애플리케이션 코드에서는 하이버네이트를 사용해서 두

개의 별도 작업 단위(unit of work)를 실행한다. 첫 번째 작업 단위에서는 새로운 Message 객체

를 저장하고, 두 번째 작업 단위에서는 모든 객체를 불러와서 콘솔에 객체에 있는 텍스트를 출

력한다.

일반적으로 데이터베이스에 접근할 때는 하이버네이트 Session, Transaction, Query 인터페

이스를 이용하면 된다.

▒ Session: 하이버네이트 Session은 많은 일을 한다. Session은 데이터베이스를 사용하는

별도의 작업 단위를 표현하는 객체로, 단일 스레드(thread)로 동작하고 공유되지 않는

객체다. 또한 Session에는 객체를 불러오고 저장할 때 호출할 수 있는 영속성 관리 API

가 포함돼 있다. (Session은 내부적으로 특정 시점에 데이터베이스와 동기화할 때 필요

한 SQL 문장 대기열[queue]과 Session이 모니터링하고 있는 관리형 영속 인스턴스의 맵

[map]으로 구성돼 있다.)

Page 88: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

50 l 하이버네이트 완벽 가이드

▒ Transaction: 이 하이버네이트 API를 사용하면 트랜잭션 경계(transaction boundary)를 프

로그램 방식(programmatically)으로 설정할 수 있지만, 프로그램 방식을 쓰는 것은 선택

사항이다(트랜잭션 경계는 반드시 설정해야 한다). 다른 대안으로 JDBC 트랜잭션 경계

설정(demarcation), JTA 인터페이스, EJB의 CMT(컨테이너가 관리하는 트랜잭션)가 있다.

▒ Query: 데이터베이스 쿼리를 하이버네이트의 객체지향적인 쿼리 언어(HQL)나 일반적인

SQL로 작성할 수 있다. 이 인터페이스는 쿼리를 만들고 쿼리의 치환자(placeholder)에 인

자를 연동(bind)하고 다양한 방법으로 쿼리를 실행할 수 있게 해준다.

HibernateUtil.getSessionFactory()를 호출하는 코드는 무시하자. 곧 살펴보겠다.

첫 번째 작업 단위가 실행되면 다음과 비슷한 SQL 이 실행된다.

insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)

values (1, 'Hello World', null)

잠깐! MESSAGE_ID 열이 지금 이상한 값으로 초기화돼 있다. 어디서도 message의 id 프로퍼

티를 설정하지 않았기 때문에 NULL이 들어갈 것으로 예상했을 것이다. 그렇지 않은가? 실제로

id 프로퍼티는 특별하다. id 프로퍼티는 식별자 프로퍼티이므로 자동으로 생성된 유일한 값을 보

관한다. save()를 호출하면 하이버네이트가 Message 인스턴스에 이 값을 할당한다. (이 값이 어

떻게 생성되는가에 관해서는 나중에 다루겠다.)

두 번째 작업 단위를 보자. "from Message m order by m.text asc"라는 문자열은 HQL로 표현

한 하이버네이트 쿼리다. 이 쿼리는 list()를 호출할 때 내부적으로 다음과 같은 SQL로 변환된다.

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID

from MESSAGES m

order by m.MESSAGE_TEXT asc

이 main() 메서드를 실행하면(지금은 하지 말자. 설정해야 할 하이버네이트 설정이 더 있다.)

다음과 같은 결과가 콘솔에 출력될 것이다.

1 message(s) found:

Hello World

지금까지 하이버네이트 같은 ORM 도구를 사용해 본 적이 없다면 아마 코드나 매핑 메타데이

터 같은 곳에서 SQL 문장을 볼 수 있으리라 기대했을지도 모르지만, 그런 곳에서 SQL 문장을

볼 수는 없을 것이다. 모든 SQL은 실행 시에 생성된다. (실제로는 재사용 가능한 SQL 문장을 애

플리케이션을 구동할 때 생성해 둔다.)

Page 89: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 51

보통 다음 단계는 하이버네이트를 설정하는 것이다. 하지만 자신이 있다면 다음 코드를 애

플리케이션에서 추가해서 세 번째 작업 단위에 하이버네이트의 다른 두 가지 기능인 변경 감지

(dirty checking)와 연쇄 작용(cascading)을 추가할 수 있다.

// 세 번째 작업 단위

Session thirdSession = HibernateUtil.getSessionFactory().openSession();

Transaction thirdTransaction = thirdSession.beginTransaction();

// msgId는 첫 번째 메시지의 식별자 값을 담고 있다.

message = (Message) thirdSession.get( Message.class, msgId );

message.setText( "Greetings Earthling" );

message.setNextMessage(

new Message( "Take me to your leader (please)" )

);

thirdTransaction.commit();

thirdSession.close();

이 코드는 동일한 데이터베이스 트랜잭션 안에서 세 개의 SQL 문을 호출한다.

select m.MESSAGE_ID, m.MESSAGE_TEXT, m.NEXT_MESSAGE_ID

from MESSAGES m

where m.MESSAGE_ID = 1

insert into MESSAGES (MESSAGE_ID, MESSAGE_TEXT, NEXT_MESSAGE_ID)

values (2, 'Take me to your leader (please)', null)

update MESSAGES

set MESSAGE_TEXT = 'Greetings Earthling', NEXT_MESSAGE_ID = 2

where MESSAGE_ID = 1

어떻게 하이버네이트가 첫 번째 메시지의 text와 nextMessage 프로퍼티에 대한 변경 사항을

감지하고 데이터베이스를 자동으로 갱신했는지 주목하자. 이는 하이버네이트가 자동 변경 감지

를 했기 때문이다. 이 기능은 작업 단위 안에서 객체의 상태를 수정했을 때 명시적으로 하이버네

이트가 데이터베이스를 갱신하도록 요청하는 수고를 덜어준다. 이와 비슷하게 첫 번째 메시지의

참조를 생성할 때도 새로운 메시지가 영속화된다. 이 기능을 연쇄저장(cascading save)이라 한다.

이를 활용하면 이미 영속상태인 객체를 참조할 수 있는 한 명시적으로 save()를 호출해서 새로운

객체를 영속화하는 수고를 덜 수 있다.

또한 SQL 문의 순서가 프로퍼티 값을 설정한 순서와 같지 않다는 것을 눈여겨보자. 하이버

네이트는 데이터베이스 외래키 제약 조건 위반을 피할 수 있게끔 복잡한 알고리즘을 사용하여

Page 90: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

52 l 하이버네이트 완벽 가이드

SQL 문의 순서를 정하지만 사용자가 충분히 예측할 수는 있다. 이 기능을 트랜잭션 단위의 쓰기

지연(transactional write-behind)이라 한다.

이제 애플리케이션을 실행하면 다음과 같은 결과를 볼 수 있다(쿼리 출력 단계를 다시 실행하

려면 두 번째 작업 단위를 세 번째 작업 단위 뒤로 복사해야 할 것이다).

2 message(s) found:

Greetings Earthling

Take me to your leader (please)

이제 도메인 클래스, XML 매핑 파일, 그리고 객체를 읽어오고 저장하는 "Hello World" 애플

리케이션 코드를 작성했다. 이 코드를 컴파일하고 실행하기에 앞서 하이버네이트 설정을 만들어

야 한다(그리고 HibernateUtil 클래스의 미스터리도 풀어야 한다).

2.1.3 하이버네이트 설정과 시작

하이버네이트를 초기화하는 일반적인 방법은 Con�guration 객체로부터 SessionFactory 객체

를 만드는 것이다. Con�guration은 하이버네이트 설정 파일(또는 프로퍼티 파일)을 객체로 나

타낸 것이라 생각해도 무방하다.

자, 이제 HibernateUtil 클래스를 살펴보기 전에 몇 가지 사용법을 살펴보자.

SessionFactory 만들기

다음은 전형적인 하이버네이트 시작 예제로, 자동 설정 파일 감지를 사용하는 한 줄짜리 코드다.

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

잠깐 기다려라! 하이버네이트가 설정 파일의 위치와 불러와야 할 파일을 어떻게 알 수 있을까?

new Configuration()이 호출되면 하이버네이트는 클래스패스 최상단(root)에서 hibernate.

properties 파일을 찾는다. 파일이 있으면 모든 hibernate.* 프로퍼티 파일을 불러들여

Con�guration 객체에 추가한다.

configure()를 호출하면 하이버네이트는 클래스패스 최상단에서 hibernate.cfg.xml 파일을

찾아보고 파일을 찾지 못하면 예외를 던진다. 물론 이 설정 파일이 없으면 이 메서드를 호출할 필

요가 없다. 만약 XML 설정 파일에 설정한 것들이 그전에 읽어들인 프로퍼티의 설정과 중복된다

면 XML 설정이 이전 설정을 덮어쓴다.

hibernate.properties 설정 파일의 위치는 항상 모든 패키지의 밖인 클래스패스 최상단에 있어

Page 91: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 53

야 한다. 만약 다른 파일을 사용하거나 하이버네이트가 클래스패스의 하위 디렉터리에서 XML

설정 파일을 찾게끔 하고 싶다면 con�gure() 메서드의 인자로 해당 경로를 넘겨줘야 한다.

SessionFactory sessionFactory = new Configuration()

.configure("/persistence/auction.cfg.xml")

.buildSessionFactory();

마지막으로 SessionFactory를 만들기 전에 부가적인 설정 옵션이나 매핑 파일 위치를

Con�guration 객체에 프로그램 방식으로 설정할 수 있다.

SessionFactory sessionFactory = new Configuration()

.configure("/persistence/auction.cfg.xml")

.setProperty(Environment.DEFAULT_SCHEMA, "CAVEATEMPTOR")

.addResource("auction/CreditCard.hbm.xml")

.buildSessionFactory();

여기서는 다양한 설정 소스(source)를 사용했다. 먼저 클래스패스에 위치한 hibernate.

properties 파일을 (파일이 존재하면) 읽어 들인다. 다음으로 /persistence/auction.cfg.xml에 있

는 모든 설정을 추가하고 이전에 적용된 설정을 덮어쓴다. 마지막으로 부가적인 설정 프로퍼티

(기본 데이터베이스 스키마 이름)를 프로그램 방식으로 설정했고 부가적인 하이버네이트 XML

매핑 메타데이터 파일을 설정에 추가했다.

물론 모든 옵션을 프로그램 방식으로 설정하거나, 배포하는 데이터베이스에 따라 XML 설정

파일을 다른 것으로 교체할 수도 있다. 사실상 하이버네이트를 설정하고 배포하는 방법은 정해

진 것이 없다. 어찌됐든 준비한 설정으로 SessionFactory만 만들면 된다.

참고 메서드 호출 연결(Method chaining) 메서드 호출 연결은 여러 하이버네이트 인

터페이스에서 지원하는 프로그래밍 스타일이다. 이 스타일은 자바보다 스몰

토크(Smalltalk)에서 더 인기있으며, 어떤 사람들은 더 읽기 불편하고 디버깅하

기도 불편하다고 말하기도 한다. 하지만 메서드 호출 연결은 이 절에서 살펴본

설정을 다루는 것과 같이 여러 경우에 유용하다. 동작 원리는 이렇다. 자바 개

발자들은 설정자 메서드나 추가 메서드의 반환 타입을 아무런 값도 반환하지

않는다는 의미로 void를 사용하지만, 스몰토크에서는 void 타입 대신 설정자

메서드나 추가 메서드에서 보통 전달받은 객체를 반환해준다. 예제 코드에서

는 이러한 스몰토크 스타일을 사용하겠지만 내키지 않는다면 하지 않아도 상

관 없다. 이런 코딩 스타일을 쓰면 각 메서드 호출을 각기 다른 줄에 걸쳐 작성

하는 것이 좋다. 그렇게 하지 않으면 디버거에서 코드를 단계별로 살펴보기가

힘들 수도 있다.

Page 92: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

54 l 하이버네이트 완벽 가이드

이제 하이버네이트를 어떻게 시작하고 SessionFactory를 어떻게 만드는지 살펴봤다. 다음은

무엇을 해야 할까? 하이버네이트에 필요한 설정을 만들어야 한다.

XML 설정 파일 만들기

우리가 대부분의 사용자처럼 간단한 것을 선호해서 하이버네이트용 XML 설정 파일 하나에 모

든 설정과 관련한 세부사항을 담기로 했다고 가정해 보자.

이때 새로운 설정 파일의 이름을 hibernate.cfg.xml로 하고 프로젝트의 모든 패키지 밖에 위

치한 소스 폴더 4 에 넣기를 권장한다. 그렇게 하면 컴파일 이후 최상위 클래스패스에 놓일 것이고

하이버네이트가 자동으로 찾을 것이다. 예제 2.4의 파일을 보자.

예제 2.4 간단한 하이버네이트 XML 설정 파일

<!DOCTYPE hibernate-configuration SYSTEM

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

<hibernate-configuration>

<session-factory>

<property name="hibernate.connection.driver_class">

org.hsqldb.jdbcDriver

</property>

<property name="hibernate.connection.url">

jdbc:hsqldb:hsql://localhost

</property>

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

sa

</property>

<property name="hibernate.dialect">

org.hibernate.dialect.HSQLDialect

</property>

<!-- C3P0 연결 풀 공급자를 사용 -->

<property name="hibernate.c3p0.min_size">5</property>

<property name="hibernate.c3p0.max_size">20</property>

<property name="hibernate.c3p0.timeout">300</property>

<property name="hibernate.c3p0.max_statements">50</property>

<property name="hibernate.c3p0.idle_test_period">3000</property>

4  (옮긴이) 최상위 소스 디렉터리를 말한다.

Page 93: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 55

<!--SQL을 보기 좋은 형태로 표준 출력 -->

<property name="show_sql">true</property>

<property name="format_sql">true</property>

<!-- XML 매핑 파일 목록 -->

<mapping resource="hello/Message.hbm.xml" />

</session-factory>

</hibernate-configuration>

문서 타입 선언(DTD, document type declaration)은 XML 파서가 하이버네이트 설정 DTD

에 따라 이 문서의 유효성 검증을 하는 데 사용된다. 해당 선언이 하이버네이트 XML 매핑 파일

과 같은 DTD가 아니라는 점에 주목하자. 또한 이것이 좀 더 눈에 띄게끔 프로퍼티 값에 몇 줄을

띄어 두었다. 데이터베이스 사용자명(username)에 줄바꿈이 들어있지 않는 한 설정 파일에서는

이렇게 하지 않아도 된다.

설정 파일에서 가장 처음으로 나오는 것은 데이터베이스 연결 설정이다. 개발자는 어떤 데이터

베이스 JDBC 드라이버를 사용하고 있고, 어떤 URL, 사용자명, 비밀번호를 사용해서 데이터베

이스에 연결하는지 하이버네이트에게 알려줄 필요가 있다(여기서는 기본적으로 비밀번호가 필

요 없는 HSQLDB를 사용하기 때문에 비밀번호는 생략했다). Dialect(hibernate.dialect 프로퍼

티)를 설정하면 하이버네이트가 어떤 SQL 구문을 생성해서 데이터베이스와 상호작용해야 할지

알려줄 수 있다. 하이버네이트에서는 십여 개의 방언(dialect)을 제공하며, 이 목록은 하이버네이

트 API 문서에서 확인할 수 있다.

XML 설정 파일에서 하이버네이트 프로퍼티는 hibernate 접두어 없이 기술해도 된다. 따라

서 hibernate.show_sql 또는 그냥 show_sql을 사용해도 된다. 여기서 사용하는 프로퍼티 이

름과 값은 프로그램 방식 설정 프로퍼티와는 다르다. 즉, 프로그램 방식 설정 프로퍼티는 org.

hibrnate.cfg.Environment에 정의돼 있는 상수를 따른다. 예를 들어, hibernate.connection.

driver_class 프로퍼티는 Environment.DRIVER 상수에 해당한다.

중요한 설정 옵션을 살펴보기에 앞서 설정에서 하이버네이트 XML 매핑 파일명을 지정하고 있

는 마지막 줄을 보자. Con�guration 객체는 SessionFactory를 만들기 전에 모든 XML 매핑 파

일을 알고 있어야 한다. SessionFactory는 특정 매핑 메타데이터에 대한 하이버네이트 설정을 나

타내는 객체다. 모든 XML 매핑 파일의 목록을 하이버네이트 XML 설정 파일에 나열하거나, 그

것의 이름과 경로를 프로그램 방식으로 Con�guration 객체에 설정할 수 있다. 어떤 경우든 여러

Page 94: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

56 l 하이버네이트 완벽 가이드

분이 나열한 매핑 파일 경로는 클래스패스상에서 상대 경로다. 그리고 이 예제에서 hello는 클래

스패스 최상단에 위치한 패키지를 나타낸다.

또한 하이버네이트가 실행하는 모든 SQL을 콘솔에 출력하도록 설정했고, 내부적으로 어떤 일

이 발생하는지 확인할 수 있게 하이버네이트가 SQL을 보기 편하게 출력하게끔 설정했다. 로깅에

관해서는 이번 장 후반부에서 다시 살펴보겠다.

때때로 유용한 방법이 하나 더 있는데, 설정 옵션을 시스템 프로퍼티를 이용해서 더욱 동적으

로 만드는 방법이 있다.

...

<property name="show_sql">${displaysql}</property>

...

이제 java -displaysql=true처럼 애플리케이션을 구동할 때 시스템 프로퍼티를 명령줄에 입력

해서 자동으로 하이버네이트 설정 프로퍼티에 적용할 수 있다.

데이터베이스 연결 풀 설정은 특히 주의해서 살펴봐야 한다.

데이터베이스 연결 풀

보통 데이터베이스를 사용해야 할 때마다 새로운 연결을 만드는 것은 바람직하지 않다. 대신 자

바 애플리케이션에서는 연결 풀을 사용하는 것이 좋다. 데이터베이스를 이용해서 작업해야 하

는 각 애플리케이션 스레드는 연결 풀에서 연결을 요청하고 모든 SQL 작업이 끝나면 풀에 연결

을 반환한다. 풀에서는 연결을 관리하고 새로운 연결을 만들고 닫는 비용을 최소화한다.

풀을 사용하는 세 가지 이유는 다음과 같다.

새로운 연결을 가져오려면 비용이 많이 든다. 심지어 어떤 데이터베이스 관리 시스템은 ▒

각 연결마다 완전히 별개의 서버 프로세스를 시작하기도 한다.

수많은 유휴 상태의 연결을 유지하는 것은 데이터베이스 관리 시스템에 많은 부담을 주 ▒

며, 풀은 유휴 연결 사용을 최적화할 수 있다(또는 더는 요청이 없다면 연결을 끊는다).

준비된 문장 ▒ (prepared statements)을 만드는 것 또한 몇몇 드라이버에서는 비용이 많이 드므로 연결 풀은 여러 요청에서 사용할 수 있게끔 문장을 캐싱할 수 있다.

그림 2.2는 비관리형(unmanaged) 애플리케이션 런타임 환경(즉, 별도의 애플리케이션 서버가

없는 환경)에서의 연결 풀의 역할을 보여준다.

Page 95: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 57

main()

비관리형 JSE 환경

사용자 관리 JDBC 연결연결 풀

데이터베이스

애플리케이션

JSP

서블릿

그림 2.2 | 비관리형 환경에서의 JDBC 연결 풀

연결 풀을 제공하는 애플리케이션 서버가 없으면 애플리케이션은 자신만의 풀 관리 알고리즘

을 만들거나 오픈소스인 C3P0 연결 풀 소프트웨어와 같은 서드파티 라이브러리를 이용하면 된

다. 하이버네이트가 없을 경우 애플리케이션 코드는 연결 풀에 JDBC 연결을 요청한 뒤 JDBC 프

로그래밍 인터페이스를 사용하여 SQL 문을 실행한다. 애플리케이션이 SQL 문을 종료하고 마침

내 연결을 닫으면 준비된 문장과 연결은 소멸되지 않고 풀로 반환된다.

하이버네이트를 사용하면 그림이 바뀐다. 그림 2.3에서 볼 수 있듯 실제로 하이버네이트가

JDBC 연결 풀의 클라이언트인 것처럼 동작한다. 애플리케이션 코드는 하이버네이트 Session과

Query API를 사용해 영속화 작업을 하고 (아마) 하이버네이트 Transaction API를 사용해 데이

터베이스 트랜잭션을 관리한다.

하이버네이트에는 어떠한 연결 풀 소프트웨어와도 통합되는 플러그인 아키텍처(plug-in

architecture)가 정의돼 있다. 하지만 하이버네이트는 C3P0을 내부적으로 지원하며, C3P0은 하

이버네이트 배포판에 포함돼 있으므로 별도로 구할 필요 없이 사용하기만 하면 된다(c3p0.jar 파

일이 이미 라이브러리 복사돼 있을 것이다). 하이버네이트가 풀을 관리해 주고 설정 프로퍼티는

하이버네이트를 통해 연결 풀에 전달된다. 그럼 어떻게 하이버네이트를 통해 C3P0을 설정할까?

main()

비관리형 JSE 환경

애플리케이션

서블릿

하이버네이트

연결 풀 데이터베이스

JSP

Session

Transaction

Query

그림 2.3 | 비관리형 환경에서 하이버네이트로 연결 풀 사용하기

Page 96: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

58 l 하이버네이트 완벽 가이드

연결 풀을 설정하는 방법 중 하나는 이전 절에서 했듯이 설정을 hibernate.cfg.xml 설정 파일

에 넣는 것이다.

또는 애플리케이션의 클래스패스 최상단에 hibernate.properties 파일을 만들어도 된다.

C3P0 설정이 담긴 hibernate.properties 파일의 예를 예제 2.5에서 확인할 수 있다. 매핑 자원 목

록이 있다는 것만 제외하면 이 파일은 예제 2.4에서 본 설정과 같다.

예제 2.5 hibernate.properties를 사용하여 C3P0 연결 풀 설정하기

hibernate.connection.driver_class = org.hsqldb.jdbcDriver

hibernate.connection.url = jdbc:hsqldb:hsql://localhost

hibernate.connection.username = sa

hibernate.dialect = org.hibernate.dialect.HSQLDialect

hibernate.c3p0.min_size = 5 b

hibernate.c3p0.max_size = 20 c

hibernate.c3p0.timeout = 300 d

hibernate.c3p0.max_statements = 50 e

hibernate.c3p0.idle_test_period = 3000 f

hibernate.show_sql = true

hibernate.format_sql = true

❶ C3P0가 늘 준비해 둬야 할 최소 JDBC 연결 수

❷ 풀에 존재하는 최대 연결 수. 실행 시에 이 수치를 초과하면 예외가 발생한다.

❸ 시간제한 주기(여기서는 300초). 이 시간이 지나면 유휴 연결을 풀에서 제거한다.

❹ 최대 50개의 준비된 문장을 캐시함. 준비된 문장 캐싱은 하이버네이트 사용 시 필수적인

성능 최적화 기법이다.

❺ 연결을 자동으로 검증하기 전의 유휴 시간을 초로 나타낸 것

hibernate.C3P0.* 형태로 프로퍼티를 기술하면 C3P0를 연결 풀로 선택한다(C3P0.max_size

옵션이 필요하다. C3P0 지원을 활성화하는 데 필요한 다른 설정은 없다). C3P0는 앞에서 살펴본

예제보다 더 많은 기능을 제공한다. 하이버네이트 배포판의 etc/ 하위 디렉터리에서 더 포괄적인

예제를 확인할 수 있다.

org.hibernate.cfg.Environment 클래스에 대한 자바독에도 모든 하이버네이트 설정 프로퍼

티가 문서화돼 있다. 또한 최신 하이버네이트 설정 프로퍼티는 모두 하이버네이트 레퍼런스 문서

Page 97: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 59

에서 찾아볼 수 있으므로 책에서는 가장 중요한 설정만 설명하겠다. 이제 애플리케이션 개발을

시작할 때 필요한 모든 사항을 확인했다.

FAQ? 내가 직접 만든 연결을 제공할 수 있을까? org.hibernate.connection.Connection

Provider 인터페이스를 구현하고 구현체 이름을 hibernate.connection.

provider_class 설정 프로퍼티에 설정하면 된다. 그럼 하이버네이트가 데이터

베이스 연결이 필요할 때 여러분이 제공한 공급자에 의존하게 된다.

이것으로 하이버네이트 설정 파일을 완성했으니 이제 애플리케이션에서 SessionFactory를

만들 수 있다.

SessionFactory 다루기

대부분의 하이버네이트 애플리케이션에서 SessionFactory는 애플리케이션이 초기화되는 동

안 단 한번만 인스턴스화돼야 한다. 그런 다음 특정 처리를 하는 모든 코드에서는 이 하나의 인

스턴스만 사용해야 하고, 모든 Session은 이 하나의 SessionFactory를 사용해서 만들어야 한다.

SessionFactory는 스레드 안전(thread-safe)하며 공유할 수 있다. Session은 단일 스레드로 동작

하는 객체다.

SessionFactory에 관해 자주 묻는 질문은 팩터리를 만든 다음 어디에 저장하고 별다른 수고

로움 없이 팩터리에 접근하는 방법은 무엇인가이다. JNDI나 JMS처럼 더욱 발전된 방식이면서도

편리한 방법도 있지만, 그것들은 완전한 자바 EE 애플리케이션 서버에서만 사용할 수 있다. 대신

하이버네이트 시작과 SessionFactory 저장 및 가져오기 문제를 모두 해결하는 실용적이면서도

즉시 적용 가능한 해법을 소개하겠다. 바로 정적 전역 변수(static global variable)와 정적 초기화

(static initialization)를 이용하는 것이다.

변수와 초기화는 모두 한 클래스 안에서 구현할 수 있는데, 이를 HibernateUtil이라 부른다.

이 도우미 클래스는 하이버네이트 커뮤니티에서 잘 알려져 있으며 자바 EE 서비스를 사용하지

않는 평범한 자바 애플리케이션에서 하이버네이트를 구동하는 일반적인 패턴이다. 기본 구현은

예제 2.6에 있다.

예제 2.6 하이버네이트 구동과 SessionFactory를 다룰 때 사용하는 HibernateUtil

package persistence;

import org.hibernate.*;

import org.hibernate.cfg.*;

public class HibernateUtil {

Page 98: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

60 l 하이버네이트 완벽 가이드

private static SessionFactory sessionFactory;

static {

try {

sessionFactory = new Configuration().configure()

.buildSessionFactory();

} catch (Throwable ex) {

throw new ExceptionInInitializerError(ex);

}

}

public static SessionFactory getSessionFactory() {

// 또는 여기서 JNDI를 룩업할 수도 있다

return sessionFactory;

}

public static void shutdown() {

// 캐시와 연결 풀을 닫는다

getSessionFactory().close();

}

}

하이버네이트를 구동하는 정적 초기화자(static initializer) 블럭을 만든다. 이 블럭은 이 클래스

가 로딩될 때 이 클래스의 로더에 의해 정확히 한번 실행된다. 애플리케이션에서 HibernateUtil

을 처음으로 호출해서 이 클래스가 로딩되면 SessionFactory를 생성하고 그와 동시에 정적 변수

를 설정한다. 이때 문제가 발생하면 Exception이나 Error는 모두 감싸서 정적 블럭 밖으로 던진

다(그래서 �rowable을 잡는 것이다). 정적 초기화시 ExceptionInInitializerError로 감싸는 것

은 필수다.

이 새로운 클래스를 persistence라는 새로운 패키지에 만들었다. 완전한 기능을 제공하는 하

이버네이트 애플리케이션이라면 종종 그와 같은 패키지가 필요한 경우가 있다. 예를 들면 직접

만든 영속화 계층 인터셉터와 데이터 타입 변환기(data type converter)를 기반 시설의 일부로 포

함하는 경우가 그렇다.

이제 애플리케이션에서 하이버네이트 Session이 필요할 때마다 이전에 살펴본 HelloWorld의

main 애플리케이션 코드에서 했던 것처럼 HibernateUtil.getSessionFactory().openSession()을

사용해서 쉽게 가져올 수 있다.

애플리케이션을 실행하고 테스트할 준비가 거의 다 됐다. 하지만 아마 내부에서 어떤 일이 벌

어지는지 궁금할 것이므로 우선 로깅을 활성화하자.

Page 99: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 61

로깅과 통계 활성화하기

hibernate.show_sql 설정 프로퍼티에 관해서는 이미 살펴본 바 있다. 하이버네이트를 사용해서

소프트웨어를 개발하는 동안에는 계속해서 이 설정 프로퍼티가 필요할 것이다. 이 설정 프로퍼

티는 생성된 모든 SQL을 콘솔에 로깅하게 한다. 이렇게 생성되는 SQL은 문제 해결과 성능 최적

화, 그리고 어떤 일이 벌어지는지 파악할 때 사용할 수 있다. 만약 hibernate.format_sql까지 가

능한 상태로 하면 출력 형태를 좀더 알아보기 쉬운 형태로 출력하지만 그만큼 공간을 더 차지한

다. 세 번째 옵션으로 지금까지 설정하지 않았던 hibernate.use_sql_comments가 있다. 이것은

하이버네이트가 생성된 SQL 문 내부에 주석을 추가하여 SQL문의 출처에 대한 힌트를 제공한

다. 이를테면 특정 SQL 문이 명시적인 쿼리로 인해 만들어진 것인지 아니면 컬렉션 초기화로 인

해 만들어진 것인지 쉽게 알 수 있다.

SQL 결과를 표준 출력(stdout)하도록 하는 것이 첫 번째 로깅 옵션이다. 하이버네이트(를 비롯

한 다른 여러 ORM 구현)는 SQL 문을 비동기적으로 실행한다. INSERT 문은 보통 애플리케이션

이 session.save()를 호출할 때 실행되지 않으며, 애플리케이션에서 item.setPrice()를 호출할 때

도 UPDATE 문이 그 즉시 실행되지 않는다. 대신 보통 트랜잭션이 끝날 때 SQL 문을 실행한다.

이는 ORM 코드를 추적하고 디버깅하는 일이 때로는 쉽지 않다는 뜻이다. 이론적으로는 애플

리케이션이 하이버네이트를 블랙박스로 간주하고 이러한 행위를 무시하는 것이 가능하다. 하지

만 어려운 문제를 해결할 때는 정확히 하이버네이트 내부에서 어떤 일이 벌어지는지 알 필요가

있다. 하이버네이트는 오픈소스이므로 하이버네이트 코드를 쉽게 확인할 수 있고 때로는 그렇게

하는 것이 굉장히 도움될 때가 있다! 노련한 하이버네이트 전문가는 하이버네이트 로그와 매핑

파일만 보고도 문제를 디버깅할 수 있다. 여러분도 하이버네이트가 만들어 낸 로그 결과물을 살

펴보는 데 시간을 투자해서 하이버네이트의 내부 구조에도 익숙해지길 바란다.

하이버네이트는 출력 결과를 아파치 Log4j(log4j.jar를 클래스패스에 둘 경우)나 JDK 1.4로깅

(JDK 1.4 이상을 사용하고 Log4j가 없을 경우)으로 보내는 얇은 추상화 계층인 아파치 커먼즈

로깅(Apache commons-logging)을 이용해서 중요 이벤트를 로깅한다. 우리는 더 성숙하고, 널리

쓰이며, 활발하게 개발되고 있는 Log4j를 추천한다.

Log4j에서 결과를 보려면 log4j.properties라는 파일을 클래스패스(hibernate.properties나

hibernate.cfg.xml 파일 근처)에 둬야 한다. 또한 log4j.jar 라이브러리를 lib 디렉터리에 복사해

넣는 것도 잊지 말자. 예제 2.7의 Log4j 설정 예제는 모든 로그 메시지를 콘솔에 출력한다.

Page 100: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

62 l 하이버네이트 완벽 가이드

예제 2.7 log4j.properties 설정 파일 예제

# 로그 메시지를 표준 출력으로 직접 로깅한다

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.Target=System.out

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

# 루트 로거 옵션

log4j.rootLogger=INFO, stdout

# 하이버네이트 로깅 옵션 (INFO는 구동 메시지만 보여준다)

log4j.logger.org.hibernate=INFO

# JDBC 연동 매개변수의 런타임 인자를 로깅한다

log4j.logger.org.hibernate.type=INFO

이 설정 파일의 마지막 부분이 특히 흥미롭다. 이를 DEBUG 수준으로 설정하면 기초적인 콘

솔 로그에서는 볼 수 없는 JDBC 연동 매개변수 정보를 로깅할 수 있다. 좀 더 포괄적인 예제를 원

한다면 하이버네이트 배포 버전의 etc/ 디렉터리에 들어있는 log4j.properties 파일을 확인하고

Log4j 문서에서 더 많은 정보를 확인할 수 있다. 운영 환경에서는 어떠한 로그도 DEBUG 수준으

로 설정해서는 안 된다. 애플리케이션의 성능에 심각한 영향을 미칠 수 있기 때문이다.

또한 실시간 통계를 활성화해서 하이버네이트를 모니터링할 수 있다. 애플리케이션 서버 없이

(즉, JMX 배포 환경이 없다면) 실행 시간에 하이버네이트 엔진의 통계를 얻는 가장 쉬운 방법은

SessionFactory다.

Statistics stats = HibernateUtil.getSessionFactory().getStatistics();

stats.setStatisticsEnabled(true);

...

stats.getSessionOpenCount();

stats.logSummary();

EntityStatistics itemStats = stats.getEntityStatistics("auction.model.Item");

itemStats.getFetchCount();

Page 101: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 63

통계 인터페이스로는 전역 정보를 나타내는 Statistics, 특정 엔티티에 관한 정보를 나타내는

EntityStatistics, 특정 컬렉션의 역할을 나타내는 CollectionStatistics, SQL과 HQL 쿼리에 관한

QueryStatistics, 선택적인 2차 데이터 캐시의 특정 영역에 관한 구체적인 런타임 정보를 나타내

는 SecondLevelCacheStatistics가 있다. 편의성 메서드로 제공되는 logSummary()는 한번의 호

출로 전체 요약 정보를 콘솔에 출력한다. 만약 통계 수집을 프로그램 방식이 아닌 설정을 이용해

서 활성화하고 싶다면 hibernate.generate_statistics 설정 프로퍼티를 true로 설정하면 된다. 다

양한 통계 관련 메서드에 관한 더 자세한 사항은 API 문서를 참고한다.

"Hello World" 애플리케이션을 실행하기 전에 작업 디렉터리에 필요한 모든 파일이 있는지 확

인하자.

WORKDIR

build.xml

+lib

<필요한 모든 라이브러리>

+src

+hello

HelloWorld.java

Message.java

Message.hbm.xml

+persistence

HibernateUtil.java

hibernate.cfg.xml (또는 hibernate.properties)

log4j.properties

첫 번째 파일인 build.xml은 앤트 빌드를 정의한 것이다. 빌드 정의 내부에는 다음에 논의할

애플리케이션을 빌드하고 실행하는 앤트 타겟이 들어있다. 다음 절에서는 데이터베이스 스키마

를 자동으로 생성할 수 있는 타겟을 추가할 것이다.

2.1.4 애플리케이션 실행과 확인

애플리케이션을 실행하려면 먼저 애플리케이션을 컴파일하고 데이터베이스 관리 시스템을 적절

한 데이터베이스 스키마를 가지고 실행해야 한다.

앤트는 강력한 자바용 빌드 시스템이다 보통 프로젝트마다 build.xml 파일을 작성한 다음 해

당 파일에 정의한 빌드 타겟을 앤트 명령줄 도구로 실행한다. 앤트 실행을 지원하는 자바 IDE를

사용하고 있다면 앤트 타겟을 자바 IDE에서도 호출할 수 있다.

Page 102: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

64 l 하이버네이트 완벽 가이드

앤트로 프로젝트 컴파일하기

이제 build.xml 파일과 "Hello World" 프로젝트에서 사용할 타겟을 추가하겠다. 빌드 파일의 초

기 내용은 예제 2.8과 같다. 이 파일을 WORKDIR에 만든다.

예제 2.8 "Hello World"에서 사용할 기본 앤트 빌드 파일

<project name="HelloWorld" default="compile" basedir=".">

<!-- 프로젝트 이름과 버전 -->

<property name="proj.name" value="HelloWorld" />

<property name="proj.version" value="1.0" />

<!-- 이 빌드에 필요한 전역 프로퍼티 -->

<property name="src.java.dir" value="src" />

<property name="lib.dir" value="lib" />

<property name="build.dir" value="bin" />

<!-- 클래스패스 선언 -->

<path id="project.classpath">

<fileset dir="${lib.dir}">

<include name="**/*.jar" />

<include name="**/*.zip" />

</fileset>

</path>

<!-- 유용한 단축 표현 -->

<patternset id="meta.files">

<include name="**/*.xml" />

<include name="**/*.properties" />

</patternset>

<!-- 정리 -->

<target name="clean">

<delete dir="${build.dir}" />

<mkdir dir="${build.dir}" />

</target>

<!-- 자바 소스 컴파일 -->

<target name="compile" depends="clean">

<mkdir dir="${build.dir}" />

<javac srcdir="${src.java.dir}" destdir="${build.dir}" nowarn="on">

<classpath refid="project.classpath" />

</javac>

Page 103: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 65

</target>

<!-- 메타데이터를 빌드 클래스패스로 복사 -->

<target name="copymetafiles">

<copy todir="${build.dir}">

<fileset dir="${src.java.dir}">

<patternset refid="meta.files" />

</fileset>

</copy>

</target>

<!-- HelloWorld 실행-->

<target name="run" depends="compile, copymetafiles" description="Build and run

HelloWorld">

<java fork="true" classname="hello.HelloWorld" classpathref="project.classpath">

<classpath path="${build.dir}" />

</java>

</target>

</project>

앤트 빌드 파일의 윗 부분 절반은 프로젝트 이름과 파일이나 디렉터리의 전역적인 위치와 같은

프로퍼티 설정을 담고 있다. 또한 이 빌드를 이전에 만들어둔 디렉터리 구조인 WORKDIR을 기

반으로 이 빌드를 작성했음을 알 수 있다(앤트에서 이 디렉터리는 basedir과 같다). 타겟 이름 없

이 빌드 파일을 실행할 때 호출되는 default 타겟은 compile이다.

다음으로 나중에 쉽게 참조할 수 있는 project.classpath는 프로젝트의 라이브러리 디렉터리

에 있는 모든 라이브러리를 참조할 수 있는 단축 표현이다. 또 다른 편의성을 위한 단축 표현으로

meta.�les가 있다. 이 필터를 이용하여 빌드 과정 중에 설정과 메타데이터 파일을 분리하여 다룰

필요가 있다.

clean 타겟은 생성되고 컴파일된 모든 파일을 삭제하고 프로젝트를 깨끗한 상태로 만든다. 마

지막 세 개의 타겟인 compile, copymeta�les, run은 이름으로 짐작할 수 있다. 애플리케이션 실

행은 모든 자바 소스 파일을 컴파일하는 것과 모든 매핑과 프로퍼티 설정 파일을 빌드 디렉터리

로 복사하는 것에 의존한다.

이제 "Hello World" 애플리케이션을 컴파일하기 위해 WORKDIR에서 ant compile을 실행하

자. 컴파일을 할 때 오류(또는 어떠한 경고도)가 없어야 하며 bin 디렉터리에서 컴파일된 클래스

파일을 볼 수 있어야 한다. 또한 ant copymeta�les를 한번 호출하여 모든 설정과 매핑 파일이 올

바르게 bin 디렉터리로 복사됐는지 확인하자.

Page 104: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

66 l 하이버네이트 완벽 가이드

애플리케이션을 실행하기 전에 데이터베이스 관리 시스템을 시작하고 새로운 데이터베이스

스키마를 생성하자.

HSQL 데이터베이스 시스템 시작하기

하이버네이트는 25개가 넘는 SQL 데이터베이스 관리 시스템을 지원하고 있으며, 잘 알려져 있지

않은 방언도 쉽게 추가할 수 있다. 이미 데이터베이스를 사용하고 있거나 기본적인 데이터베이스

관리에 관해 알고 있다면 앞에서 살펴본 설정 옵션(대부분 연결과 방언과 관련된 설정)을 각자

선호하는 시스템에 대한 설정으로 변경할 수 있을 것이다.

이 세상에 안부를 전하려면(hello world) 가볍고 설치 및 설정이 간편하면서도 제 몫을 다 하

는 데이터베이스 시스템이 필요하다. 자바로 작성된 오픈소스 SQL 데이터베이스 관리 시스템인

HSQL은 탁월한 선택이다. HSQL은 메인 애플리케이션의 내부 프로세스로 동작할 수도 있지만,

경험상 보통 TCP 포트로 연결을 대기하는 독립적인 형태로 실행하는 편이 더 편리하다. 이미 앞

에서 hsqldb.jar 파일을 WORKDIR의 라이브러리 디렉터리에 복사한 바 있다. 이 라이브러리에

는 데이터베이스 엔진과 동작중인 HSQL에 연결할 때 필요한 JDBC 드라이버가 모두 들어있다.

HSQLDB 서버를 실행하려면 명령줄을 열고 WORKDIR로 이동한 다음 그림 2.4에 나온 명령

어를 실행하면 된다. 시작 메시지가 나타나고 마지막에는 데이터베이스 시스템을 어떻게 종료하

는지 설명하는 도움말 메시지가 나타날 것이다(Ctrl+C를 써도 무방하다). test 명령을 실행하면

WORKDIR에 새로운 파일들이 몇 개 생길 것이다. 이 파일들은 HSQLDB가 데이터를 저장할 때

사용하는 파일이다. 깨끗한 상태로 데이터베이스를 시작하려면 서버를 다시 시작하기 전에 해당

파일들을 삭제하면 된다.

그림 2.4 | 명령줄에서 HSQLDB 서버 실행하기

이제 아무런 내용도 없는 빈 데이터베이스가 준비됐다. 심지어 스키마도 없다. 자 이제 스키마

를 만들어 보자.

Page 105: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 67

데이터베이스 스키마 추출하기

데이터베이스 스키마는 CREATE 문으로 SQL DDL을 직접 작성한 뒤 데이터베이스에서 해당

DDL을 실행해서 만들 수 있다. 또는 (더 훨씬 편리한 방법으로) 하이버네이트가 애플리케이션

에서 사용할 스키마를 관리하고 만들 수 있게 할 수 있다. 하이버네이트가 자동으로 SQL DDL

을 생성하려면 하이버네이트 매핑 메타데이터 정의가 필요하다. 매핑 메타데이터는 XML 매핑

파일이나 자바 소스 코드를 말한다. 이번에도 이전 절에서 언급한 내용에 따라 도메인 모델 클래

스를 설계하고 구현했으며 매핑 메타데이터를 XML로 작성했다고 가정하겠다.

스키마 생성에 사용할 도구는 hbm2ddl이다. 핵심 클래스는 org.hibernate.tool.hbm2ddl.

SchemaExport이다. 따라서 SchemaExport라고도 부른다.

이 도구를 실행해서 스키마를 만드는 방법은 여러 가지가 있다.

<hbm2ddl>을 빌드 과정에 넣어 앤트 타겟에서 실행할 수 있다. ▒

SchemaExport를 애플리케이션 코드(HibernateUtil 구동 클래스와 같은 곳에서)에서 ▒

프로그램 방식으로 실행할 수 있다. 하지만 보통 그렇게 사용하지는 않는다. 왜냐하면 스

키마 생성을 프로그램 방식으로 제어하는 경우는 드물기 때문이다.

▒ hibernate.hbm2ddl.auto 설정 프로퍼티를 create나 create-drop으로 설정하여

SessionFactory가 만들어질 때 스키마를 자동으로 생성하게 할 수 있다. 첫 번째 설정은

SessionFactory를 만들 때 DROP 이후에 CREATE 문을 실행한다. 두 번째 설정은 애플

리케이션이 종료되고 SessionFactory가 닫힐 때 추가적으로 DROP 문을 추가한다. 즉, 매

번 실행한 뒤에 데이터베이스를 깨끗한 상태로 만드는 것이다.

프로그램 방식으로 스키마를 생성하는 것은 간단하다.

Configuration cfg = new Configuration().configure();

SchemaExport schemaExport = new SchemaExport(cfg);

schemaExport.create(false, true);

새로운 SchemaExport 객체는 Con�guration에서 만든다. 데이터베이스 드라이버, 연결 URL

등의 모든 설정은 SchemaExport 생성자로 넘어간다. create(false, true)를 호출하면 (false 설정

으로 인해) SQL을 표준 출력하지는 않지만, (true 설정으로 인해) 데이터베이스에 DDL을 바로

바로 실행하면서 DDL 생성 과정을 시작한다. 더 자세한 설명과 추가적인 설정은 SchemaExport

API를 참고하자.

각자의 개발 프로세스에 따라 hibernate.hbm2ddl.auto 설정을 자동 스키마 생성으로 설정할

Page 106: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

68 l 하이버네이트 완벽 가이드

지 여부를 결정하자. 대다수의 신규 하이버네이트 사용자는 SessionFactory를 만들 때 스키마

를 없애고 다시 만드는 것을 약간 혼란스러워한다. 일단 하이버네이트에 친숙해진 다음, 통합 테

스트 수행 시 빠른 피드백을 받을 수 있도록 이 옵션을 사용할지 검토해 보길 권장한다.

이 설정 프로퍼티에 대한 부가적인 옵션으로 update가 개발 중에 유용할 수 있다. 이 옵션은

내장된 SchemaUpdate 도구를 활성화하며, 이 도구는 스키마를 쉽게 개선할 수 있게 해준다.

이 옵션을 활성화하면 구동 시에 하이버네이트가 JDBC 데이터베이스의 메타데이터를 읽어 예

전 스키마와 현재 매핑 데이터를 비교하여 새로운 테이블과 제약 조건을 만들어준다. 이 기능은

JDBC 드라이버가 제공하는 메타데이터의 품질에 의존하며, 많은 드라이버는 이 부분에서 미흡

하다. 따라서 실제로 이 기능은 생각만큼 굉장하거나 유용하지는 않다.

주의 하이버네이트 사용자들이 SchemaUpdate를 사용하여 운영 환경의 데이터베

이스를 자동으로 갱신하는 것을 본 적이 있다. 그렇게 하면 금세 재앙이 뒤따

를 것이며 DBA가 그렇게 하도록 가만 놔두지 않을 것이다.

SchemaUpdate도 프로그램 방식으로 실행할 수 있다.

Configuration cfg = new Configuration().configure();

SchemaUpdate schemaUpdate = new SchemaUpdate(cfg);

schemaUpdate.execute(false);

마지막 부분의 false 설정은 SQL DDL을 콘솔에 출력하지 않고 데이터베이스에서 직접 실행하

도록 설정한 것이다. DDL을 콘솔이나 텍스트 파일에 출력하면 DBA가 출력된 내용을 활용해서

품질 좋은 스키마 개선 스크립트를 작성하는 출발점으로 삼을 수 있다.

개발 시에 유용한 또다른 hbm2ddl.auto 설정으로는 validate가 있다. 이것은 구동 시에

SchemaValidator를 실행하게 한다. 이 도구는 매핑과 JDBC 메타데이터를 비교하여 스키마가

매핑과 일치하는지 알려준다. SchemaValidator도 프로그램 방식으로 실행할 수 있다.

Configuration cfg = new Configuration().configure();

new SchemaValidator(cfg).validate();

매핑과 사용중인 데이터베이스 스키마가 일치하지 않으면 예외가 발생한다.

빌드 시스템이 앤트 기반이기 때문에 schemaexport 타겟을 앤트 빌드에 추가하여 필요할 때

마다 데이터베이스에서 쓸 새로운 스키마를 만들고 추출할 수 있다. (예제 2.9)

Page 107: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 69

예제 2.9 스키마 생성용 앤트 타겟

<taskdef name="hibernatetool"

classname="org.hibernate.tool.ant.HibernateToolTask"

classpathref="project.classpath" />

<target name="schemaexport" depends="compile, copymetafiles"

description="Exports a generated schema to DB and file">

<hibernatetool destdir="${basedir}">

<classpath path="${build.dir}" />

<configuration

configurationfile="${build.dir}/hibernate.cfg.xml" />

<hbm2ddl

drop="true"

create="true"

export="true"

outputfilename="helloworld-ddl.sql"

delimiter=";"

format="true" />

</hibernatetool>

</target>

이 타겟에서 가장 먼저 정의한 것은 HibernateToolTask로, 우리가 사용하려는 새로운 앤트 타

겟이다. 이것은 일반적인 태스크로 여러 가지를 할 수 있다. SQL DDL 스키마를 하이버네이트

매핑 데이터로부터 만드는 것도 그 중 하나다. 이번 장에서 사용하는 모든 앤트 빌드에서는 이것

을 사용한다. 하이버네이트 라이브러리, 필요한 서드파티 라이브러리, JDBC 드라이버를 태스크

의 클래스패스에 두었는지 확인하자. 또한 Hibernate Tools 다운로드 패키지에서 찾을 수 있는

hibernate-tools.jar 파일도 필요할 것이다.

schemaexport 앤트 타겟은 이 태스크를 사용하며, 빌드 디렉터리에 컴파일된 클래스와 복

사된 설정 파일에도 의존한다. <hibernatetool> 태스크의 기본적인 사용법은 항상 동일하

다. 코드 생성을 할 때는 항상 configuration이 출발점이다. 그 사례 중 하나로 여기서 사용한

<con�guration>은 하이버네이트 XML 설정 파일과 설정에 들어 있는 모든 하이버네이트 XML

매핑 메타데이터 파일을 읽어들인다. 메타데이터 파일을 읽어 하이버네이트 메타데이터 모델

Page 108: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

70 l 하이버네이트 완벽 가이드

(Hibernate metadata model. hbm이 이것의 약자다)을 내부적으로 만든 뒤, 연쇄적으로 이 모

델 데이터를 추출기(exporter)가 처리한다. 애노테이션이나 역공학에 사용할 데이터베이스를 읽

을 수 있는 도구 설정은 이 장의 후반부에서 살펴보겠다.

schemaexport 타겟의 또 다른 요소는 이른바 추출기라 부르는 것이다. 이 도구 설정은 메타

데이터 정보를 우리가 선택한 추출기에 넘겨준다. 위 예제에서는 <hbm2ddl> 추출기가 여기에

해당한다. 짐작했을지도 모르지만 이 추출기는 하이버네이트 메타데이터 모델을 읽어들여 SQL

DDL을 만든다. DDL 생성은 몇 가지 옵션으로 제어할 수 있다.

추출기는 SQL을 생성하기 때문에 SQL 방언을 하이버네이트 설정 파일에 반드시 설정해 ▒

야 한다.

만약 drop을 true로 설정하면 SQL DROP 문이 먼저 생성되고 기존의 테이블과 제약 조 ▒

건은 모두 삭제될 것이다. create가 true로 설정돼 있으면 다음으로 SQL CREATE 문이

생성되어 테이블과 제약 조건을 만든다. 두 가지 옵션을 모두 true로 설정하면 앤트 타겟

을 실행할 때마다 데이터베이스 스키마를 사실상 삭제하고 다시 만들 것이다.

export를 true로 설정하면 모든 DDL 문을 데이터베이스에서 바로 실행한다. 추출기는 ▒

설정 파일에서 연결 설정 정보를 찾아 데이터베이스에 연결한다.

output�lename을 주면 모든 DDL 문을 해당 파일에 작성하고 destdir로 설정한 곳에 파 ▒

일을 저장한다. delimiter 문자는 파일에 작성된 모든 SQL 문에 추가되며, format을 true

로 설정하면 SQL 문이 보기 좋게 들여쓰기 된다.

이제 WORKDIR에서 ant schemaexport를 실행하여 스키마를 생성하고, 출력하고, 텍스트

파일 또는 데이터베이스로 추출할 수 있다. 모든 테이블과 제약 조건이 삭제된 후 다시 생성되어

깨끗한 상태의 데이터베이스가 마련됐다. (테이블이 존재하지 않기 때문에 삭제하지 못한다는

오류 메시지는 무시해도 된다.)

데이터베이스가 동작하는지, 그리고 데이터베이스 스키마가 올바른지 확인하자. HSQLDB에

포함된 유용한 도구로 간단한 데이터베이스 브라우저가 있다. 아래의 앤트 타겟으로 HSQLDB

브라우저를 호출할 수 있다.

<target name="dbmanager" description="Start HSQLDB manager">

<java

classname="org.hsqldb.util.DatabaseManagerSwing"

fork="yes"

Page 109: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 71

classpathref="project.classpath"

failonerror="true">

<arg value="-url" />

<arg value="jdbc:hsqldb:hsql://localhost/" />

<arg value="-driver" />

<arg value="org.hsqldb.jdbcDriver" />

</java>

</target>

로그인하고 나면 그림 2.5에 보이는 스키마를 볼 수 있을 것이다.

ant run으로 애플리케이션을 실행하고 하이버네이트 로그 결과를 콘솔에서 확인하자. 메시지

가 저장되고, 로딩되고, 출력되는 것을 확인할 수 있을 것이다. HSQLDB 브라우저에서 SQL 쿼리

를 실행하여 직접 데이터베이스에 있는 내용을 확인해보자.

이제 동작하는 하이버네이트 기반 시설과 앤트 프로젝트 빌드가 마련됐다. 다음 장으로 넘

어갈 수도 있고 계속해서 좀더 복잡한 비즈니스 클래스를 작성하고 매핑할 수도 있다. 하지만

"Hello World" 애플리케이션에 조금 더 시간을 투자해서 애플리케이션을 다양한 기능으로 확장

해보길 권장한다. 예를 들어, 전혀 다른 HQL 쿼리나 로딩 옵션을 시도해 볼 수 있겠다. 데이터베

이스 시스템이 계속해서 동작하고 있기 때문에 다시 데이터베이스를 깨끗하게 빈 상태로 만들려

면 깨끗한 스키마를 추출하거나 데이터베이스를 멈추고 파일을 모두 삭제해야 한다는 사실을 잊

지말자.

그림 2.5 | HSQLDB 브라우저와 SQL 콘솔

Page 110: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

72 l 하이버네이트 완벽 가이드

다음 절에서는 "Hello World" 예제를 자바 퍼시스턴스 인터페이스와 EJB 3.0으로 다시 살펴보

겠다.

2.2 자바 퍼시스턴스 프로젝트 시작하기

이어지는 절에서는 JPA의 몇가지 장점과 새로운 EJB 3.0 표준을 살펴보고 애노테이션과 표준화

된 프로그래밍 인터페이스를 사용하면 애플리케이션 개발이 얼마나 단순해지는지(심지어 하이

버네이트와 비교해 봐도) 살펴보겠다. 분명 표준화된 인터페이스를 이용해서 설계하면 애플리케

이션을 다른 런타임 환경에 배포하거나 이식해야 할 때 장점으로 작용한다. 게다가 이식성을 떠

나서 JPA를 자세히 살펴봐야 하는 이유는 여러 가지가 있다.

이제 또 다른 "Hel lo World" 예제를 살펴볼텐데, 이번에는 하이버네이트 애노테이션

(Hibernate Annotations)과 하이버네이트 EntityManager를 사용하겠다. JPA와 하이버네이트의

차이를 볼 수 있게 기본적인 프로젝트 기반 시설은 이전에 소개한 것을 재사용하겠다. 애노테이

션과 JPA 인터페이스를 이용하고 난 다음에는 애플리케이션이 다른 관리형 컴포넌트(EJB)와 연

동하고 상호작용하는 방법을 살펴보겠다. 이 책의 후반부에서 다양한 애플리케이션 설계를 살펴

보겠지만 이번에 살펴볼 내용만으로도 가능한 빨리 특정 접근 방법을 선택할 수 있게 될 것이다.

2.2.1 하이버네이트 애노테이션 사용하기

먼저 하이버네이트 애노테이션을 사용하여 하이버네이트 XML 매핑 파일을 인라인 메타데이터

로 교체해보자. 네이티브 하이버네이트에서 표준 JPA 매핑(과 프로그램 코드)으로 변경하기 전

에 기존의 "Hello World" 프로젝트를 복사해 둔다.

하이버네이트 애노테이션 라이브러리를 WORKDIR/lib 디렉터리로 복사하자. 필요한 라이

브러리 목록은 하이버네이트 애노테이션 문서에서 확인할 수 있다. (이 글을 작성할 시점에는

hibernate-annotations.jar와 ejb3-persistence.jar의 API 스텁 5 이 필요했다.)

이제 src/hello/Message.hbm.xml 파일을 삭제한다. 예제 2.10과 같이 이 파일을 src/hello/

Message.java 클래스 소스 코드의 애노테이션으로 교체하겠다.

5  (옮긴이) 보통 RMI(Remote Method Invocation, 원격 메서드 호출)에서 원격 객체 호출에 필요한 로컬 객체를 의미한다. 여기

서는 일종의 인터페이스로 생각하면 된다.

Page 111: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 73

예제 2.10 Message 클래스를 애노테이션으로 매핑하기

package hello;

import javax.persistence.*;

@Entity

@Table(name = "MESSAGES")

public class Message {

@Id

@GeneratedValue

@Column(name = "MESSAGE_ID")

private Long id;

@Column(name = "MESSAGE_TEXT")

private String text;

@ManyToOne(cascade = CascadeType.ALL)

@JoinColumn(name = "NEXT_MESSAGE_ID")

private Message nextMessage;

private Message() {

}

public Message(String text) {

this.text = text;

}

public Long getId() {

return id;

}

private void setId(Long id) {

this.id = id;

}

public String getText() {

return text;

}

public void setText(String text) {

this.text = text;

Page 112: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

74 l 하이버네이트 완벽 가이드

}

public Message getNextMessage() {

return nextMessage;

}

public void setNextMessage(Message nextMessage) {

this.nextMessage = nextMessage;

}

}

수정된 비즈니스 클래스에서 아마도 가장 먼저 봐야 할 부분은 javax.persistence 인터페이스

를 임포트(import)하는 부분일 것이다. 이 패키지에는 @Entity 클래스를 데이터베이스 @Table

로 매핑할 때 필요한 모든 표준 JPA 애노테이션이 들어있다. 애노테이션을 클래스의 private 필

드에 설정한다. @Id와 @GeneratedValue로 데이터베이스 식별자 매핑을 하는 것부터 시작하자.

JPA 영속성 제공자는 필드에 @Id 애노테이션이 지정돼 있음을 감지하고 실행 시점에 직접 필드

를 통해 객체 프로퍼티에 접근할 것이라 가정한다. 만약 @Id 애노테이션을 getId() 메서드에 놓으

면 기본적으로 접근자 메서드와 설정자 메서드를 통해 프로퍼티에 접근하게 할 수 있다. 따라서

다른 애노테이션도 모두선택한 전략에 따라 필드나 접근자 메서드에 두자.

@Table, @Colum, @JoinColumn 애노테이션이 반드시 필요하지는 않다는 점을 알아두자. 엔

티티의 모두 프로퍼티는 기본적인 전략에 따라 테이블과 열 이름으로 자동적으로 영속화 대상

으로 간주되기 때문이다. 여기서 이것들을 사용한 이유는 매핑을 더욱 명시적으로 하고 XML 매

핑 파일을 사용한 것과 동일한 결과를 내기 위해서다. 현재 두 가지 메타데이터 전략을 비교해보

자면 애노테이션이 훨씬 편리하고 메타데이터 줄 수를 눈에 띄게 줄일 수 있다. 또한 애노테이션

은 타입 안전성을 보장하며, IDE에서 입력할 때 (마치 다른 자바 인터페이스와 같이) 자동 완성

이 지원되고 클래스와 프로퍼티를 리팩터링하기가 더 쉽다.

만약 JPA 인터페이스를 임포트하여 코드가 해당 패키지에 묶이는 것이 걱정된다면 하이버네

이트가 해당 애노테이션들을 사용하는 실행 시점에만 해당 패키지가 클래스패스에 있으면 된다

는 사실을 알아두자. 즉, 하이버네이트로 인스턴스를 저장하거나 로딩하고 싶지 않다면 JPA 인터

페이스가 클래스패스에 없더라도 클래스를 로딩해서 사용할 수 있다.

애노테이션을 처음 접하는 개발자들의 두 번째 관심사는 설정 메타데이터가 자바 소스 코드

에 포함돼 있다는 것이다. 기본적으로 설정 메타데이터는 테이블 이름처럼 배포할 때마다 변경

될 수 있다. 이 문제에 대한 JPA의 해결책은 간단하다. 즉, 모든 애노테이션 메타데이터를 XML

Page 113: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 75

메타데이터 파일로 재정의하거나 교체하면 된다. 이 방법은 이 책의 후반부에서 살펴보겠다.

JPA를 사용할 때 바라는 건 XML 대신 애노테이션을 사용하는 것뿐이라고 가정해 보자. 그렇

다면 JPA 프로그래밍 인터페이스나 쿼리 언어를 사용하고 싶지 않을 것이다. 대신 하이버네이트

Session과 HQL을 사용할 것이다. 못 쓰게 된 XML 파일을 모두 삭제하는 것 말고 프로젝트에서

변경해야 할 사항은 hibernate.cfg.xml 하이버네이트 설정 파일을 변경하는 것뿐이다.

<!DOCTYPE hibernate-configuration SYSTEM

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

<hibernate-configuration>

<session-factory>

<!-- ... 각종 프로퍼티 설정 ... -->

<!-- 애노테이션을 설정한 클래스 목록 -->

<mapping class="hello.Message" />

</session-factory>

</hibernate-configuration>

전에 살펴본 하이버네이트 설정 파일에는 모든 XML 매핑 파일의 목록이 들어 있었다.

이 목록은 애노테이션을 설정한 모든 클래스의 목록으로 교체되었다. 프로그램 방식으로

SessionFactory를 설정했다면 addResource() 메서드를 addAnnotatedClass() 메서드로 대체해

야 한다.

// hibernate.properties로부터 설정을 로딩

AnnotationConfiguration cfg = new AnnotationConfiguration();

// ... 다른 설정 옵션을 프로그램 방식으로 구성

cfg.addAnnotatedClass(hello.Message.class);

SessionFactory sessionFactory = cfg.buildSessionFactory();

기본적인 하이버네이트 Configuration 인터페이스가 아닌 AnnotationConfiguration

을 쓰고 있음을 주목하자. 이 확장 기능은 애노테이션을 지정한 클래스를 인식한다. 최소

한 HibernateUtil의 초기화 과정에서도 이 인터페이스를 쓰도록 변경해야 한다. 데이터베

이스 스키마를 앤트 타겟으로 생성하고 있다면 build.xml 파일에서 <configuration>을

<annotationcon�guration>으로 교체하면 된다.

애노테이션을 사용하는 "Hello World" 애플리케이션을 실행하기 위해 변경해야 할 부분은 방

금 설명한 것뿐이다. 깨끗한 상태의 데이터베이스로 다시 실행해 보자.

Page 114: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

76 l 하이버네이트 완벽 가이드

"Hello World" 애플리케이션에서는 그럴 필요가 없겠지만 애노테이션 메타데이터를 전역적으

로 설정할 수도 있다. 전역 애노테이션 메타데이터는 특정 패키지에 들어있는 package-info.java

라는 파일에 들어있다. 애노테이션 클래스 목록을 나열하는 것과 더불어 전역 메타데이터를 담

은 패키지도 설정에 추가해야 한다. 예를 들면, 하이버네이트 XML 설정에 다음과 같은 내용을

추가해야 한다.

<!DOCTYPE hibernate-configuration SYSTEM

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

<hibernate-configuration>

<session-factory>

<!-- ... 각종 프로퍼티 설정 -->

<!-- 애노테이션을 설정한 클래스 목록 -->

<mapping class="hello.Message" />

<!-- package-info.java가 포함된 패키지 목록 -->

<mapping package="hello" />

</session-factory>

</hibernate-configuration>

또는 프로그램 방식을 통한 설정으로도 동일한 결과를 얻을 수 있다.

// hibernate.properties에서 설정을 불러온다.

AnnotationConfiguration cfg = new AnnotationConfiguration();

// ... 다른 설정 옵션을 프로그램 방식으로 설정한다.

cfg.addClass(hello.Message.class);

cfg.addPackage("hello");

SessionFactory sessionFactory = cfg.buildSessionFactory();

한발 더 나아가 메시지를 읽고 저장하는 네이티브 하이버네이트 코드를 JPA를 사용하는 코드

로 교체해보자. 하이버네이트 애노테이션과 하이버네이트 EntityManager를 사용하면 이식가

능하며 표준을 준수하는 매핑과 데이터 접근 코드를 만들 수 있다.

2.2.2 하이버네이트 EntityManager 사용하기

하이버네이트 EntityManager는 JPA 프로그래밍 인터페이스를 제공하는 하이버네이트 코어를

감싼 래퍼(wrapper)에 해당한다. JPA 프로그래밍 인터페이스는 JPA 엔티티 생명주기를 지원하

Page 115: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 77

며, 쿼리를 표준 자바 퍼시스턴스 쿼리 언어로 작성할 수 있게 해준다. JPA 기능은 하이버네이트

고유 기능의 부분 집합인데, 왜 하이버네이트를 기반으로 하는 EntityManager를 사용해야 하

는지 의아할지도 모르겠다. 본 절의 후반부에서 그렇게 했을 때의 장점을 나열하겠지만, 당장 하

이버네이트 EntityManager를 설정하자마자 알 수 있는 한 가지 특별한 단순함이 있다. 바로 애

노테이션이 지정된 클래스(또는 XML 매핑 파일)를 더는 설정 파일에 나열할 필요가 없다는 점

이다.

"Hello World" 프로젝트를 수정해서 완전히 JPA와 호환가능하게 바꿔보자.

기본 JPA 설정

SessionFactory는 하이버네이트 애플리케이션에서 논리적인 데이터 저장소 설정을 나타낸다.

EntityManagerFactory는 JPA 애플리케이션에서 같은 역할을 하고 있으며, SessionFactory를

설정한 것처럼 설정 파일이나 애플리케이션 코드로 EntityManagerFactory(EMF)를 설정한다.

매핑 메타데이터(보통 애노테이션 클래스)를 포함해서 EMF 설정을 영속화 단위(persistence

unit)라 부른다.

영속화 단위란 개념에는 애플리케이션 패키징도 포함되지만, "Hello World" 애플리케이션에

대해서는 가능한 단순하게 유지하고자 한다. 여기서는 별다른 패키징 없이 표준 JPA 설정으로

시작한다고 가정하겠다. 영속화 단위에 대해서는 JPA 설정 파일의 내용뿐 아니라 설정 파일의 위

치와 이름까지도 표준화되어 있다.

WORKDIR/etc/META-INF라는 디렉터리를 만들고, 예제 2.11에 보이는 persistence.xml이

라는 기본 설정 파일을 해당 디렉터리에 둔다.

예제 2.11 영속화 단위 설정 파일

<persistence xmlns="http://java.sun.com/xml/ns/persistence"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence

http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"

version="1.0">

<persistence-unit name="helloworld">

<properties>

<property name="hibernate.ejb.cfgfile" value="/hibernate.cfg.xml" />

</properties>

</persistence-unit>

</persistence>

Page 116: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

78 l 하이버네이트 완벽 가이드

모든 영속화 단위는 이름이 있어야 하며, 여기서는 helloworld라 하겠다.

참고 위 영속화 단위 설정 파일의 XML 헤더에는 어떤 스키마를 사용해야 하는지

선언했으며, 선언한 내용은 항상 같다. 다음 예제부터는 그 부분을 생략하고

여러분이 추가한다고 가정하겠다.

영속화 단위에는 벤더 종속적인 프로퍼티가 다수 설정될 수 있다. 이전 예제인 hibernate.ejb.

cfg�le 프로퍼티는 다양한 용도로 설정한 것이다. hibernate.cfg.xml 파일(클래스패스 최상단에

있는)에는 이 영속화 단위에 필요한 모든 설정이 들어있기 때문에 기존의 하이버네이트 설정을

재사용하겠다. 나중에 모든 세부적인 설정은 persistence.xml 파일로 옮기겠지만, 지금은 JPA를

사용하여 "Hello World"를 실행하는 데만 집중하자.

JPA 표준에는 persistence.xml 파일이 배포할 영속화 단위의 META-INF 디렉터리에 있어야

한다고 명시되어 있다. 여기서는 실제로 영속화 단위를 패키징하거나 배포하지는 않을 것이므로

persistence.xml을 빌드 결과 디렉터리의 META-INF 디렉터리에 복사해야 한다. build.xml을

수정하고 다음을 copymeta�les 타겟에 추가하자.

<property name="src.etc.dir" value="etc" />

<target name="copymetafiles">

<!-- 빌드에 필요한 메타데이터 복사 -->

<copy todir="${build.dir}">

<fileset dir="${src.java.dir}">

<patternset refid="meta.files" />

</fileset>

</copy>

<!-- etc/ 에서 설정 파일 복사 -->

<copy todir="${build.dir}">

<fileset dir="${src.etc.dir}">

<patternset refid="meta.files" />

</fileset>

</copy>

</target>

WORKDIR/etc에서 meta.�les 패턴으로 찾을 수 있는 모든 파일을 실행 시 클래스패스의 일

부분인 빌드 결과 디렉터리로 복사한다.

Page 117: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 79

메인 애플리케이션을 JPA로 다시 작성해보자.

JPA로 작성한 "Hello World"

다음은 자바 퍼시스턴스에서 주로 사용하는 프로그래밍 인터페이스다.

▒ javax.persistence.Persistence: EntityManagerFactory 생성에 사용할 정적 메서드를 제

공하는 시작 클래스

▒ javax.persistence.EntityManagerFactory: 하이버네이트 SessionFactory에 해당한다. 이

런타임 객체는 특정 영속화 단위를 나타낸다. 스레드 안전하고, 보통 싱글톤으로 사용하

며, EntityManager 인스턴스 생성에 필요한 메서드를 제공한다.

▒ javax.persistence.EntityManager: 하이버네이트 Session에 해당한다. 단일 스레드로 동

작하고 비공유 객체(nonshared object)이며 특정 데이터 접근에 대한 작업 단위를 표현한

다. 엔티티 인스턴스의 생명주기를 관리하는 메서드와 Query 인스턴스를 만드는 메서드

를 제공한다.

▒ javax.persistence.Query: 하이버네이트 Query에 해당한다. 특정 JPA 쿼리 언어나 네이

티브 SQL 쿼리를 나타내는 객체로서, 매개변수를 안전하게 연동할 수 있고 쿼리 실행에

필요한 다양한 메서드를 제공한다.

▒ javax.persistence.EntityTransaction: 하이버네이트 Transaction에 해당하며, 자바 SE

환경에서 RESOURCE_LOCAL 트랜잭션의 경계를 설정하는 데 사용된다. 자바 EE에

서 프로그램 방식의 트랜잭션 경계를 설정하려면 표준화된 JPA의 javax.transaction.

UserTransaction 인터페이스를 사용해야 한다.

JPA 인터페이스를 사용하려면 WORKDIR/lib 디렉터리에 필요한 라이브러리를 복사해야 한

다. 어떤 라이브러리가 필요한지는 최신 하이버네이트 EntityManager에 포함돼 있는 문서를 확

인하기 바란다. 이제 WORKDIR/src/hello/HwlloWorld.java 코드를 다시 작성하고 하이버네이

트에서 JPA 인터페이스로 교체해보자(예제 2.12).

예제 2.12 JPA로 작성한 "Hello World" 메인 애플리케이션

package hello;

import java.util.*;

import javax.persistence.*;

Page 118: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

80 l 하이버네이트 완벽 가이드

public class HelloWorld {

public static void main(String[] args) {

// EntityManagerFactory 시작

EntityManagerFactory emf = Persistence.createEntityManagerFactory("helloworld");

// 첫 번째 작업 단위

EntityManager em = emf.createEntityManager();

EntityTransaction tx = em.getTransaction();

tx.begin();

Message message = new Message("Hello World");

em.persist(message);

tx.commit();

em.close();

// 두 번째 작업 단위

EntityManager newEm = emf.createEntityManager();

EntityTransaction newTx = newEm.getTransaction();

newTx.begin();

List messages = newEm

.createQuery("select m from Message m order by m.text asc")

.getResultList();

System.out.println(messages.size() + " message(s) found");

for (Object m : messages) {

Message loadedMsg = (Message) m;

System.out.println(loadedMsg.getText());

}

newTx.commit();

newEm.close();

// 애플리케이션 종료

emf.close();

}

}

Page 119: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 81

위 예제에서 가장 먼저 눈에 띄는 것은 javax.persistence.*을 제외한 어디서도 하이버네이트를

임포트하지 않는다는 것이다. EntityManagerFactory는 영속화 단위의 이름을 Persistence의 정

적 메서드에 전달해서 생성했다. 나머지 코드는 API상의 차이가 약간 있고 메서드 이름이 조금

씩 다르지만 JPA를 마치 하이버네이트처럼 사용하고 있기 때문에 그 자체로 무엇을 하는지 금방

이해할 수 있다. 또한 기반 시설에 대한 정적인 초기화를 담당하던 HibernateUtil 클래스는 사용

하지 않았다. 원한다면 JPAUtil 클래스를 작성해서 EntityManagerFactory 생성을 그 쪽으로 옮

기거나 지금은 사용하지 않는 WORKDIR/src/persistence 패키지를 삭제할 수 있다.

JPA는 옵션으로 구성된 맵을 사용하는 프로그램 방식의 설정도 지원한다.

Map myProperties = new HashMap();

myProperties.put("hibernate.hbm2ddl.auto", "create-drop");

EntityManagerFactory emf =

Persistence.createEntityManagerFactory("helloworld", myProperties);

직접 만든 프로그램 방식 프로퍼티는 persistence.xml 설정 파일에서 설정한 모든 속성을 재정

의한다.

하이버네이트에서 JPA로 변경한 HelloWorld 코드를 깔끔한 상태의 데이터베이스에서 실행해

보자. JPA 영속성 제공자 엔진이 하이버네이트이므로 하이버네이트로 했을 때와 동일한 로그가

출력되는 모습을 확인할 수 있을 것이다.

메타데이터 자동 감지

앞서 애노테이션을 지정한 클래스나 XML 매핑 파일을 (하이버네이트) 설정에 나열할 필요가 없

다고 약속한 바 있지만, 여전히 hibernate.cfg.xml에는 해당 설정이 남아있다. JPA의 자동 감지

기능을 활성화해보자.

org.hibernate 패키지에 대한 로깅을 DEBUG로 변경하고 "Hello World" 애플리케이션을 다

시 실행하자. 로그에 다음과 같은 추가적인 메시지가 보일 것이다.

...

Ejb3Configuration:141

- Trying to find persistence unit: helloworld

Ejb3Configuration:150

- Analyse of persistence.xml: file:/helloworld/build/META-INF/persistence.xml

PersistenceXmlLoader:115

- Persistent Unit name from persistence.xml: helloworld

Ejb3Configuration:359

- Detect class: true; detect hbm: true

Page 120: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

82 l 하이버네이트 완벽 가이드

JarVisitor:178

- Searching mapped entities in jar/par: file:/helloworld/build

JarVisitor:217

- Filtering: hello.HelloWorld

JarVisitor:217

- Filtering: hello.Message

JarVisitor:255

- Java element filter matched for hello.Message

Ejb3Configuration:101

- Creating Factory: helloworld

애플리케이션이 시작될 때 Persistence.createEntityManagerFactory() 메서드는 이름이

helloworld인 영속화 단위를 찾으려고 한다. 클래스패스에 있는 모든 META-INF/persistence.

xml 파일을 찾아 대응하는 것이 있으면 EMF를 설정한다. 로그의 두 번째 부분은 아마 예상

하지 못했을 것이다. JPA 영속성 제공자는 모든 애노테이션을 지정한 클래스와 하이버네이트

XML 매핑 파일을 빌드 결과 디렉터리에서 찾는다. hello.Message라는 애노테이션을 지정한 엔

티티 클래스는 이미 찾았으므로 hibernate.cfg.xml에 나열한 애노테이션을 지정한 클래스(또는

XML 매핑 파일 목록)를 설정할 필요가 없다.

이처럼 불필요한 옵션을 hibernate.cfg.xml에서 제거하는 대신 파일 자체를 지우고 모든 설정

세부사항을 persistence.xml로 옮긴다(예제 2.13).

예제 2.13 완전한 영속화 단위 설정 파일

<persistence-unit name="helloworld">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<!-- 필요 없음. 하이버네이트는 JSE 환경에서 자동 감지를 지원한다.

<class>hello.Message</class>

-->

<class>hello.Message</class>

-->

<properties>

<property name="hibernate.archive.autodetection"

value="class, hbm" />

<property name="hibernate.show_sql" value="true" />

<property name="hibernate.format_sql" value="true" />

Page 121: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 83

<property name="hibernate.connection.driver_class"

value="org.hsqldb.jdbcDriver" />

<property name="hibernate.connection.url"

value="jdbc:hsqldb:hsql://localhost" />

<property name="hibernate.connection.username"

value="sa" />

<property name="hibernate.c3p0.min_size"

value="5" />

<property name="hibernate.c3p0.max_size"

value="20" />

<property name="hibernate.c3p0.timeout"

value="300" />

<property name="hibernate.c3p0.max_statements"

value="50" />

<property name="hibernate.c3p0.idle_test_period"

value="3000" />

<property name="hibernate.dialect"

value="org.hibernate.dialect.HSQLDialect" />

<property name="hibernate.hbm2ddl.auto" value="create" />

</properties>

</persistence-unit>

설정 파일에서 새로운 요소 세 가지를 볼 수 있다. 먼저 명시적으로 <provider>를 사용하여 이

영속화 단위에서 사용할 영속성 제공자를 설정했다. 보통 이 설정은 동시에 여러가지 JPA 구현

체를 사용할 때 필요한 옵션이지만, 이 옵션에는 하이버네이트 하나만 지정하길 바란다. 다음으

로 명세에서는 자바 EE가 아닌 환경에 배포할 때 <class> 엘리먼트로 모든 애노테이션을 지정한

클래스를 나열하도록 요구하지만 하이버네이트는 어디서든 매핑 메타데이터 자동 감지를 지원

하기 때문에 이 요소를 반드시 지정할 필요는 없다. 마지막으로 archive.autodetection 하이버네

이트 설정은 하이버네이트에게 어떤 메타데이터를 자동으로 감지할지 알려준다. 애노테이션을

지정한 클래스(class)와 하이버네이트 XML 매핑 파일(hbm)를 둘 다 혹은 그 중 한 종류만 감지

할지 설정한다. 기본적으로 하이버네이트 EntityManager는 둘 다 감지한다. 설정 파일의 나머지

부분은 이미 이번 장에서 설명한 내용과 일반적인 hibernate.cfg.xml 파일에 있던 것이다.

애노테이션을 지정한 클래스와 XML 매핑 파일 자동 감지는 JPA의 훌륭한 기능이다. 보통 이

Page 122: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

84 l 하이버네이트 완벽 가이드

기능은 자바 EE 애플리케이션 서버에서만 사용할 수 있다. 최소한 EJB 3.0 명세에서 이것을 보장

한다. 하지만 하이버네이트도 하나의 JPA 공급자로서 일반 자바 SE 환경에서도 이 기능을 구현

하고 있다. 그러나 설정 방법은 다른 JPA 공급자와 다를 수 있다.

이제 완전히 JPA 명세에 준하는 애플리케이션을 만들었다. 프로젝트 디렉터리는 다음과 같을

것이다(log4j.properties도 etc/ 디렉터리로 옮긴 것을 주목하자).

WORKDIR

+etc

log4j.properties

+META-INF

persistence.xml

+lib

<필요한 모든 라이브러리>

+src

+hello

HelloWorld.java

Message.java

모든 JPA 설정은 persistence.xml에 들어있고 모든 매핑 메타데이터는 Message 클래스의 자

바 소스 코드에 포함돼 있으며, 구동시 하이버네이트는 자동으로 메타데이터를 감지하고 찾아낸

다. 순수 하이버네이트와 비교했을 때 다음과 같은 이점이 있다.

대규모 프로젝트에서 중요한 기능인 배포된 메타데이터의 자동 감지. 대규모 팀에서 개발 ▒

하는 엔티티의 수가 수 백개에 이른다면 애노테이션을 지정한 클래스나 매핑 파일 목록

을 관리하는 일이 힘들어진다.

설정 파일의 표준 위치와 배포 개념(영속성 단위)을 비롯한 표준화되고 단순한 설정. 이 ▒

는 여러 단위(JAR)를 하나의 애플리케이션 아카이브(EAR)로 묶는 대형 프로젝트에서

유용하다.

표준화된 데이터 접근 코드와 엔티티 인스턴스 생명주기, 완전한 이식성을 갖춘 쿼리. 애 ▒

플리케이션에 비표준 코드를 임포트하지 않는다.

방금 나열한 내용은 JPA가 제공하는 이점에만 해당한다. 완전한 EJB 3.0 프로그래밍 모델과

다른 관리형 컴포넌트를 함께 사용한다면 JPA의 진정한 위력을 확인할 수 있을 것이다.

Page 123: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 85

2.2.3 EJB 컴포넌트 도입하기

자바 퍼시스턴스는 EJB 3.0 세션 빈과 메시지 주도 빈(등 자바 EE 5.0 표준)을 쓸 때도 빛을 발한

다. EJB 3.0 표준은 영속화 메커니즘을 통합할 수 있게 설계되었기 때문에, 이를테면 빈 메서드

경계에 자동적인 트랜잭션 경계 설정이나, 상태 유지 세션(stateful session) EJB의 생명주기를 영

속성 컨텍스트(Session을 생각하면 된다)로 확장하는 것이 가능하다.

이 절에서는 EJB 3.0와 JPA를 관리형 자바 EE 환경에서 사용해보겠다. 이번에도 "Hello

World" 애플리케이션을 수정하면서 기본기를 익힐 것이다. 먼저 자바 EE 환경, 즉 자바 EE 서비

스를 제공하는 런타임 컨테이너가 필요하다. 이러한 런타임 컨테이너를 얻는 방법은 두 가지다.

EJB 3.0과 JPA를 지원하는 완전한 ▒ 자바 EE 5.0 애플리케이션 서버를 설치한다. 이 글

을 쓰고 있는 지금 몇 가지 오픈소스(썬의 GlassFish 6 , 제이보스의 AS 7 , 오브젝트웹의

EasyBeans 8 )를 비롯해서 상용 라이선스를 가진 것들이 있는데, 이 책을 읽을 때쯤이면

더 많은 것들이 있을지도 모르겠다.

전체 자바 EE 5.0 번들에서는 필요한 서비스만 선택적으로 제공해주는 모듈화 서버를 설 ▒

치한다. 최소한 EJB 3.0 컨테이너, JTA 트랜잭션 서비스, JNDI 레지스트리가 필요할 것이

다. 이 책을 쓰는 지금은 제이보스 AS만 모듈화된 자바 EE 5.0 서비스를 사용자가 조정하

기 쉬운 형태로 제공하고 있다.

간단하게 EJB 3.0을 시작하는 것이 얼마나 쉬운지 보여주기 위해 모듈화된 서버인 제이보스

애플리케이션 서버를 설치하고 설정한 다음 필요한 자바 EE 5.0 서비스만 이용해 보겠다.

EJB 컨테이너 설치하기

http://jboss.com/products/ejb3으로 이동해서 모듈화되어 있고 내장할 수 있는(embeddable) 서

버를 다운로드해서 압축을 푼다. 서버에 들어 있는 모든 라이브러리를 프로젝트의 WORKDIR/

lib 디렉터리에 복사하고 포함돼 있는 모든 설정 파일을 WORKDIR/src 디렉터리에 복사한다. 그

러고 나면 디렉터리 구조는 다음과 같을 것이다.

WORKDIR

+etc

default.persistence.properties

6  https://glassfish.dev.java.net/

7  http://www.jboss.org/jbossas.html

8  http://wiki.easybeans.org/xwiki/bin/view/Main/WebHome

Page 124: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

86 l 하이버네이트 완벽 가이드

ejb3-interceptors-aop.xml

embedded-jboss-beans.xml

jndi.properties

log4j.properties

+META-INF

helloworld-beans.xml

persistence.xml

+lib

<필요한 모든 라이브러리>

+src

+hello

HelloWorld.java

Message.java

제이보스의 내장 가능한 서버는 자바 퍼시스턴스를 하이버네이트에 의존하기 때문에 default.

persistence.properties 파일에는 (JTA 통합 설정 같은) 배포에 필요한 모든 하이버네이트 기본

설정이 포함돼 있다. ejb3-interceptor-aop.xml과 embedded-jboss-beans.xml 설정 파일에는 서

버의 서비스 설정이 들어있다. 이 파일들을 살펴봐도 되지만 지금 당장 수정할 필요는 없다. 기본

적으로 이 글을 작성하는 시점에서 활성화 가능한 서비스로는 JNDI, JCA, JTA, EJB 3.0 컨테이

너이며, 정확히 여기서 필요한 것들이다.

"Hello World" 애플리케이션을 이전(migrate)하려면 관리형 데이터소스(datasource)가 필요하

다. 관리형 데이터소스란 데이터소스 연결을 내장 가능한 서버가 관리해주는 데이터소스다. 서

버에서 제공하는 데이터소스를 설정하는 가장 쉬운 방법은 데이터소스를 관리형 서비스로 배

포하는 설정 파일을 추가하는 것이다. 예제 2.14에 있는 내용을 WORKDIR/etc/META-INF/

helloworld-beans.xml로 만들자.

예제 2.14 제이보스 서버의 데이터소스 설정 파일

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

<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="urn:jboss:bean-deployer bean-deployer_1_0.xsd"

xmlns="urn:jboss:bean-deployer:2.0">

<!-- JCA 데이터소스를 JNDI를 통해 사용할 수 있게 활성화 -->

<bean name="helloWorldDatasourceFactory"

class="org.jboss.resource.adapter.jdbc.local.LocalTxDataSource">

<property name="jndiName">java:/HelloWorldDS</property>

Page 125: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 87

<!-- HSQLDB -->

<property name="driverClass">

org.hsqldb.jdbcDriver

</property>

<property name="connectionURL">

jdbc:hsqldb:hsql://localhost

</property>

<property name="userName">sa</property>

<property name="minSize">0</property>

<property name="maxSize">10</property>

<property name="blockingTimeout">1000</property>

<property name="idleTimeout">100000</property>

<property name="transactionManager">

<inject bean="TransactionManager" />

</property>

<property name="cachedConnectionManager">

<inject bean="CachedConnectionManager" />

</property>

<property name="initialContextProperties">

<inject bean="InitialContextProperties" />

</property>

</bean>

<bean name="HelloWorldDS" class="java.lang.Object">

<constructor factoryMethod="getDatasource">

<factory bean="helloWorldDatasourceFactory" />

</constructor>

</bean>

</deployment>

이번에도 XML 헤더와 스키마 설정은 이 예제에서 중요하지 않다. 여기서는 빈을 두 개 설정했

다. 첫 번째는 빈의 두 번째 타입의 빈을 만들 수 있는 팩터리다. LocalTxDataSource는 사실상

데이터베이스 연결 풀이며, 모든 연결 풀 설정을 이 팩터리에다 할 수 있다. 팩터리는 관리형 데이

터소스를 java:/HelloWorldDS라는 JNDI 이름으로 연동한다.

두 번째 빈 설정은 HelloWorldDS라는 이름으로 등록된 객체를 또 다른 서비스에서 JNDI

레지스트리로부터 룩업할 때 어떻게 인스턴스화되어야 할지 선언한다. "Hello World" 애

플리케이션은 데이터소스를 이 이름으로 요청하고 서버는 LocalTxDataSource 팩터리의

getDatasource()를 호출해서 데이터소스를 가져온다.

Page 126: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

88 l 하이버네이트 완벽 가이드

또한 가독성을 위해 프로퍼티 값에 줄바꿈을 추가했음을 알아두자. (데이터베이스 username

에 줄바꿈이 포함돼 있지 않는 한) 실제 설정 파일에서는 그렇게 하면 안 된다.

영속화 단위 설정하기

다음으로, "Hello World" 애플리케이션의 영속화 단위 설정을 변경하여 리소스 로컬 연결 풀 대

신 관리형 JTA 데이터소스에 접근하게 해야 한다. WORKDIR/etc/META-INF/persistence.

xml 파일을 다음과 같이 수정한다.

<persistence .>

<persistence-unit name="helloworld">

<jta-data-source>java:/HelloWorldDS</jta-data-source>

<properties>

<property name="hibernate.show_sql" value="true" />

<property name="hibernate.format_sql" value="true" />

<property name="hibernate.dialect"

value="org.hibernate.dialect.HSQLDialect" />

<property name="hibernate.hbm2ddl.auto" value="create" />

</properties>

</persistence-unit>

</persistence>

연결 풀과 데이터베이스 연결 설정처럼 더는 의미가 없는 여러 하이버네이트 설정 옵션은 제거

했다. 대신 <jta-data-source> 프로퍼티를 JNDI에 연동한 데이터소스 이름으로 설정했다. 하지

만 여전히 default.persistence.properties에 없는 하이버네이트 옵션과 적절한 SQL 방언을 설정

해야 한다는 사실을 잊어서는 안 된다.

환경을 설치하고 설정하는 일은 이제 끝났으니 (jndi.properties 파일의 용도를 잠시 살펴본

뒤) 애플리케이션 코드를 EJB로 다시 작성해보자.

EJB 작성하기

서버가 제공하는 컴포넌트로 애플리케이션을 설계하고 만드는 방법에는 여러 가지가 있다.

"Hello World" 애플리케이션은 정교한 예제를 보여줄 만큼 복잡하지 않기 때문에 가장 기본적

인 EJB 타입인 무상태 세션 빈을 사용하겠다. (엔티티 클래스는 이미 살펴본 바 있다. 영속 인스

턴스를 생성할 수 있게 애노테이션을 지정한 일반 자바 클래스 말이다. 엔티티 빈이라는 용어는

예전 EJB 2.1의 엔티티 빈을 뜻하는 말임을 주의하라. EJB 3.0과 자바 퍼시스턴스는 일반 엔티티

클래스를 대상으로 경량 프로그래밍 모델을 표준화했다.)

Page 127: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 89

모든 EJB 세션 빈은 비즈니스 인터페이스를 필요로 한다. 이 인터페이스는 미리 정의된 메서드

를 가지고 있는 인터페이스를 구현하거나 기존의 것을 상속할 필요가 있는 특수한 인터페이스가

아니며, 평범한 인터페이스에 불과하다. 다음 인터페이스를 WORKDIR/src/hello 패키지에 작성

한다.

package hello;

public interface MessageHandler {

public void saveMessages();

public void showMessages();

}

MessageHandler는 메시지를 저장하고 보여줄 수 있다. 코드는 매우 직관적이다. 실제로 EJB

는 이 비즈니스 인터페이스를 구현한 것으로, 기본적으로 로컬 인터페이스로 간주된다(즉, 원격

EJB 클라이언트는 호출할 수 없다). 예제 2.15를 보자.

예제 2.15 "Hello World" EJB 세션 빈의 애플리케이션 코드

package hello;

import javax.ejb.Stateless;

import javax.persistence.*;

import java.util.List;

@Stateless

public class MessageHandlerBean implements MessageHandler {

@PersistenceContext

EntityManager em;

public void saveMessages() {

Message message = new Message("Hello World");

em.persist(message);

}

public void showMessages() {

List messages =

em.createQuery("select m from Message m order by m.text asc")

.getResultList();

Page 128: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

90 l 하이버네이트 완벽 가이드

System.out.println(messages.size() + " message(s) found:");

for (Object m : messages) {

Message loadedMsg = (Message) m;

System.out.println(loadedMsg.getText());

}

}

}

이 구현에는 살펴봐야 할 흥미로운 것들이 몇 가지 있다. 먼저 이 클래스는 다른 패키지

에 강한 의존성을 가지지 않는 일반 자바 클래스다. 이 클래스는 @Stateless라는 메타데이

터 애노테이션 하나로 EJB가 된다. EJB는 컨테이터가 관리하는 서비스를 지원하기 때문에 @

PersistenceContext 애노테이션을 적용하면 무상태 세션 빈의 어떤 메서드를 호출하든 관계없

이 서버가 새로운 EntityManager 인스턴스를 주입해준다. 또한 모든 메서드는 자동으로 컨테이

너에 의해 트랜잭션이 적용된다. 트랜잭션은 메서드가 호출될 때 시작되고, 메서드 반환이 끝나

면 커밋된다. (메서드 내부에서 오류가 발생하면 롤백될 것이다.)

이제 HelloWorld의 메인 클래스를 수정해서 메시지를 저장하고 보여주는 모든 작업을

MessageHandler에 위임할 수 있다.

애플리케이션 실행

"Hello World" 애플리케이션의 메인 클래스는 JNDI 레지스트리를 찾아본 뒤 무상태 세션 빈인

MessageHandler를 호출한다. 이때 당연히 관리형 환경과 JNDI 레지스트리를 포함한 전체 애플

리케이션 서버가 먼저 시작돼 있어야 한다. 이 모든 일은 HelloWorld.java의 main() 메서드에서

할 것이다(예제 2.16).

예제 2.16 EJB를 호출하는 "Hello World"의 메인 애플리케이션 코드

package hello;

import org.jboss.ejb3.embedded.EJB3StandaloneBootstrap;

import javax.naming.InitialContext;

public class HelloWorld {

public static void main(String[] args) throws Exception {

Page 129: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 91

// EJB3 설정을 가지고 제이보스 마이크로컨테이너(JBoss Microcontainer)를

// 구동하고 ejb3-interceptors-aop.xml와 embedded-jboss-beans.xml 불러옴

EJB3StandaloneBootstrap.boot(null);

// 직접 작성한 무상태 빈(대부분 데이터소스)을 배포

EJB3StandaloneBootstrap

.deployXmlResource("META-INF/helloworld-beans.xml");

// 클래스패스에서 찾은 모든 EJB 배포(모두 스캔하기 때문에 느림)

// EJB3StandaloneBootstrap.scanClasspath();

// 클래스패스에서 찾은 모든 EJB 배포 (빌드 디렉터리만 스캔하므로 빠르다)

// 다음은 상대 경로로 java.class.path 중 하나로 끝나는 것과 일치한다.

// 모든 값을 보려면 System.getProperty("java.class.path")의 값을 출력한다.

EJB3StandaloneBootstrap.scanClasspath("helloworld-ejb3/bin");

// jndi.properties로부터 InitialContext 생성

InitialContext initialContext = new InitialContext();

// 무상태 MessageHandler EJB 룩업

MessageHandler msgHandler = (MessageHandler) initialContext

.lookup("MessageHandlerBean/local");

// 무상태 EJB 호출

msgHandler.saveMessages();

msgHandler.showMessages();

// EJB 컨테이너 종료

EJB3StandaloneBootstrap.shutdown();

}

}

main()의 첫 번째 명령은 서버의 커널을 구동하고 서비스 설정 파일에 있는 기본 서비스를 배

포한다. 다음으로 이전의 helloworld-beans.xml에서 작성한 데이터소스 팩터리 설정을 배포하

면 컨테이너에 의해 데이터소스가 JNDI와 연동된다. 그 순간부터 컨테이너는 EJB를 배포할 준

비가 된 상태다. 모든 EJB를 배포하는 (가장 빠르지는 않지만) 가장 쉬운 방법은 컨테이너가 EJB

애노테이션을 가지고 있는 클래스를 전체 클래스패스에서 찾게 하는 것이다. 사용할 수 있는 모

든 배포 옵션을 익히려면 다운로드할 때 포함돼 있던 제이보스 AS 문서를 참조하기 바란다.

EJB를 찾아 내려면 InitialContext가 필요한데, 이것은 JNDI 레지스트리 진입점에 해당한다.

Page 130: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

92 l 하이버네이트 완벽 가이드

InitialContext 인스턴스를 만들면 자바는 자동적으로 jndi.properties를 클래스패스에서 찾는

다. 이 파일을 WORKDIR/etc에 만들어 제이보스 서버의 JNDI 레지스트리 설정과 일치하는 설

정을 넣어둬야 한다.

java.naming.factory.initial org.jnp.interfaces.LocalOnlyContextFactory

java.naming.factory.url.pkgs org.jboss.naming:org.jnp.interfaces

이 설정이 무엇을 의미하는지 정확히 알아야 할 필요는 없지만, 기본적으로 InitialContext는

로컬 가상 머신에서 동작하고 있는 JNDI를 가리킨다(원격 EJB 클라이언트 호출은 원격 통신을

지원하는 JNDI 서비스를 필요로 한다).

기본적으로 MessageHandler 빈은 로컬 인터페이스의 접미어인 /local과 구현 클래스의 이름

으로 룩업한다. EJB의 이름을 부여하는 방법과 EJB가 JNDI와 연동되는 방법, JNDI에서 EJB를

찾는 방법은 다양하고 사용자 환경에 맞게 바꿀 수 있다. 방금 설명한 것이 제이보스 서버의 기

본값이다.

마지막으로 MessageHandler EJB를 호출하고 두 개의 작업 단위 내에서 모든 작업을 자동으

로 수행하게끔 한다. 각 메서드 호출은 별도의 트랜잭션으로 처리된다.

이것으로 관리형 EJB 컴포넌트와 JPA를 연동하는 첫 번째 예제를 마친다. 아마도 자동 트랜잭

션 경계 설정과 EntityManager 주입으로 인해 코드의 가독성이 높아진 것을 확인할 수 있을 것

이다. 나중에 트랜잭션 처리를 하는 사용자와 애플리케이션 사이의 복잡한 상호작용을 (상태를

유지하는) 세션 빈을 이용하여 어떻게 구현하는지 살펴보겠다. 또한 EJB 컴포넌트는 어떠한 불

필요한 접착제 코드나 기반 메서드를 포함하지 않으며, 완전히 재사용 가능하고, 이식성 있으며

어떠한 EJB 3.0 컨테이너에서도 실행이 가능하다.

참고 영속화 단위의 패키징 영속화 단위 패키징에 관해서는 많은 내용을 언급하지

않았다. 지금까지 살펴본 모든 배포 방식에서는 "Hello World" 예제를 패키

징할 필요가 없었다. 하지만 완전한 애플리케이션 서버 환경에서 빠른 재배포

(Hot redeployment)와 같은 기능을 사용하고 싶다면 애플리케이션을 올바르

게 패키징해야 한다. 여기엔 JAR, WAR, EJB-JAR, EAR의 조합이 포함된다. 배

포와 패키징은 보통 벤더에 따라 달라지기 때문에 더 자세한 내용은 애플리케

이션 서버의 문서를 참조해야 한다. JPA 영속화 단위는 JAR, WAR, EJB-JAR

에 포함될 수 있다. 즉, 이러한 아카이브 가운데 하나 또는 여럿이 모든 애노테

이션이 설정된 클래스와 해당 영속화 단위의 모든 설정을 담은 META-INF/

persistence.xml 설정 파일을 포함한다는 뜻이다. 하나 또는 여러 JAR, WAR,

EJB-JAR를 하나의 애플리케이션 아카이브인 EAR로 감쌀 수 있다. 애플리케

Page 131: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 93

이션 서버는 올바르게 모든 영속화 단위를 감지하고 필요한 팩터리를 자동으

로 만들 것이다. 또한 @PersistenceContext 애노테이션의 단위 이름을 이용해

서 컨테이너가 특정 단위의 EntityManager를 주입하도록 지시할 수 있다.

JPA나 EJB 3.0을 쓰는 주된 이유가 애플리케이션의 완전한 이식성 때문만은 아니다. 아무튼

하이버네이트를 JPA 영속성 제공자로 쓰기로 결정했으니 가끔 어떻게 하이버네이트로 돌아가

하이버네이트 고유의 기능을 사용할 수 있는지 살펴보자.

2.2.4 하이버네이트 인터페이스로 교체하기

몇 가지 이유로 하이버네이트를 JPA 영속성 제공자로 사용하기로 결정했다고 하자. 먼저 하이버

네이트는 코드에 영향을 주지 않으면서도 다양한 대안을 제공하는 훌륭한 JPA 구현이다. 이를테

면, 하이버네이트 2차 데이터 캐시를 JPA 설정에서 사용하도록 설정할 수 있으며, 투명하게 아무

런 코드도 건드리지 않고 애플리케이션의 성능과 확장성을 개선하는 것도 가능하다.

다음으로 필요하면 하이버네이트 고유의 매핑과 API를 사용할 수 있다. 매핑 방식 혼합(특히

애노테이션)에 대해서는 3장 3.3절, "객체/관계형 매핑 메타데이터"에서 다루지만, 여기서는 필

요에 따라 JPA 애플리케이션에서 하이버네이트 API를 어떻게 사용하는지 보여주겠다 한다. 당

연히 하이버네이트 API를 코드에 넣으면 코드를 다른 JPA 공급자로 바꾸기가 더 어려울 것이다.

따라서 그러한 코드를 적절히 따로 빼내거나 최소한 왜 그리고 언제 하이버네이트 고유의 기능

을 사용했는지 문서화하는 것이 매우 중요하다.

JPA 인터페이스를 그에 대응하는 하이버네이트 API로 되돌릴 수 있다. 이를테면, 필요 시

Con�guration, SessionFactory, Session을 쓸 수 있다는 말이다.

예를 들어, Persistence 정적 클래스로 EntityManagerFactory를 생성하는 대신 다음과 같이

하이버네이트 Ejb3Con�guration을 사용할 수 있다.

Ejb3Configuration cfg = new Ejb3Configuration();

EntityManagerFactory emf =

cfg.configure("/custom/hibernate.cfg.xml")

.setProperty("hibernate.show_sql", "false")

.setInterceptor(new MyInterceptor())

.addAnnotatedClass(hello.Message.class)

.addResource("/Foo.hbm.xml")

.buildEntityManagerFactory();

AnnotationConfiguration hibCfg = cfg.getHibernateConfiguration();

Page 132: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

94 l 하이버네이트 완벽 가이드

Ejb3Con�guration은 하이버네이트 Con�guration을 확장하는 대신 그것을 복사해서 만든

새로운 인터페이스다(이것은 구현 세부사항에 해당한다). 이는 일반적인 Ejb3Con�guration으

로부터 AnnotationCon�guration 객체를 얻어 프로그램 방식으로 SchemaExport 인스턴스에

넘겨줄 수 있다는 의미다.

SessionFactory 인터페이스는 보통 2차 캐시 영역을 프로그램 방식으로 제어해야 할 때 유용

하다. 먼저 SessionFactory는 EntityManagerFactory를 형변환하여 얻을 수 있다.

HibernateEntityManagerFactory hibEMF = (HibernateEntityManagerFactory) emf;

SessionFactory sf = hibEMF.getSessionFactory();

같은 기법을 EntityManager에서 Session을 가져올 때도 쓸 수 있다.

HibernateEntityManager hibEM = (HibernateEntityManager) em;

Session session = hibEM.getSession();

표준화된 EntityManager로부터 하이버네이트 고유의 API를 가져오는 방법은 이뿐만이 아니

다. JPA 표준은 getDelegate() 메서드를 제공하여 기반이 되는 JPA구현을 가져올 수 있도록 지원

한다.

Session session = (Session) entityManager.getDelegate();

아니면 EJB 컴포넌트에 주입된 세션을 가져올 수도 있다(이 방법은 제이보스 AS에서만 가능

하다).

@Stateless

public class MessageHandlerBean implements MessageHandler {

@PersistenceContext

Session session;

...

}

드문 경우지만 하이버네이트 Session에서 JDBC 인터페이스로 되돌아갈 수도 있다.

Connection jdbcConnection = session.connection();

이 방법에는 몇 가지 주의가 따른다. 하이버네이트에서 가져온 JDBC 연결은 닫을 수가 없다(자

동으로 닫히게 되어 있다). 이 규칙이 적용되지 않는 경우는 적극적으로 연결을 해제하는 환경인

데, 즉 JTA나 CMT 환경에서는 애플리케이션 코드에서 돌려받은 연결을 반드시 닫아줘야 한다.

Page 133: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 95

JDBC 연결에 직접 접근하는 것보다 낫고 안전한 방법은 자바 EE 5.0에서 주입한 리소스를 이

용하는 것이다. 다음과 같이 EJB, EJB 리스너, 서블릿, 서블릿 필터, 또는 JSF(JavaServer Faces)

기반 빈의 필드나 설정자 메서드에 애노테이션을 지정하면 된다

@Resource(mappedName="java:/HelloWorldDS") DataSource ds;

지금까지는 레거시 애플리케이션 코드나 기존의 데이터베이스 스키마가 없는 새로운 하이버

네이트 프로젝트나 JPA 프로젝트를 진행한다고 가정했다. 이제 관점을 바꿔 상향식(bottom-up)

개발을 생각해 보자. 그러한 경우에는 아마도 역공학으로 기존의 데이터베이스 스키마로부터 산

출물을 자동적으로 만들어내고 싶을 것이다.

2.3 레거시 데이터베이스 역공학하기

레거시 데이터베이스를 매핑하는 첫 번째 단계에서는 보통 자동적인 역공학 (reverse-

engineering) 절차가 포함된다. 결국 이미 데이터베이스 시스템에 엔티티 스키마가 존재하기 때문

이다. 이러한 과정을 손쉽게 하기 위해 하이버네이트는 스키마를 읽어 해당 메타데이터로부터

XML 매핑과 자바 소스 코드 같은 다양한 산출물을 생성할 수 있는 도구 집합을 가지고 있다.

이 모든 것은 템플릿 기반이기 때문에 입맛에 맞게 산출물을 손볼 수 있다.

역공학 작업은 앤트 빌드의 도구와 태스크로 제어할 수 있다. 이전에 하이버네이트 매핑 메타

데이터에서 SQL DDL을 생성할 때 사용한 HibernateToolTask는 다양한 옵션을 가지고 있으며,

대부분 XML 매핑 파일, 자바 코드, 또는 심지어 전체 애플리케이션 구조를 어떻게 기존의 데이

터베이스 스키마로부터 자동 생성할 수 있는지와 관련이 있다.

먼저 기존의 데이터베이스를 하이버네이트 매핑 모델로 불러올 수 있는 앤트 타겟을 작성하는

방법부터 살펴보겠다. 다음으로 다양한 추출기를 적용하여 XML 파일, 자바 코드를 비롯한 다

른 유용한 산출물을 데이터베이스 테이블과 열 이름을 토대로 생성하겠다.

2.3.1 데이터베이스 설정 만들기

WORKDIR에 (일반적으로 필요한 라이브러리가 들어있는) lib 디렉터리와 빈 src 디렉터리만 있

다고 가정하자. 기존 데이터베이스로부터 매핑과 코드를 생성하려면 먼저 데이터베이스 연결 정

보를 가지고 있는 설정 파일을 만들어야 한다.

hibernate.dialect = org.hibernate.dialect.HSQLDialect

hibernate.connection.driver_class = org.hsqldb.jdbcDriver

Page 134: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

96 l 하이버네이트 완벽 가이드

hibernate.connection.url = jdbc:hsqldb:hsql://localhost

hibernate.connection.username = sa

이 파일을 WORKDIR 디렉터리에 저장하고 helloworld.db.properties라는 이름으로 저장한

다. 여기 보이는 네 줄이 데이터베이스에 연결하여 모든 테이블과 열을 읽어들일 때 필요한 최소

한의 코드다. helloworld.db.properties 대신 하이버네이트 XML 설정 파일을 만들 수도 있겠지

만 필요 이상으로 복잡하게 만들 필요는 없다.

다음으로 앤트 타겟을 작성하자. 프로젝트의 build.xml 파일에 다음 코드를 추가한다.

<taskdef name="hibernatetool"

classname="org.hibernate.tool.ant.HibernateToolTask"

classpathref="project.classpath" />

<target name="reveng.hbmxml"

description="XML 매핑 파일을 src 디렉터리에 생성한다.">

<hibernatetool destdir="${basedir}/src">

<jdbcconfiguration

propertyfile="${basedir}/helloworld.db.properties"

revengfile="${basedir}/helloworld.reveng.xml" />

<hbm2hbmxml /> <!-- 하이버네이트 XML 파일 추출 -->

<hbm2cfgxml /> <!-- hibernate.cfg.xml 파일 추출 -->

</hibernatetool>

</target>

앤트 HibernateToolTask 정의는 이전과 동일하다. 이전 절에서 소개한 빌드 파일의 대부분을

재사용한다고 가정하기 때문에 project.classpath과 같은 것도 전과 동일하다. <hibernatetool>

태스크에서는 WORKDIR/src을 산출물을 생성할 기본 결과 디렉터리로 설정했다.

<jdbccon�guration>은 JDBC에 접근할 수 있고 데이터베이스 카탈로그에서 JDBC 메타데이

터를 읽는 하이버네이트 도구 설정이다. 보통 여기엔 데이터베이스 연결 설정(프로퍼티 파일)과

선택적인 역공학 과정을 정의한 파일을 설정한다.

도구 설정으로 생성된 메타데이터는 곧 추출기에 전달된다. 예제 앤트 타겟은 추출기를 두 개

사용한다. hbm2hbmxml 추출기는 이름에서 알수 있듯 하이버네이트 메타데이터(hbm)를 설

Page 135: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 97

정에서 가져온 다음 하이버네이트 XML 매핑 파일을 생성한다. 두 번째 추출기는 생성된 모든

XML 매핑 파일 목록을 담은 hibernate.cfg.xml을 만든다.

이 추출기와 다양한 추출기에 관해 논하기 전에 역공학 설정 파일과 그것을 사용하여 무엇을

할 수 있는지 잠시 알아보자.

2.3.2 역공학 과정 재정의하기

JDBC 메타데이터, 즉 JDBC를 통해 데이터베이스에서 읽을 수 있는 데이터베이스 자체에 관한

정보만 가지고는 자바 애플리케이션은 둘째치고 완전한 XML 매핑 파일을 만들기도 어려울 때

가 있다. 그 반대도 마찬가지일 수 있다. 데이터베이스는 아마도 무시하거나(특정 테이블이나 열

처럼) 기본값이 아닌 전략을 이용해서 변경하고자 하는 정보를 담고 있을지도 모른다. XML 문

법을 사용하는 역공학 설정 파일을 이용하면 역공학 절차를 재정의할 수 있다.

이 장의 초기에 만들었던 "Hello World" 데이터베이스를 역공학하고 있다고 가정해보자.

이 데이터베이스에는 MESSAGES 테이블과 몇 개의 열만 들어있을 뿐이다. 예제 2.17과 같이

helloworld.reveng.xml 파일로 이러한 역공학 과정을 재정의할 수 있다.

예제 2.17 재정의한 역공학 설정

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

<!DOCTYPE hibernate-reverse-engineering SYSTEM ❶

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

<hibernate-reverse-engineering>

<table-filter match-name=".*" package="hello" /> ❷

<table name="MESSAGES" schema="PUBLIC" class="Message"> ❸

<primary-key> ❹

<generator class="increment" />

<key-column name="MESSAGE_ID" property="id" type="long" />

</primary-key>

<column name="MESSAGE_TEXT" property="text" /> ❺

<foreign-key constraint-name="FK_NEXT_MESSAGE"> ❻

<many-to-one property="nextMessage" />

Page 136: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

98 l 하이버네이트 완벽 가이드

<set exclude="true" />

</foreign-key>

</table>

</hibernate-reverse-engineering>

❶ 이 XML 파일은 유효성 검증과 자동 완성을 위한 자체적인 DTD를 가지고 있다.

❷ 테이블 필터(table �lter)는 정규 표현식을 써서 테이블 이름을 기준으로 테이블을 역공학 대

상에서 제외할 수 있다. 하지만 이 예제에서는 정규 표현식에 부합하는 테이블에 대해 생성

될 모든 클래스의 기본 패키지를 정의했다.

❸ 이름을 기준으로 개별적인 테이블을 직접 재정의할 수도 있다. 스키마 이름은 보통 선택사

항이지만 HSQLDB는 PUBLIC 스키마를 모든 테이블에 기본으로 적용하기 때문에 JDBC

메타데이터를 가져왔을 때 테이블을 식별하려면 이 설정이 필요하다. 또한 생성된 엔티티에

대한 사용자 정의 클래스 이름을 여기서 설정할 수도 있다.

❹ 주키 열은 id라는 프로퍼티를 생성하며, 기본값은 아마 messageId가 될 것이다. 또한 어떤

하이버네이트 식별자 생성기가 사용될지도 명시적으로 선언할 수 있다.

❺ 개별적으로 열을 제외하거나, 이 경우처럼 생성될 프로퍼티의 이름을 명시할 수 있다. 기본

값은 messageText가 될 것이다.

❻ FK_NEXT_MESSAGE 외래키 제약 조건을 JDBC 메타데이터에서 가져오면 기본적으로

클래스의 대상 엔티티에 대해 다대일 연관 관계가 만들어진다. 외래키 제약 조건을 이름을

기준으로 일치하게 만들어 그에 대응하는 역 컬렉션(일대다)을 생성해야 하는지와 (예제에

서는 제외하였음) 다대일 프로퍼티가 어떤 이름을 가져야 할지 지정할 수 있다.

이처럼 정의한 설정 파일을 앤트 타겟으로 실행하면 소스 디렉터리의 hello 패키지에 Message.

hbm.xml 파일이 생성된다. (먼저 프리마커와 jTidy JAR 파일을 라이브러리 디렉터리에 복사해

야 한다.) 직접 재정의한 결과 이전에 예제 2.2에서 손수 작성한 하이버네이트 매핑 파일과 같은

매핑 파일이 만들어진다.

앤트 타겟은 XML 매핑 파일과 더불어 하이버네이트 XML 파일도 소스 디렉터리에 만들어

준다.

Page 137: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 99

<hibernate-configuration>

<session-factory>

<property name="hibernate.connection.driver_class">

org.hsqldb.jdbcDriver

</property>

<property name="hibernate.connection.url">

jdbc:hsqldb:hsql://localhost

</property>

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

sa

</property>

<property name="hibernate.dialect">

org.hibernate.dialect.HSQLDialect

</property>

<mapping resource="hello/Message.hbm.xml" />

</session-factory>

</hibernate-configuration>

추출기는 역공학에 쓴 모든 데이터베이스 연결 설정을 애플리케이션 실행 시 사용하려는 데

이터베이스라 가정하고 이 파일로 내보낸다. 또한 생성된 모든 XML 매핑 파일을 설정에 추가해

준다.

다음으로 해야 할 일은 뭘까? 이제 Message 자바 클래스의 소스 코드를 작성할 수 있다. 또는

하이버네이트 도구가 도메인 모델 클래스를 생성하게 할 수도 있다.

2.3.3 자바 소스 코드 생성하기

Message 클래스에 대한 기존의 하이버네이트 XML 매핑 파일이 있고 클래스 소스를 자동으로

생성하고 싶다고 가정하자. 3장에서 논의하겠지만 일반 자바 엔티티 클래스는 보통 Serializable

을 구현하고, 인자가 없는 생성자를 가지고 있으며, 모든 프로퍼티에 대한 접근자와 설정자, 캡슐

화된 구현을 가지고 있다.

엔티티 클래스의 소스 코드는 하이버네이트 도구와 앤트 빌드의 hbm2java 추출기를 사용하

여 생성할 수 있다. 소스 산출물은 하이버네이트 메타데이터 모델로 읽어들일 수 있는 것이라면

뭐든지 가능하다. 자바 코드 생성 과정을 직접 재정의하고 싶다면 하이버네이트 XML 매핑이 최

선이다.

Page 138: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

100 l 하이버네이트 완벽 가이드

다음 타겟을 앤트 빌드에 추가한다.

<target name="reveng.pojos" description="XML 매핑으로부터 자바 클래스를 생성한다">

<hibernatetool destdir="${basedir}/src">

<configuration>

<fileset dir="${basedir}/src">

<include name="**/*.hbm.xml" />

</fileset>

</configuration>

<hbm2java /> <!-- 엔티티 클래스 소스를 생성 -->

</hibernatetool>

</target>

<con�guration>은 모든 하이버네이트 XML 매핑 파일을 읽어들이고, <hbm2java> 추출기는

기본 전략을 이용해서 자바 소스 코드를 생성한다.

엔티티 클래스 생성 과정 재정의 하기

기본적으로 hbm2java는 매핑된 각 엔티티에 대해 간단한 엔티티 클래스를 생성한다. 생성된 클

래스는 Serializable 마커 인터페이스를 구현하며, 모든 프로퍼티에 대한 접근 메서드와 필수 생

성자를 만든다. 클래스의 모든 속성은 필드에 대해 private 가시성(visibility)을 지니며, 이러한 가

시성은 XML 매핑 파일의 <meta> 요소와 속성을 이용해서 변경할 수 있다.

기본 역공학 전략을 변경할 때 가장 먼저 할 일은 Message 속성의 가시성 범위를 제한하는 것

이다. 생성되는 모든 접근 메소드는 기본적으로 가시성이 public이다. Message 객체가 불변 객

체라고 가정해보자. 오직 접근자 메서드만 공개하고, 설정자 메서드를 public으로 노출하고 싶

지 않을 것이다. 모든 속성 매핑에 <meta> 엘리먼트 추가하여 매핑 정보량을 키우는 대신, 메타

속성(meta-attribute)을 클래스 수준에서 설정하여 클래스의 모든 속성에 해당 설정을 적용할 수

있다.

<class name="Message" table="MESSAGES">

<meta attribute="scope-set">private</meta>

...

</class>

Page 139: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 101

해당 scope-set 속성은 설정자 메서드의 가시성을 정의한다.

hbm2java 추출기도 <hibernate-mapping> 루트 요소 안의 두 번째 상위 요소인 메타 속성을

읽어들인다. 그런 다음 XML 파일에 맵핑된 모든 클래스에 메타 속성의 내용을 적용한다. 더 세

밀한 메타 속성을 단일 프로퍼티나 컬렉션, 또는 컴포넌트 매핑에 추가할 수도 있다.

(사소하지만) 생성되는 엔티티 클래스를 개선하는 몇 가지 방법이 있는데 Message 클래스의

text 값을 toString() 메서드의 반환값에 포함시키는 것이 그 중 하나다. text는 애플리케이션의

로그 출력 가운데 시각적인 제어 요소로 쓰기 좋다. Message 매핑을 변경해서 생성된 코드에 텍

스트를 포함하게 할 수 있다.

<property name="text" type="string">

<meta attribute="use-in-tostring">true</meta>

<column name="MESSAGE_TEXT" />

</property>

생성된 Message.java의 toString() 메서드는 다음과 같다.

public String toString() {

StringBuffer buffer = new StringBuffer();

buffer.append(getClass().getName())

.append("@")

.append( Integer.toHexString(hashCode()) )

.append(" [");

.append("text").append("='").append(getText()).append("' ");

.append("]");

return buffer.toString();

}

메타 속성은 상속될 수 있다. 즉, 만약 use-in-tostring을 <class> 요소 수준에 선언하면 해당

클래스의 모든 속성이 toString() 메서드에 포함될 것이다. 이 상속 메커니즘은 모든 hbm2java

메타 속성에서 동작하지만 선택적으로 해당 기능을 사용하지 않을 수도 있다.

<meta attribute="scope-class" inherit="false">public abstract</meta>

scope-class라는 메타 속성에서 inherit을 false로 설정하면 오직 해당 <meta> 요소의 부모 클

래스만 public abstract로 생성하고 (아마) 내부의 하위 클래스는 그렇게 만들지 않는다.

hbm2java 추출기는 이 책을 쓰는 시점에 17개의 메타 속성을 제공하여 정교한 코드 생성을

지원한다. 메타 속성은 대부분 가시성, 인터페이스 구현, 클래스 상속, 미리 정의된 자바독 주석

과 관련이 있다. 전체 목록은 하이버네이트 도구 문서를 참고하자.

Page 140: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

102 l 하이버네이트 완벽 가이드

만약 JDK 5.0을 사용하고 있다면 <hbm2java> 태스크에 jdk5="true"를 설정하여 자동으로

정적 임포트(static import)와 제네릭을 생성하게 할 수도 있다. 또는 애노테이션이 지정된 EJB 3.0

엔티티 클래스도 생성 가능하다.

자바 퍼시스턴스 엔티티 클래스 생성하기

보통 하이버네이트 XML 매핑 파일이나 엔티티 클래스 소스 코드의 JPA 애노테이션을 사용하

여 매핑 메타데이터를 정의하기 때문에 애노테이션을 가지고 있는 자바 영속 엔티티 클래스를

XML 매핑 파일에서 만들어 내는 것이 이상하게 생각될 수도 있다. 하지만 JDBC 메타데이터로

부터 직접 애노테이션이 지정된 엔티티 클래스 소스 코드를 만들어 XML 매핑 과정을 생략할 수

있다. 다음의 앤트 타겟을 살펴보자.

<target name="reveng.entities"

description="자바 엔티티 클래스를 src 디렉터리에 생성한다">

<hibernatetool destdir="${basedir}/src">

<jdbcconfiguration

propertyfile="${basedir}/helloworld.db.properties"

revengfile="${basedir}/helloworld.reveng.xml" />

<hbm2java jdk5="true" ejb3="true" />

<hbm2cfgxml ejb3="true" />

</hibernatetool>

</target>

이 타겟은 매핑 애노테이션이 지정된 엔티티 클래스의 소스 코드와 모든 매핑 클래스 목록을

담은 hibernate.cfg.xml 파일을 생성한다. 만약 helloworld.reveng.xml이 너무 제약 사항이 많

다면(조정가능한 요소가 정해져 있어), 자바 소스를 직접 수정하여 매핑을 재정의할 수 있다.

또한 모든 추출기는 프리마커 템플릿 언어로 작성된 템플릿에 의존한다는 점을 알아두자. 템

플릿을 얼마든지 원하는 대로 수정하거나, 심지어 직접 만든 템플릿을 써도 된다. 프로그램 방식

의 코드 생성을 직접 재정의하는 것도 가능하다. 이와 관련한 내용은 하이버네이트 도구 레퍼런

스 문서에서 확인할 수 있다.

Page 141: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 103

하이버네이트 도구로 다음과 같은 추출기와 설정을 사용할 수 있다.

▒ <annotationcon�guration>은 XML 파일 대신 애노테이션을 지정한 자바 클래스에서

매핑 메타데이터를 읽어오고 싶을 때 일반적인 <con�guration>을 대체한다. 필요한 인

자는 애노테이션을 지정한 클래스 목록을 가지고 있는 hibernate.cfg.xml 파일의 위치뿐

이다. 애노테이션을 지정한 클래스에서 데이터베이스 스키마를 생성하려면 이 접근법을

쓰면 된다.

▒ <e jb3 c on f i g u r a t ion > 은 h i b e r n at e . c f g . x m l 파일이 아니라 클래스패스에

서 자 동으로 애노테이션을 지정한 자바 클래스를 스캔한다는 점만 제외하면

<annotationcon�guration>과 같다.

▒ <hbm2dao> 추출기는 데이터 접근 객체 패턴(DAO 패턴)에 따라 영속화 계층에 필요한

부가적인 자바 소스를 만들 수 있다. 이 책을 쓰는 지금 이 추출기에 대한 템플릿은 오래

된 코드라 갱신이 필요하다. 16장 16.2절, "영속화 계층 만들기"에 있는 DAO 코드와 비슷

한 템플릿으로 수정되기를 기대한다.

▒ <hbm2doc> 추출기는 테이블과 자바 엔티티에 대한 HTML 파일을 생성한다.

▒ <hbmtemplate> 추출기는 직접 작성한 프리마커 템플릿을 이용해 매개변수화할 수 있어

이러한 방법으로 원하는 무엇이든 생성할 수 있다. 템플릿은 하이버네이트 도구에 포함되

어 있는 제이보스 심 프레임워크를 이용해 완전히 실행가능한 기본 뼈대가 되는 9 애플리

케이션을 생성해준다.

도구의 가져오기와 내보내기 기능을 창의적으로 활용하는 것도 가능하다.

이를테면, <annotationcon�guration>을 이용해서 애노테이션을 지정한 자바 클래스를 읽거나

<hbm2hbmxml>을 이용해서 그것들을 내보낼 수 있다. 이렇게 하면 개발할 때는 더 편리한 애

노테이션과 JDK 5.0을 사용하고 운영 환경(JDK 1.4)에는 하이버네이트 XML을 배포할 수 있다.

몇 가지 고급 설정 옵션과 자바 EE 서비스와 하이버네이트 통합을 살펴보는 것으로 이 장을

마무리하겠다.

9  (옮긴이) 원문에는 skeleton application이라는 표현을 쓰나 의미에 맞게 풀어썼다.

Page 142: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

104 l 하이버네이트 완벽 가이드

2.4 자바 EE 서비스와의 통합

"Helo World" 예제에 관해서는 이미 이전 절에서 살펴봤으므로 기본적인 하이버네이트 설정과

어떻게 하이버네이트를 일반 자바 애플리케이션에 통합할 수 있는지 알고 있다고 가정하겠다. 이

제 좀 더 수준 높은 하이버네이트 고유 설정 옵션에 관해 논의하고 어떻게 일반 하이버네이트 애

플리케이션에서 자바 EE 애플리케이션 서버가 제공하는 자바 EE 서비스를 활용할 수 있는지 살

펴보자.

하이버네이트 애노테이션과 하이버네이트 EntityManager를 이용해서 첫 번째 JPA 프로젝트

를 만들었다면 다음에 제시할 설정과 관련된 조언은 여러분에게 별로 의미가 없다. JPA를 사용

하고 있다면 이미 자바 EE 세상에 깊이 발을 들여 놓은 셈이며 별도의 통합 과정도 필요하지 않

기 때문이다. 따라서 이미 하이버네이트 EntityManager를 사용하고 있다면 이번 절을 건너뛰어

도 무방하다.

제이보스 AS, BEA 웹로직, IBM 웹스피어 같은 자바 EE 애플리케이션 서버는 자바의 표준(자

바 EE에 특화된) 관리형 환경을 구현한 것이다. 하이버네이트와 통합 가능한 가장 흥미로운 세

가지 자바 EE 서비스로는 JTA, JNDI, JMS가 있다.

JTA는 관리형 자원에 대한 트랜잭션에 하이버네이트가 참여하게 해준다. 하이버네이트는

JNDI를 사용하여 관리형 자원(데이터베이스 연결)을 룩업할 수 있으며 또한 관리형 자원 자체

를 서비스로서 JNDI와 연동할 수도 있다. 마지막으로 하이버네이트는 JMX를 통해 배포될 수 있

으며 그렇게 되면 JMX 컨테이너에 의해 서비스로서 관리되고 표준 JMX 클라이언트로 실행 중

에 모니터링할 수도 있다.

각 서비스를 살펴보고 하이버네이트와 어떻게 통합할 수 있는지 살펴보자.

2.4.1 JTA와의 통합

자바 트랜잭션 API(JTA, Java Transaction API)는 자바 엔터프라이즈 애플리케이션에서 트랜

잭션을 제어하기 위한 표준 서비스 인터페이스다. JTA는 트랜잭션 경계 설정에 사용할 API로

UserTransaction과 트랜잭션 생명주기를 관리하는 TransactionManager 등 몇 가지 인터페이

스를 제공한다. 트랜잭션 관리자(transaction manager)는 여러 트랜잭션에 걸쳐 적용되는 트랜잭

션을 조율할 수 있는데, 즉 두 데이터베이스에 대한 두 개의 하이버네이트 Session을 하나의 트랜

잭션 안에서 이용하는 것을 상상해 보면 된다.

Page 143: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 105

JTA 트랜잭션 서비스는 모든 자바 EE 애플리케이션 서버에서 제공된다. 하지만 대부분의 자

바 EE 서비스는 독립적으로 사용할 수 있기 때문에 제이보스 트랜잭션(JBoss Transaction)이나

오브젝트웹의 JOTM 같은 JTA 공급자를 여러분의 애플리케이션에 배포할 수 있다. 이 부분에

관해서는 많이 설명하지 않을 것이며 하이버네이트를 JTA 서비스와 통합하는 부분에 집중하겠

다. 이 부분은 완전한 애플리케이션 서버나 독립적인 JTA 공급자나 모두 동일하게 적용된다.

그림 2.6을 보자. 하이버네이트 Session 인터페이스를 사용하여 데이터베이스에 접근하며 관

리형 환경의 자바 EE 서비스와 통합하는 일은 하이버네이트가 담당한다.

애플리케이션 서버

애플리케이션

서블릿

하이버네이트 서비스

트랜잭션 관리자

자원 관리자

데이터베이스

데이터베이스

EJBSession

UserTransaction

Query

그림 2.6 | 관리형 자원을 사용하는 환경에서의 하이버네이트

그와 같은 관리형 환경에서는 하이버네이트가 더는 JDBC 연결 풀을 만들거나 관리하지 않는

다. 하이버네이트는 JNDI 레지스트리에서 Datasource 객체를 룩업하여 데이터베이스 연결을 획

득한다. 따라서 하이버네이트 설정에는 관리형 연결을 어디서 가져올 수 있는지 JNDI 이름에 대

한 참조가 필요하다.

<hibernate-configuration>

<session-factory>

<property name="hibernate.connection.datasource">

java:/MyDatasource

</property>

<property name="hibernate.dialect">

org.hibernate.dialect.HSQLDialect

</property>

...

</session-factory>

</hibernate-configuration>

Page 144: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

106 l 하이버네이트 완벽 가이드

이 설정 파일을 가지고 하이버네이트는 java:/MyDatasource라는 이름을 사용해 JNDI에서 데

이터베이스 연결을 가져온다. 따라서 애플리케이션 서버를 설정하거나 애플리케이션을 배포하거

나 독립적인 JTA 공급자를 설정할 때 이 이름으로 관리형 자원을 연동해야 한다. 하지만 하이버

네이트가 올바른 SQL을 생성하려면 여전히 데이터베이스 방언 설정이 필요하다.

참고 하이버네이트와 톰캣 톰캣은 자바 EE 애플리케이션 서버가 아니다. 톰캣은 단

순한 서블릿 컨테이너로, 애플리케이션 서버가 제공하는 몇 가지 기능을 갖고

있을 뿐이다. 그러한 기능 중 하나를 하이버네이트와 함께 사용할 수 있다. 바

로 톰캣 연결 풀이다. 톰캣은 내부적으로 DBCP 연결 풀을 사용하지만 그것

을 진짜 애플리케이션 서버처럼 JNDI 데이터소스(datasource)로 공개한다. 톰

캣 데이터소스를 설정하려면 톰캣 JNDI/JDBC 문서의 안내에 따라 server.

xml을 수정해야 한다. 하이버네이트는 hibernate.connection.datasource

로 해당 데이터소스를 사용하도록 설정할 수 있다. 톰캣은 트랜잭션 관리자

를 제공하지 않는다는 사실을 명심하자. 그래서 여전히 하이버네이트의 부

가적인 트랜잭션 API를 사용하여 감출 수 있는 일반 JDBC 트랜잭션 시맨틱

(semantic)을 쓰는 것이다. 이것의 대안으로는 JTA 호환 독립 트랜잭션 관리자

를 웹 애플리케이션(표준 UserTransaction API를 사용할 것으로 예상되는)

과 함께 배포하는 방법이 있다. 반면 일반 애플리케이션 서버(특히 제이보스

AS처럼 모듈화되어 있다면)는 톰캣에 DBCP와 JTA를 추가로 설정하는 것보

다 설정하기 쉽고 더 나은 서비스를 제공한다.

하이버네이트를 JTA와 완전히 통합하려면 하이버네이트에 현재 사용하는 트랜잭션 관리자에

대해 좀 더 알려줄 필요가 있다. 예를 들어, 캐시를 관리하기 위해서는 하이버네이트가 트랜잭션

생명주기에 끼어들어야 한다. 먼저 어떤 트랜잭션 관리자를 사용중인지 하이버네이트에 설정해

야 한다.

<hibernate-configuration>

<session-factory>

<property name="hibernate.connection.datasource">

java:/MyDatasource

</property>

<property name="hibernate.dialect">

org.hibernate.dialect.HSQLDialect

</property>

Page 145: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 107

<property name="hibernate.transaction.manager_lookup_class">

org.hibernate.transaction.JBossTransactionManagerLookup

</property>

<property name="hibernate.transaction.factory_class">

org.hibernate.transaction.JTATransactionFactory

</property>

...

</session-factory>

</hibernate-configuration>

이전 코드에서 했던 것처럼 애플리케이션 서버에 따라 적당한 룩업 클래스를 골라야 한다. 하

이버네이트에는 가장 널리 사용되는 JTA 공급자와 애플리케이션 서버의 클래스가 포함돼 있다.

마지막으로 트랜잭션 경계를 설정할 때 애플리케이션에서 어떤 JTA 트랜잭션 인터페이스를 사

용하는지 하이버네이트에 설정해야 한다. JTATransactionFactory는 다음과 같은 작업을 한다.

매번 Session을 손수 열고 닫는 대신 SessionFactory. ▒ getCurrentSession() 메서드를 사

용하기로 했을 때 JTA에 올바른 Session 범위 설정과 전파(propagation)를 가능하게 한다.

이 기능에 대해서는 11장 11.1절, "하이버네이트 세션 전파"에서 자세히 다루겠다.

애플리케이션에서 JTA UserTransaction 인터페이스를 호출해서 시스템 트랜잭션을 시 ▒

작, 커밋, 또는 롤백하리라는 것을 하이버네이트에게 알린다.

또한 표준화된 UserTransaction을 사용하고 싶지 않은 경우에 대비해 하이버네이트 트 ▒

랜잭션을 API를 JTA로 전환하기도 한다. 이제 하이버네이트 API로 트랜잭션을 시작하

면 현재 진행 중인 JTA 트랜잭션이 있는지 확인하고, 만약 가능하다면 해당 트랜잭션에

동참한다. 진행 중인 JTA 트랜잭션이 없다면 새로운 트랜잭션을 시작한다. 하이버네이

트 API를 이용해서 커밋이나 롤백을 하면 해당 호출을 무시하거나(만약 하이버네이트가

기존 트랜잭션에 동참하고 있다면) 시스템 트랜잭션이 커밋하거나 롤백하도록 설정한다.

JTA를 지원하는 환경에 배포한다면 하이버네이트 트랜잭션 API를 사용하는 것을 권장

하지 않는다. 하지만 그렇게 설정함으로써 트랜잭션 행위가 달라질 가능성은 있지만 관리

형 환경이나 비관리형 환경 사이에서 기존 코드의 이식성은 유지할 수 있다.

다른 내장 TransactionFactory 옵션도 있으며 여러분이 직접 이 인터페이스를 구현해도 된다.

비관리형 환경에서는 JDBCTransactionFactory가 기본값이며, 이 장의 JTA를 사용하지 않는

"Hello World" 예제에서 이를 두루 사용한 바 있다. CMTTransactionFactory는 JTA나 EJB를

Page 146: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

108 l 하이버네이트 완벽 가이드

사용하거나 트랜잭션 범위를 관리형 EJB 컴포넌트에 선언적으로 설정하고자 할 때 활성화해야

한다. 다시 말해서 EJB 애플리케이션을 자바 EE 애플리케이션 서버에 배포하더라도 애플리케이

션 코드에서는 UserTransaction 인터페이스를 이용해서 프로그램 방식으로 트랜잭션 경계를 설

정하지 말라는 뜻이다.

권장하는 설정 옵션을 선호도 순으로 나열해 봤다.

애플리케이션을 관리형 환경이나 비관리형 환경에서 모두 실행하려면 트랜잭션 통합과 ▒

자원 관리 책임을 배포자에게 위임해야 한다. 애플리케이션에서 JTA UserTransaction

API를 호출해서 애플리케이션의 배포자가 애플리케이션 서버나 독립적인 JTA 공급자를

설정하게 한다. 하이버네이트 설정에서 JTATransactionFactory를 활성화하여 JTA 서비

스와 통합하고 적절한 룩업 클래스를 설정한다.

트랜잭션 경계 설정을 EJB 컴포넌트를 이용해 선언적으로 하는 것을 고려해 본 ▒

다. 그렇게 하면 데이터 접근 코드가 더는 트랜잭션 API에 종속되지 않으며, CMT

TransactionFactory가 뒷단에서 하이버네이트 Session을 다룰 것이다. 이것이 가장 쉬운

방법이다. 물론 JTA와 EJB 컴포넌트를 지원하는 환경을 제공할 책임은 이제 배포자의 몫

이 된다.

하이버네이트 Transaction API로 코드를 작성하고 배포 환경에 따라 JDBCTransaction ▒

Factory나 JTATransactionFactory 중 하나를 설정해 하이버네이트가 바꿔 쓸 수 있게

한다. 이렇게 하면 트랜잭션의 시맨틱이 달라질 수 있고, 트랜잭션 시작과 커밋이 예상대

로 동작하지 않을 수도 있다. 이것은 트랜잭션 경계 설정의 이식성이 필요할 때 마지막으

로 선택해야 할 수단이어야 한다.

FAQ? 하이버네이트에서 어떻게 여러 개의 데이터베이스를 사용할 수 있나요?

여러 개의 데이터베이스를 다루고 싶다면 설정 파일도 여러 개 만들어야 한다.

각 데이터베이스에 대한 SessionFactory를 만들고 별도의 Con�guration 객

체로부터 SessionFactory를 생성해야 한다. 어느 SessionFactory에서 생성하

든 열려 있는 각 Session은 JNDI에서 관리형 데이터소스를 룩업한다. 이제 이

러한 자원을 조율하는 일은 트랜잭션과 자원 관리자의 책임이다. 하이버네이

트는 이러한 데이터베이스 연결을 대상으로 SQL 문장만 실행할 뿐이다. 트랜

잭션 경계는 JTA를 이용해서 프로그램 방식으로 설정하거나 EJB와 선언적인

조합을 통해 컨테이너가 처리한다.

Page 147: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 109

하이버네이트는 JNDI에서 관리형 자원을 룩업하는 것뿐 아니라 자기 자신을 JNDI와 연동할

수도 있다. 계속해서 살펴보자.

2.4.2 JNDI와 연동된 SessionFactory

앞에서는 모든 신규 하이버네이트 사용자가 다뤄야 할 "SessionFactory를 어떻게 저장하고

애플리케이션 코드에서 어떻게 접근할 수 있을까?"라는 문제를 살펴봤다. 이 장 초반부에서

는 SessionFactory를 정적 필드로 가지고 있으며 getSessionFactory() 메서드로 제공해주는

HibernateUtil 클래스를 작성하여 이 문제를 해결했다. 하지만 애플리케이션을 JNDI가 지원되

는 곳에 배포한다면 하이버네이트는 SessionFactory를 JNDI와 연동해서 필요할 때 JNDI에서

가져오게끔 할 수 있다.

참고 JNDI(Java Naming and Directory Interface) API는 객체를 계층 구조(디렉터

리 트리)에 저장하고 그것으로부터 가져올 수 있게 해준다. JNDI는 레지스트

리(Registry) 패턴을 구현한다. 기반 시설 객체(트랜잭션 컨텍스트, 데이터소스

등)와 설정(환경 설정, 사용자 레지스트리 등), 심지어 애플리케이션 객체(EJB

참조, 객체 팩터리 등)까지 모두 JNDI와 연동할 수 있다.

hibernate.session_factory_name 프로퍼티에 JNDI 노드 이름을 설정하면 하이버네이트

SessionFactory는 자동적으로 자신을 JNDI와 연동한다. 만약 런타임 환경이 기본 JNDI 컨텍스

트를 지원하지 않는다면(또는 기본 JNDI 구현체가 Referenceable 인스턴스를 지원하지 않는다

면) hibernate.jndi.url과 hibernate.jndi.class 프로퍼티를 이용해서 JNDI 초기 컨텍스트를 설정

해야 한다.

다음 하이버네이트 설정은 SessionFactory를 썬의 (무료) 파일 시스템 기반 JNDI 구현인

fscontext.jar를 이용해서 java:/hibernate/MySessionFactory라는 이름으로 연동한다.

hibernate.connection.datasource = java:/MyDatasource

hibernate.transaction.factory_class = \

org.hibernate.transaction.JTATransactionFactory

hibernate.transaction.manager_lookup_class = \

org.hibernate.transaction.JBossTransactionManagerLookup

hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

hibernate.session_factory_name = java:/hibernate/MySessionFactory

hibernate.jndi.class = com.sun.jndi.fscontext.RefFSContextFactory

hibernate.jndi.url = file:/auction/jndi

Page 148: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

110 l 하이버네이트 완벽 가이드

물론 위 설정에 대한 XML 기반 설정도 사용할 수 있다. 이 예제는 현실적이지 않다. 왜냐하면

JNDI를 통해 연결 풀을 제공하는 대부분의 애플리케이션 서버에도 쓰기가 가능한 기본 컨텍스

트를 탑재하고 있는 JNDI 구현이 포함돼 있기 때문이다. 제이보스 AS도 분명히 이러한 구현을

포함하고 있으므로 마지막 두 설정은 생략하고 간단하게 SessionFactory 이름만 명시해도 된다.

참고 JNDI와 톰캣 톰캣은 서블릿 컨테이너를 구동한 이후에는 애플리케이션 수

준의 코드에서는 쓰기가 불가능한 읽기 전용 JNDI 컨텍스트를 탑재하고 있

다. 하이버네이트는 이 컨텍스트에 연동될 수 없다. 그러므로 완전한 컨텍스트

구현(썬 FS 컨텍스트 같은)을 사용하거나 session_factory_name 프로퍼티를

설정에서 제거하여 JNDI 연동 기능을 비활성화한다.

SessionFactory는 생성될 때, 즉 Con�guration.buildSessionFactory()를 호출할 때 JNDI와

연동된다. 애플리케이션 코드를 이식 가능하게 유지하려면 예제 2.18에 나와 있는 것과 같은 생

성 및 룩업 기능을 HibernateUtil에 구현하여 데이터 접근 코드에서는 변함 없이 이 도우미 클래

스를 계속 사용할 수 있게끔 해야 한다.

예제 2.18 SessionFactory를 JNDI에서 가져오는 HibernateUtil

public class HibernateUtil {

private static Context jndiContext;

static {

try {

// SessionFactory를 생성하고 JNDI와 연동한다

new Configuration().buildSessionFactory();

// 레지스트리에 대한 참조를 가져온다(jndi.properties 읽음)

jndiContext = new InitialContext();

} catch (Throwable ex) {

throw new ExceptionInInitializerError(ex);

}

}

public static SessionFactory getSessionFactory(String sfName) {

SessionFactory sf;

try {

sf = (SessionFactory) jndiContext.lookup(sfName);

} catch (NamingException ex) {

Page 149: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 111

throw new RuntimeException(ex);

}

return sf;

}

}

이것의 대안으로 SessionFactory를 애플리케이션 코드에서 JNDI를 직접 호출해 가져올 수

도 있다. 하지만 여전히 최소한 new Configuration().buildSessionFactory()라는 구동 코드

를 애플리케이션 어딘가에서 호출해야 한다. 하이버네이트 구동 코드의 이 마지막 줄을 제거해

서 HibernateUtil 클래스를 완전히 제거하는 한 가지 방법은 하이버네이트를 JMX 서비스(또는

JPA와 자바 EE를 이용해서)로 배포하는 것이다.

2.4.3 JMX 서비스 배포

자바 세상은 스펙, 표준, 그리고 그것의 구현체로 가득 차 있다. 그 중에서 비교적 새롭지만 중요

한 표준이 초기 버전으로 존재한다. JMX(Java Management Extensions)이 바로 그것이다. JMX는

시스템 컴포넌트나 (오히려) 시스템 서비스의 관리와 관련된 표준이다.

하이버네이트는 이 새로운 그림 중 어디에 들어 맞을까? 애플리케이션 서버에 배포될 때 하이

버네이트는 관리형 트랜잭션과 풀에서 관리되는 데이터소스 같은 다른 서비스를 이용한다. 마찬

가지로 하이버네이트를 JMX와 통합하면 하이버이네트는 관리형 JMX 서비스가 되어 다른 서비

스에 의존할 수도, 다른 서비스를 이용할 수도 있게 된다.

JMX 명세에는 다음과 같은 구성요소를 정의돼 있다.

▒ JMX MBean: 재사용 가능한 컴포넌트(보통 기반 시설에 해당하는)로 관리용(관리자용)

인터페이스를 외부에 제공한다.

▒ JMX 컨테이너: MBean으로의 일반적인 접근(로컬 또는 원격)을 중재한다.

▒ JMX 클라이언트: 관리자가 JMX 컨테이너를 통해 MBean에 접근할 때 사용한다.

JMX를 지원하는 애플리케이션 서버(제이보스 AS와 같은)는 마치 JMX 컨테이너처럼 동작하

며 애플리케이션 서버 시작 과정 중 하나로 MBean을 초기화하고 설정한다. 여러분의 하이버네

이트 서비스는 아마 JMX MBean으로 패키징되어 배포될 것이다. 이때 함께 배포되는 인터페이

스는 org.hibernate.jmx.HibernateService다. 표준 JMX 클라이언트라면 이 인터페이스를 이용

하여 하이버네이트 코어를 시작, 중지, 모니터링할 수 있다. 부가적으로 배포할 수 있는 두 번째

Page 150: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

112 l 하이버네이트 완벽 가이드

MBean 인터페이스는 org.hibernate.jmx.StatisticsService로서 JMX 클라이언트를 이용해 하이

버네이트의 런타임 행위를 이용하거나 모니터링할 수 있다.

JMX 서비스와 MBean을 배포하는 방법은 벤더에 따라 다르다. 예를 들어, 제이보스 애플리케

이션 서버를 사용한다면 jboss-service.xml 파일을 애플리케이션의 EAR에 추가하고 하이버네

이트를 관리형 JMX 서비스로 배포하기만 하면 된다.

여기서 모든 옵션을 설명하는 대신 제이보스 애플리케이션 서버 레퍼런스 문서를 참조하기 바

란다. 이 문서에는 하이버네이트 연동과 배포를 단계적으로 보여주는 절이 포함돼 있다(http://

docs.jboss.org/jbossas). JMX를 지원하는 다른 애플리케이션 서버에 설정하고 배포하는 것은

비슷할 것이므로 제이보스 설정 파일을 가져다 사용할 수 있을 것이다.

2.5 정리

이 장에서는 첫 번째 하이버네이트 프로젝트를 빠짐없이 살펴보았다. 여기서는 하이버네이트

XML 매핑 파일을 어떻게 작성하는지, 하이버네이트에서 어떤 API를 호출하여 데이터베이스와

상호작용하는지 살펴보았다.

그런 다음 자바 퍼시스턴스와 EJB 3.0을 소개하고 가장 기초적인 하이버네이트 애플리케이션

이더라도 어떻게 자동 메타데이터 감지, 표준화된 설정과 패키징, 관리형 EJB 컴포넌트의 의존성

주입을 이용해 단순화할 수 있는지 설명했다.

기존 데이터베이스를 가지고 시작해야 한다면 하이버네이트 도구 집합을 이용해서 기존 스키

마를 역공학해 XML 매핑 파일을 만들 수 있다. 아니면 JDK 5.0이나 EJB 3.0을 사용한다면 SQL

데이터베이스로부터 직접 자바 애플리케이션 코드를 생성할 수 있다.

마지막으로 자바 EE 환경에서 사용할 수 있는 더 수준 높은 하이버네이트 통합 및 설정 옵션

을 살펴보았다. JPA나 EJB 3.0을 사용하고 있다면 이미 통합된 것이나 마찬가지다.

표 2.1은 하이버네이트 기능과 자바 퍼시스턴스를 고수준에서 살펴보고 비교한 내용을 보여준

다. (이와 비슷한 비교 표를 모든 장에서 볼 수 있을 것이다.)

Page 151: 하이버네이트 완벽 가이드 : 자바 개발자를 위한 자바 퍼시스턴스 프로그래밍

2장 프로젝트 시작하기 l 113

표 2.1 하이버네이트와 JPA 비교

하이버네이트 코어 자바 퍼시스턴스와 EJB 3.0

어떤 것이든, 어느 곳에서든 통합 가능하다. 유연하지

만 간혹 설정이 복잡한 경우도 있다.

자바 EE와 자바 SE에서 동작한다. 간단하며 표준화

된 설정을 제공하며, 자바 EE 환경에서 별도의 통합

이나 특별한 설정이 필요 없다.

설정에 XML 매핑 파일이나 애노테이션을 지정한 클

래스 목록이 필요한다.

JPA 공급자가 XML 매핑 파일과 애노테이션을 지정

한 클래스를 자동으로 찾는다.

비표준이지만 강력하고 지속적으로 고유의 프로그래

밍 인터페이스와 쿼리 언어를 개선해 나가고 있다.

표준화되었으며 안정화된 인터페이스로, 하이버네이

트가 제공하는 기능의 부분집합에 해당한다. 하이버

네이트 API로 전환하기 쉽다.

다음 장에서는 이 책의 남은 부분에서 다룰 좀더 복잡한 예제를 소개하겠다. 도메인 모델을 어

떻게 설계하고 구현하는지, 어떤 매핑 메타데이터 옵션이 대규모 프로젝트에서 최선의 선택인지

살펴보겠다.