2009년 11월 26일
하이버네이트 우수 실천법
하이버네이트 레퍼런스 문서의 뒷부분에 있는 24장 Best Practices를 번역했습니다. 하이버네이트 한글 레퍼런스가 있을 줄 알았는데 번역된 것을 찾지 못하겠네요.
하아버네이트를 배우고 쓰면서 생소한 환경에 어려움을 격게 되는데 사용법을 익히고나서 보면 많이 도움이 되는 글입니다.
세분화된 클래스를 작성하고 <component>로 매핑한다.
street, suburb, state, postcode를 Address 클래스로 캡슐화 한다. 이렇게 하면 코드 재사용성이 올라가고 리팩토링하기 쉬워진다.
영속 객체에 ID 속성을 선언한다.
하이버네이트에서 ID 속성을 꼭 만들어야 하는 건 아니지만 ID 속성을 써야만 하는 여러가지 이유가 있다. ID는 업무상 의미가 없도록 인위적으로 생성된 모조 키가 좋다.
자연 키(natural key)를 식별한다.
모든 엔티티에서 자연 키를 식별하고 <natural-id>로 매핑한다. 자연 키에 해당하는 속성들을 비교하도록 equals()와 hashCode()를 구현한다.
각 클래스 매핑은 개별 파일에 저장한다.
한 파일에 매핑 정보를 몰아 넣지 않는다. com.eg.Foo 클래스는 com/eg/Foo.hbm.xml 파일에서 매핑한다. 팀으로 일하는 환경이라면 이 방식이 특히 합리적이다.
매핑 파일을 자원으로 적재한다.
매핑 파일은 매핑 대상 클래스와 함께 배포한다.
질의 문자열을 외부에 두는 것을 고려한다.
질의문이 비 ANSI 표준 SQL 함수를 호출한다면 이렇게 하는 게 좋다. 질의 문자열을 외부 매핑 파일에 두면 응용 S/W가 더 유연해진다.
바인드 변수를 사용한다.
JDBC에서처럼 비 상수 값을 "?" 문자로 대체한다. 질의문에 비 상수 값을 넣으려고 문자열 조작을 하지 말아라. 질의문에 이름을 붙힌 매개변수(named parameter)를 쓰는 것도 고려할 수 있다.
JDBC 연결을 따로 관리하지 않는다.
하이버네이트는 응용 S/W가 JDBC 연결을 관리하도록 허용하지만 이 방식은 마지막 수단으로 생각해야 한다. 내장 연결 제공자를 사용할 수 없다면 org.hibernate.connection.ConnectionProvider의 독자 구현을 제공하는 것도 검토해보도록 한다.
자작 타입(custom type)을 고려한다.
라이브러리에서 가지고 온 자바 타입이 있고 이것을 영속화해야 하는데 컴포넌트로서 매핑하려해도 필요한 접근자가 없다고 하자. 이럴 때는 org.hibernate.UserType을 구현해 볼 만하다. 이 방식을 쓰면 하이버네이트 타입으로 변환하는 작업을 응용 프로그램 코드에서 따로 구현하지 않아도 된다.
병목지점에 JDBC를 사용한다.
시스템 중 성능이 중요한 부분에서 어떤 작업은 직접 JDBC를 쓰는 것이 유리할 수 있다. 그렇지만 JDBC가 필요하다고 너무 빨리 가정하지 말라. 어떤 지점이 병목인지 알 때까지 판단을 유보해라. 직접 JDBC를 써야 한다면 하이버네이트의 Session을 열고 여기에서 JDBC 연결을 받아 사용하도록 한다. 이렇게 해서 여전히 같은 트랜젝션 전략과 연결 제공자를 사용할 수 있다.
세션 비우기(session flushing)을 이해해라.
이따금 Session은 자신의 영속 상태를 데이터베이스와 동기화 한다. 이 작업이 너무 자주 일어난다면 성능에 영향을 줄 것이다. 때로는 자동 비우기를 꺼서 필요 없는 비우기를 최소화 할 수 있다. 또는, 특정 트랜젝션내의 질의문이나 여타 작업의 순서를 바꿔서 그렇게 하기도 한다.
삼중 계층(3-tier) 구조에서는 이탈(detached) 객체를 고려한다.
서블릿/세선 빈 아키텍쳐를 쓸 때는 세션 빈에서 읽은 영속 객체를 서블릿/JSP 단에 보내거나 받을 수 있 수 있다. 매 요청을 처리할 때 마다 새 Session을 사용해라. Session.merge()나 Session.saveOrUpdate()를 써서 객체를 데이터베이스와 동기화 해라.
이중 계층(2-tier) 구조에서는 긴 영속 문맥(long persistence contexts)를 고려한다.
성능 확장성을 최대한 높이려면 데이터베이스 트랜잭션은 가능한 짧아야 한다. 그렇지만, 종종 오랫동안 운영되는 응용 트랜잭션이 필요하다. 응용 트랜잭션이란 사용자의 관점에서 본 단위 작업 하나를 말한다. 응용 트랜잭션 하나가 여러 클라이언트 요청/답변 주기에 걸쳐있을 수도 있다. 응용 트랜잭션을 구현할 때에 이탈 객체를 쓰는 것이 일반적이다. 2-티어 아키택처에 적당한 대안은 단일 개방 영속 접촉 세션을 응용 트랜잭션의 전체 생애 주기 동안 유지하는 것이다. 요청이 끝나면 단순히 JDBC 연결을 끊고 이어지는 요청이 시작할 때 재접속한다. 한 세션을 여러 응용 트랜잭션이 공유하지 말아야 한다. 그렇지 않으면 노후한 데이터로 일을하게 될 것이다.
예외(Exception)를 복구할 수 있는 것처럼 다루지 말아라.
이것은 우수 실천법이라기 보다는 필수 실천법에 더 가깝다. 예외가 발생하면 Transaction을 되돌리고 Session을 닫아라. 이렇게 하지 않으면, 하이버네이트는 메모리 상의 상태가 영속 상태를 정확히 표현한다고 보장할 수 없다. 예를 들어, 데이터베이스에 특정 ID가 있는지 확인할 때 query 대신 Session.load()를 쓰지 말아라.
연관(association)에는 유보적 취득(Lazy fetching)을 택하라.
모든 연관 객체를 미리 다 읽어 놓지 말고 연관 대부분을 클래스에 플락시(proxy)와 유보적 수집을 적용하여 처리하도록 한다. 이렇게 하면 2차 캐시에 모든 것을 다 담아두지 않을 수 있다. 연관된 클래스가 캐시되어 있다면 캐시 적중율은 극적으로 올라갈 것이다. lazy='false' 사용하면 명시적으로 유보적 취득 기능을 끌 수 있다. 조인으로 취득해야 하는 일이 그리 많지 않고 한두 쓰임새에서만 쓰인다면 질의문에 left join fetch을 사용한다.
미취득 데이터 문제를 예방하려면 뷰 단까지 세션 개방하기(Open session in view) 패턴이나 잘 통제된 조립 단계를 사용한다.
하이버네이트는 데이터 전송 객체(Data Transfer Objects, DTO)을 작성하는 지루한 작업에서 개발자를 자유롭게 한다. 전통적인 EJB 아키텍처에서 DTO 두 가지 목적으로 쓰였다. 우선은 엔티티 빈을 직렬화 할 수 없는 문제를 해결한다. 두 번째로는 암묵적으로 조립 단계를 정의한다. 조립 단계에서는 제어권을 표현 단으로 넘기기 전에 뷰에서 사용될 모든 데이터를 취득하고 DTO에 정리한다. 하이버네이트를 쓰면 첫번째 목적은 의미 없다. 영속화 문맥(persistence context; 세션)을 뷰 생성 단계에까지 열어둘 준비가 되어있지 않다면 조립 단계는 여전히 필요하다. 비즈니스 절차를 어떤 데이터가 이탈 객체로서 표현 단에 제공될지에 대한 엄격한 제약을 갖는 것으로 여기도록 한다. 이것은 하이버네이트의 제약이 아니고 안전한 데이터를 주고 받기 위한 기본적인 요건이다.
하이버네이트에서 비즈니스 로직을 추상화하도록 고려한다.
하이버네이트 데이터 접근 코드를 인터페이스 뒤에 숨기도록 해라. DAO와 쓰레드 지역 세션(Thread Local Session) 패턴을 조합해라. 심지어 어떤 클래스는 UserType으로 하이버네이트에 연결돤 JDBC로 직접 코드를 작성해 영속화 할 수 있다. 하지만, 이 조언은 충분히 큰 응용 S/W를
고려한 것이지 달랑 테이블 다섯개인 응용 S/W에는 적당하지 않다.
특이한 연관 매핑을 쓰지 말라.
진짜로 다대다 연관이 필요한 실용적 테스트 케이스는 흔치 않다. 대부분 연계 테이블(link table)에 부가 정보를 저장해야만 하는데 이럴 때는 두 일대다 관계로 분리하고 중간에 연계 클래스(link class)를 두는 것이 훨씬 좋다. 사실, 대부분의 연관은 일대다이거나 다대일이다. 이 때문에, 다른 연관 유형을 사용할 때에는 조심해서 처리해야 한다.
양방향 연관을 활용하라.
단방향 연관은 질의하기 어려운 편이다. 대규모 응용 S/W에서는 질의 시 연관 대부분이 양쪽 방향으로 탐색 가능해야 한다.
하아버네이트를 배우고 쓰면서 생소한 환경에 어려움을 격게 되는데 사용법을 익히고나서 보면 많이 도움이 되는 글입니다.
세분화된 클래스를 작성하고 <component>로 매핑한다.
street, suburb, state, postcode를 Address 클래스로 캡슐화 한다. 이렇게 하면 코드 재사용성이 올라가고 리팩토링하기 쉬워진다.
영속 객체에 ID 속성을 선언한다.
하이버네이트에서 ID 속성을 꼭 만들어야 하는 건 아니지만 ID 속성을 써야만 하는 여러가지 이유가 있다. ID는 업무상 의미가 없도록 인위적으로 생성된 모조 키가 좋다.
자연 키(natural key)를 식별한다.
모든 엔티티에서 자연 키를 식별하고 <natural-id>로 매핑한다. 자연 키에 해당하는 속성들을 비교하도록 equals()와 hashCode()를 구현한다.
각 클래스 매핑은 개별 파일에 저장한다.
한 파일에 매핑 정보를 몰아 넣지 않는다. com.eg.Foo 클래스는 com/eg/Foo.hbm.xml 파일에서 매핑한다. 팀으로 일하는 환경이라면 이 방식이 특히 합리적이다.
매핑 파일을 자원으로 적재한다.
매핑 파일은 매핑 대상 클래스와 함께 배포한다.
질의 문자열을 외부에 두는 것을 고려한다.
질의문이 비 ANSI 표준 SQL 함수를 호출한다면 이렇게 하는 게 좋다. 질의 문자열을 외부 매핑 파일에 두면 응용 S/W가 더 유연해진다.
바인드 변수를 사용한다.
JDBC에서처럼 비 상수 값을 "?" 문자로 대체한다. 질의문에 비 상수 값을 넣으려고 문자열 조작을 하지 말아라. 질의문에 이름을 붙힌 매개변수(named parameter)를 쓰는 것도 고려할 수 있다.
JDBC 연결을 따로 관리하지 않는다.
하이버네이트는 응용 S/W가 JDBC 연결을 관리하도록 허용하지만 이 방식은 마지막 수단으로 생각해야 한다. 내장 연결 제공자를 사용할 수 없다면 org.hibernate.connection.ConnectionProvider의 독자 구현을 제공하는 것도 검토해보도록 한다.
자작 타입(custom type)을 고려한다.
라이브러리에서 가지고 온 자바 타입이 있고 이것을 영속화해야 하는데 컴포넌트로서 매핑하려해도 필요한 접근자가 없다고 하자. 이럴 때는 org.hibernate.UserType을 구현해 볼 만하다. 이 방식을 쓰면 하이버네이트 타입으로 변환하는 작업을 응용 프로그램 코드에서 따로 구현하지 않아도 된다.
병목지점에 JDBC를 사용한다.
시스템 중 성능이 중요한 부분에서 어떤 작업은 직접 JDBC를 쓰는 것이 유리할 수 있다. 그렇지만 JDBC가 필요하다고 너무 빨리 가정하지 말라. 어떤 지점이 병목인지 알 때까지 판단을 유보해라. 직접 JDBC를 써야 한다면 하이버네이트의 Session을 열고 여기에서 JDBC 연결을 받아 사용하도록 한다. 이렇게 해서 여전히 같은 트랜젝션 전략과 연결 제공자를 사용할 수 있다.
세션 비우기(session flushing)을 이해해라.
이따금 Session은 자신의 영속 상태를 데이터베이스와 동기화 한다. 이 작업이 너무 자주 일어난다면 성능에 영향을 줄 것이다. 때로는 자동 비우기를 꺼서 필요 없는 비우기를 최소화 할 수 있다. 또는, 특정 트랜젝션내의 질의문이나 여타 작업의 순서를 바꿔서 그렇게 하기도 한다.
삼중 계층(3-tier) 구조에서는 이탈(detached) 객체를 고려한다.
서블릿/세선 빈 아키텍쳐를 쓸 때는 세션 빈에서 읽은 영속 객체를 서블릿/JSP 단에 보내거나 받을 수 있 수 있다. 매 요청을 처리할 때 마다 새 Session을 사용해라. Session.merge()나 Session.saveOrUpdate()를 써서 객체를 데이터베이스와 동기화 해라.
이중 계층(2-tier) 구조에서는 긴 영속 문맥(long persistence contexts)를 고려한다.
성능 확장성을 최대한 높이려면 데이터베이스 트랜잭션은 가능한 짧아야 한다. 그렇지만, 종종 오랫동안 운영되는 응용 트랜잭션이 필요하다. 응용 트랜잭션이란 사용자의 관점에서 본 단위 작업 하나를 말한다. 응용 트랜잭션 하나가 여러 클라이언트 요청/답변 주기에 걸쳐있을 수도 있다. 응용 트랜잭션을 구현할 때에 이탈 객체를 쓰는 것이 일반적이다. 2-티어 아키택처에 적당한 대안은 단일 개방 영속 접촉 세션을 응용 트랜잭션의 전체 생애 주기 동안 유지하는 것이다. 요청이 끝나면 단순히 JDBC 연결을 끊고 이어지는 요청이 시작할 때 재접속한다. 한 세션을 여러 응용 트랜잭션이 공유하지 말아야 한다. 그렇지 않으면 노후한 데이터로 일을하게 될 것이다.
예외(Exception)를 복구할 수 있는 것처럼 다루지 말아라.
이것은 우수 실천법이라기 보다는 필수 실천법에 더 가깝다. 예외가 발생하면 Transaction을 되돌리고 Session을 닫아라. 이렇게 하지 않으면, 하이버네이트는 메모리 상의 상태가 영속 상태를 정확히 표현한다고 보장할 수 없다. 예를 들어, 데이터베이스에 특정 ID가 있는지 확인할 때 query 대신 Session.load()를 쓰지 말아라.
연관(association)에는 유보적 취득(Lazy fetching)을 택하라.
모든 연관 객체를 미리 다 읽어 놓지 말고 연관 대부분을 클래스에 플락시(proxy)와 유보적 수집을 적용하여 처리하도록 한다. 이렇게 하면 2차 캐시에 모든 것을 다 담아두지 않을 수 있다. 연관된 클래스가 캐시되어 있다면 캐시 적중율은 극적으로 올라갈 것이다. lazy='false' 사용하면 명시적으로 유보적 취득 기능을 끌 수 있다. 조인으로 취득해야 하는 일이 그리 많지 않고 한두 쓰임새에서만 쓰인다면 질의문에 left join fetch을 사용한다.
미취득 데이터 문제를 예방하려면 뷰 단까지 세션 개방하기(Open session in view) 패턴이나 잘 통제된 조립 단계를 사용한다.
하이버네이트는 데이터 전송 객체(Data Transfer Objects, DTO)을 작성하는 지루한 작업에서 개발자를 자유롭게 한다. 전통적인 EJB 아키텍처에서 DTO 두 가지 목적으로 쓰였다. 우선은 엔티티 빈을 직렬화 할 수 없는 문제를 해결한다. 두 번째로는 암묵적으로 조립 단계를 정의한다. 조립 단계에서는 제어권을 표현 단으로 넘기기 전에 뷰에서 사용될 모든 데이터를 취득하고 DTO에 정리한다. 하이버네이트를 쓰면 첫번째 목적은 의미 없다. 영속화 문맥(persistence context; 세션)을 뷰 생성 단계에까지 열어둘 준비가 되어있지 않다면 조립 단계는 여전히 필요하다. 비즈니스 절차를 어떤 데이터가 이탈 객체로서 표현 단에 제공될지에 대한 엄격한 제약을 갖는 것으로 여기도록 한다. 이것은 하이버네이트의 제약이 아니고 안전한 데이터를 주고 받기 위한 기본적인 요건이다.
하이버네이트에서 비즈니스 로직을 추상화하도록 고려한다.
하이버네이트 데이터 접근 코드를 인터페이스 뒤에 숨기도록 해라. DAO와 쓰레드 지역 세션(Thread Local Session) 패턴을 조합해라. 심지어 어떤 클래스는 UserType으로 하이버네이트에 연결돤 JDBC로 직접 코드를 작성해 영속화 할 수 있다. 하지만, 이 조언은 충분히 큰 응용 S/W를
고려한 것이지 달랑 테이블 다섯개인 응용 S/W에는 적당하지 않다.
특이한 연관 매핑을 쓰지 말라.
진짜로 다대다 연관이 필요한 실용적 테스트 케이스는 흔치 않다. 대부분 연계 테이블(link table)에 부가 정보를 저장해야만 하는데 이럴 때는 두 일대다 관계로 분리하고 중간에 연계 클래스(link class)를 두는 것이 훨씬 좋다. 사실, 대부분의 연관은 일대다이거나 다대일이다. 이 때문에, 다른 연관 유형을 사용할 때에는 조심해서 처리해야 한다.
양방향 연관을 활용하라.
단방향 연관은 질의하기 어려운 편이다. 대규모 응용 S/W에서는 질의 시 연관 대부분이 양쪽 방향으로 탐색 가능해야 한다.
# by | 2009/11/26 19:39 | 프로그래밍 이야기 | 트랙백(1) | 덧글(0)

![[수입] 요요 마가 연주한 엔니오 모리코네 [CD+DVD 듀얼 디스크]](http://image.aladdin.co.kr/coveretc/music/coveroff/2592437362_1.jpg)
![[SACD] 조수미 (Sumi Jo) - Journey To Baroque (바로크로의 여행)](http://image.aladdin.co.kr/coveretc/music/coveroff/1011203995_1.jpg)






