Hibernate 초급을 넘어서
박성재
bluehatch@gmail.com
하이버네이트 요소
• 클래스 맵핑
• 연관 관계 맵핑
• 영속성 컨텍스트
• 트랜잭션
• Fetch와 2nd Cache
• HQL, Criteria
오늘은 이것만
• 클래스 맵핑
• 연관 관계 맵핑
• 영속성 컨텍스트
• 트랜잭션
• Fetch와 2nd Cache
• HQL, Criteria
영속성 생명 주기
영속성 생명 주기 – 조금 더 단순하게…
비영속 객
체
(Transient)
영속 객체
(Persistent)
준영속 객
체
(Detached)
get(), load()
, any query
영속성 생명 주기 – 객체 상태
• 비영속 객체(Transient)
– new 연산자로 생성된 객체
– DB나 영속성 컨텍스트와 연관이 없다.
• 영속 객체(Persistent)
– DB와 동일성을 지닌 엔터티(PK 기준)
– 플러시 시점에 DB에 동기화(변경 내용 자동 반영)
• 준영속 객체(Detached)
– 작업 단위가 완료되어 영속성 컨텍스트(세션)가 닫힘
– 재진입, 병합을 통해 영속 상태로 진입
• 삭제 객체(Removed)
– DB에서 삭제됨
영속성 컨텍스트(aka 세션)
Database
영속성 컨텍스트 특징
• 영속 객체 자동 변경 감지
• 1차 캐시
• 객체 동일성 보장(== 비교)
• 트랜잭션을 지원하는 지연 쓰기
• 지연 로딩
• 일반적으로 쓰래드 단위
영속성 컨텍스트 – 자동 변경 감지
• 영속 객체의 현재 스냅샷과 이전 스냅샷을 비교
• 플러시 시점에 변경된 객체를 찾아 자동 업데이
트 수행
• 동적 업데이트 설정을 (dynamicUpdate = true)
통해 변경된 컬럼만 업데이트 가능
영속성 컨텍스트 – 1차 캐시
• PK로 객체 조회시 영속성 컨텍스트에서 찾아
반환
– 컨텍스트에 존재 안 할 경우만 DB 쿼리 수행
– get(), load()
• 일반적인 쿼리를 수행 한 경우에도 쿼리 결과
집합은 영속성 컨텍스트와 상호 작용함
– 예) A가 이미 컨텍스트에 있는 상태에서 쿼리 결과
가 A, B, C이면 A는 컨텍스트에 이미 있는 것으로
대체 하여 결과 집합 반환.
– 값만 조회하는 스칼라 쿼리 제외
영속성 컨텍스트 – 1차 캐시
영속성 컨텍스트 – 지연 쓰기/지연 로딩
• 하이버네이트는 DB 요청을 최대한 뒤로 미룬다.
– 변경 사항을 모아 DB 요청을 최소화
– DB lock을 최대한 짧게 유지
• 지연 로딩을 통해 성능 향상
– 세션 내에서만 유효(LazyInitializationException)
• update()는 준영속 객체를 컨텍스트에 재진입하는
용도이다.
– 사실 reattach() 라는 이름이 더 적합
– 세션 내에서 명시적 호출이 있더라도 플러시 시점에 동작
함
영속성 컨텍스트 – 플러시
• 영속성 컨텍스트의 변경 내용을 DB와 동기화
– 컨텍스트 비우는거 아님~!
• 플러시 모드 : AUTO, COMMIT, MANUAL, NEVER
• AUTO 모드
– Hibernate 기본 모드
– 플러시 동작 시점: 트랜잭션 커밋, 명시적인 Session.flush(), 쿼리 실행 전
• MANUAL 모드
– 트랜잭션 내에서 DB 쿼리가 많으며 컨텍스트에 대량의 인스턴스가 로딩되는 작업인
경우 MANUAL 모드 사용하자~
영속성 컨텍스트 – Open Session In View 패턴
• 영속성 컨텍스트(세션)를 View까지 열어둠
• View에서 지연 로딩이 가능해짐
• OSIV vs FACADE vs DTO
– OSIV를 사용하지 않으면 준영속 상태가 되기 전에 프락시
(지연 로딩 객체)를 초기화 해야 함
• 스프링 OSIV는 엔터티 수정을 트랜잭션이 동작하는
계층에서만 지원
– 초기 플러시 모드: FlushMode.MANUAL
– 각 트랜잰셕 시작시 FlushMode.AUTO로 변경되며 각 트랜
잭션이 종료되면 다시 FlushMode.MANUAL로 원복
트랜잭션 범위
영속성 컨텍스트 – 스프링 OSIV
Filter
Interceptor
Controller
View
Service DAO
영속성 컨텍스트(세션) 생존 범위
FlushMode.MANUAL FlushMode.AUTO
트랜잭션 – ACID
• 원자성(Atomic)
– 트랜잭션 내의 여러 작업은 모두 성공 혹은 모두 실패
• 일관성(Consistency)
– 예)무결정 제약 조건
• 격리성(Isolation)
– 동시에 실행하는 트랜잭션들이 서로에게 영향을 미치지 않
도록 격리되어야 함
• 지속성(Durability)
– 트랜잭션이 성공적으로 끝나면 결과는 기록되어야 함
트랜잭션 – 격리 수준
격리 수준 DIRTY READ NON-REPATABLE
READ
PHANTOM READ
커밋되지 않은 읽
기
O O O
커밋된 읽기 O O
반복 가능한 읽기 O
직렬화
• DIRTY READ: 커밋되지 않고 수정 중인 데이터를 읽는 문제
• NON-REPATABLE READ: 한 트랜잭션 내에서 같은 쿼리를 두 번 수행할 때 그 사이에 다
른 트랜잭션이 값을 수정 또는 삭제함으로써 두 쿼리의 결과가 상이하게 나타나는 비 일관
성 발생
• PHANTOM READ: 한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽을 때, 첫 번째
쿼리에서 없던 레코드가 두 번째 쿼리에서 나타나는 현상. 이는 트랜잭션 도중 새로운 레코
드가 삽입되는 것을 허용하기 때문에 나타남.
트랜잭션 – 격리 수준(하이버네이트 적용)
격리 수준 DIRTY READ NON-REPATABLE
READ
PHANTOM READ
커밋되지 않은 읽
기
O O O
커밋된 읽기(일반
적인 수준)
영속성 컨텍스트+
버전 관리로 해결
O
반복 가능한 읽기 O
직렬화
• DIRTY READ: 커밋되지 않고 수정 중인 데이터를 읽는 문제
• NON-REPATABLE READ: 한 트랜잭션 내에서 같은 쿼리를 두 번 수행할 때 그 사이에 다
른 트랜잭션이 값을 수정 또는 삭제함으로써 두 쿼리의 결과가 상이하게 나타나는 비 일관
성 발생
• PHANTOM READ: 한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽을 때, 첫 번째
쿼리에서 없던 레코드가 두 번째 쿼리에서 나타나는 현상. 이는 트랜잭션 도중 새로운 레코
드가 삽입되는 것을 허용하기 때문에 나타남.
트랜잭션 - 낙관적 락, 비관적 락
• 낙관적 락
– 어플리케이션(하이버네이트)에서 제공하는 락
– @Version -> 최초 커밋만 인정하기 두 번째부터 예외
를 던짐
• 비관적 락
– Database lock을 사용
– select for update
트랜잭션 – 경계 설정
• 프로그래밍 방식
– 스프링 TransactionTemplate 사용
– 정교하게 경계를 나눌 때 사용
• 선언적
– @Transactional(메소드 단위)
– AOP
– 일반적인 방법
트랜잭션 – 예외처리하기
• @Transactional 메소드 외부로 예외가 던져지면
롤백 수행
• 메소드 콜 트리에서 중첩된 @Transactional 중
예외가 밖으로 던져질 경우에도 롤백 수행
• 트랜잭션 롤백 예외
– rollbackFor, rollbackForClassName
– noRollbackFor, noRollbackForClassName
트랜잭션 – 스프링에서 제공하는 트랜잭션 전파
속성
• REQUIRED
– 기본 속성이며 대부분
이 속성이면 충분함
– 이미 시작된 트랜잭션
이 있으면 참여하고 없
으면 새로 시작
• REQUIRES_NEW
– 항상 새로운 트랜잭션
을 시작
– 이미 진행 중인 트랜잭
션이 있으면 잠시 보류
시킴
– 사용 예) 권한 체크, 감
사 로깅
• SUPPORTS, MANDATORY, NOT_SUPPORTED, NEVER, NESTED
N+1 문제
member
-id
-name
…
orders
-id
-member_id
…
1 *
-- get all of the member first
select * from member
-- get the orders for each member returned
select * from orders where member_id = 1
select * from orders where member_id = 2
select * from orders where member_id = 3
select * from orders where member_id = …
select * from orders where member_id = N
페치 전략
• Select 페치
– 연관된 인스턴스나 콜렉션을 하나씩 SELECT 실행
• Join 페치
– SELECT 절에서 OUTER JOIN으로 한번에 쿼리
– 중복 제거를 위해 distinct 필요
• Subselect 페치
– 이전 쿼리 조건을 sub query조건에 추가하여 SELECT 실행
– 맵핑시 결정: @Fetch(FetchMode.SUBSELECT)
• Batch 페치
– select fetching의 최적화 전략
– PK또는 FK 목록을 통해 하나의 SELECT절로 묶어서 쿼리
– 맵핑시 결정: @BatchSize(size=10)
N+1 문제 – 조인을 이용한 즉시 페치
-- HQL
select distinct m from Member m join fetch m.orders
-- 실행된 SQL
SELECT DISTINCT M.*, O.* FROM MEMBER M INNJER
JOIN ORDERS O ON M.ID = O.MEMBER_ID
-> distinct 옵션 적용 시 하이버네이트는 SQL에 DISTINCT
적용과 함께 결과 리스트에세 Member객체를 중복 제거
함
-> 컬렉션을 두 개 이상 조인 페치할 경우 카테시안 곱
(Cartesian Product) 발생함
N+1 문제 – Subselect 페치
-- HQL
select m from Member m where m.id > 10
-- 지연 로딩된 엔터티를 사용하는 시점의 SQL 실행문
SELECT O.* FROM ORDERS O
WHERE O.MEMBER_ID IN (
SELECT M.ID
FROM MEMBER M
WHERE M.ID > 10
)
N+1 문제 – Batch로 데이터 선행 페치
-- HQL
select m from Member …
-- 지연 로딩된 엔터티를 사용하는 시점의 SQL 실행문
(BatchSize개수 만큼 로딩하는 쿼리를 여러 번 수행)
SELECT O.* FROM ORDERS O
WHERE O.MEMBER_ID IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
하이버네이트의 영속성 컨텍스트와 패치 전략

하이버네이트의 영속성 컨텍스트와 패치 전략

  • 1.
  • 2.
    하이버네이트 요소 • 클래스맵핑 • 연관 관계 맵핑 • 영속성 컨텍스트 • 트랜잭션 • Fetch와 2nd Cache • HQL, Criteria
  • 3.
    오늘은 이것만 • 클래스맵핑 • 연관 관계 맵핑 • 영속성 컨텍스트 • 트랜잭션 • Fetch와 2nd Cache • HQL, Criteria
  • 5.
  • 6.
    영속성 생명 주기– 조금 더 단순하게… 비영속 객 체 (Transient) 영속 객체 (Persistent) 준영속 객 체 (Detached) get(), load() , any query
  • 7.
    영속성 생명 주기– 객체 상태 • 비영속 객체(Transient) – new 연산자로 생성된 객체 – DB나 영속성 컨텍스트와 연관이 없다. • 영속 객체(Persistent) – DB와 동일성을 지닌 엔터티(PK 기준) – 플러시 시점에 DB에 동기화(변경 내용 자동 반영) • 준영속 객체(Detached) – 작업 단위가 완료되어 영속성 컨텍스트(세션)가 닫힘 – 재진입, 병합을 통해 영속 상태로 진입 • 삭제 객체(Removed) – DB에서 삭제됨
  • 8.
  • 9.
    영속성 컨텍스트 특징 •영속 객체 자동 변경 감지 • 1차 캐시 • 객체 동일성 보장(== 비교) • 트랜잭션을 지원하는 지연 쓰기 • 지연 로딩 • 일반적으로 쓰래드 단위
  • 10.
    영속성 컨텍스트 –자동 변경 감지 • 영속 객체의 현재 스냅샷과 이전 스냅샷을 비교 • 플러시 시점에 변경된 객체를 찾아 자동 업데이 트 수행 • 동적 업데이트 설정을 (dynamicUpdate = true) 통해 변경된 컬럼만 업데이트 가능
  • 11.
    영속성 컨텍스트 –1차 캐시 • PK로 객체 조회시 영속성 컨텍스트에서 찾아 반환 – 컨텍스트에 존재 안 할 경우만 DB 쿼리 수행 – get(), load() • 일반적인 쿼리를 수행 한 경우에도 쿼리 결과 집합은 영속성 컨텍스트와 상호 작용함 – 예) A가 이미 컨텍스트에 있는 상태에서 쿼리 결과 가 A, B, C이면 A는 컨텍스트에 이미 있는 것으로 대체 하여 결과 집합 반환. – 값만 조회하는 스칼라 쿼리 제외
  • 12.
  • 13.
    영속성 컨텍스트 –지연 쓰기/지연 로딩 • 하이버네이트는 DB 요청을 최대한 뒤로 미룬다. – 변경 사항을 모아 DB 요청을 최소화 – DB lock을 최대한 짧게 유지 • 지연 로딩을 통해 성능 향상 – 세션 내에서만 유효(LazyInitializationException) • update()는 준영속 객체를 컨텍스트에 재진입하는 용도이다. – 사실 reattach() 라는 이름이 더 적합 – 세션 내에서 명시적 호출이 있더라도 플러시 시점에 동작 함
  • 14.
    영속성 컨텍스트 –플러시 • 영속성 컨텍스트의 변경 내용을 DB와 동기화 – 컨텍스트 비우는거 아님~! • 플러시 모드 : AUTO, COMMIT, MANUAL, NEVER • AUTO 모드 – Hibernate 기본 모드 – 플러시 동작 시점: 트랜잭션 커밋, 명시적인 Session.flush(), 쿼리 실행 전 • MANUAL 모드 – 트랜잭션 내에서 DB 쿼리가 많으며 컨텍스트에 대량의 인스턴스가 로딩되는 작업인 경우 MANUAL 모드 사용하자~
  • 15.
    영속성 컨텍스트 –Open Session In View 패턴 • 영속성 컨텍스트(세션)를 View까지 열어둠 • View에서 지연 로딩이 가능해짐 • OSIV vs FACADE vs DTO – OSIV를 사용하지 않으면 준영속 상태가 되기 전에 프락시 (지연 로딩 객체)를 초기화 해야 함 • 스프링 OSIV는 엔터티 수정을 트랜잭션이 동작하는 계층에서만 지원 – 초기 플러시 모드: FlushMode.MANUAL – 각 트랜잰셕 시작시 FlushMode.AUTO로 변경되며 각 트랜 잭션이 종료되면 다시 FlushMode.MANUAL로 원복
  • 16.
    트랜잭션 범위 영속성 컨텍스트– 스프링 OSIV Filter Interceptor Controller View Service DAO 영속성 컨텍스트(세션) 생존 범위 FlushMode.MANUAL FlushMode.AUTO
  • 18.
    트랜잭션 – ACID •원자성(Atomic) – 트랜잭션 내의 여러 작업은 모두 성공 혹은 모두 실패 • 일관성(Consistency) – 예)무결정 제약 조건 • 격리성(Isolation) – 동시에 실행하는 트랜잭션들이 서로에게 영향을 미치지 않 도록 격리되어야 함 • 지속성(Durability) – 트랜잭션이 성공적으로 끝나면 결과는 기록되어야 함
  • 19.
    트랜잭션 – 격리수준 격리 수준 DIRTY READ NON-REPATABLE READ PHANTOM READ 커밋되지 않은 읽 기 O O O 커밋된 읽기 O O 반복 가능한 읽기 O 직렬화 • DIRTY READ: 커밋되지 않고 수정 중인 데이터를 읽는 문제 • NON-REPATABLE READ: 한 트랜잭션 내에서 같은 쿼리를 두 번 수행할 때 그 사이에 다 른 트랜잭션이 값을 수정 또는 삭제함으로써 두 쿼리의 결과가 상이하게 나타나는 비 일관 성 발생 • PHANTOM READ: 한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽을 때, 첫 번째 쿼리에서 없던 레코드가 두 번째 쿼리에서 나타나는 현상. 이는 트랜잭션 도중 새로운 레코 드가 삽입되는 것을 허용하기 때문에 나타남.
  • 20.
    트랜잭션 – 격리수준(하이버네이트 적용) 격리 수준 DIRTY READ NON-REPATABLE READ PHANTOM READ 커밋되지 않은 읽 기 O O O 커밋된 읽기(일반 적인 수준) 영속성 컨텍스트+ 버전 관리로 해결 O 반복 가능한 읽기 O 직렬화 • DIRTY READ: 커밋되지 않고 수정 중인 데이터를 읽는 문제 • NON-REPATABLE READ: 한 트랜잭션 내에서 같은 쿼리를 두 번 수행할 때 그 사이에 다 른 트랜잭션이 값을 수정 또는 삭제함으로써 두 쿼리의 결과가 상이하게 나타나는 비 일관 성 발생 • PHANTOM READ: 한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽을 때, 첫 번째 쿼리에서 없던 레코드가 두 번째 쿼리에서 나타나는 현상. 이는 트랜잭션 도중 새로운 레코 드가 삽입되는 것을 허용하기 때문에 나타남.
  • 21.
    트랜잭션 - 낙관적락, 비관적 락 • 낙관적 락 – 어플리케이션(하이버네이트)에서 제공하는 락 – @Version -> 최초 커밋만 인정하기 두 번째부터 예외 를 던짐 • 비관적 락 – Database lock을 사용 – select for update
  • 22.
    트랜잭션 – 경계설정 • 프로그래밍 방식 – 스프링 TransactionTemplate 사용 – 정교하게 경계를 나눌 때 사용 • 선언적 – @Transactional(메소드 단위) – AOP – 일반적인 방법
  • 23.
    트랜잭션 – 예외처리하기 •@Transactional 메소드 외부로 예외가 던져지면 롤백 수행 • 메소드 콜 트리에서 중첩된 @Transactional 중 예외가 밖으로 던져질 경우에도 롤백 수행 • 트랜잭션 롤백 예외 – rollbackFor, rollbackForClassName – noRollbackFor, noRollbackForClassName
  • 24.
    트랜잭션 – 스프링에서제공하는 트랜잭션 전파 속성 • REQUIRED – 기본 속성이며 대부분 이 속성이면 충분함 – 이미 시작된 트랜잭션 이 있으면 참여하고 없 으면 새로 시작 • REQUIRES_NEW – 항상 새로운 트랜잭션 을 시작 – 이미 진행 중인 트랜잭 션이 있으면 잠시 보류 시킴 – 사용 예) 권한 체크, 감 사 로깅 • SUPPORTS, MANDATORY, NOT_SUPPORTED, NEVER, NESTED
  • 26.
    N+1 문제 member -id -name … orders -id -member_id … 1 * --get all of the member first select * from member -- get the orders for each member returned select * from orders where member_id = 1 select * from orders where member_id = 2 select * from orders where member_id = 3 select * from orders where member_id = … select * from orders where member_id = N
  • 27.
    페치 전략 • Select페치 – 연관된 인스턴스나 콜렉션을 하나씩 SELECT 실행 • Join 페치 – SELECT 절에서 OUTER JOIN으로 한번에 쿼리 – 중복 제거를 위해 distinct 필요 • Subselect 페치 – 이전 쿼리 조건을 sub query조건에 추가하여 SELECT 실행 – 맵핑시 결정: @Fetch(FetchMode.SUBSELECT) • Batch 페치 – select fetching의 최적화 전략 – PK또는 FK 목록을 통해 하나의 SELECT절로 묶어서 쿼리 – 맵핑시 결정: @BatchSize(size=10)
  • 28.
    N+1 문제 –조인을 이용한 즉시 페치 -- HQL select distinct m from Member m join fetch m.orders -- 실행된 SQL SELECT DISTINCT M.*, O.* FROM MEMBER M INNJER JOIN ORDERS O ON M.ID = O.MEMBER_ID -> distinct 옵션 적용 시 하이버네이트는 SQL에 DISTINCT 적용과 함께 결과 리스트에세 Member객체를 중복 제거 함 -> 컬렉션을 두 개 이상 조인 페치할 경우 카테시안 곱 (Cartesian Product) 발생함
  • 29.
    N+1 문제 –Subselect 페치 -- HQL select m from Member m where m.id > 10 -- 지연 로딩된 엔터티를 사용하는 시점의 SQL 실행문 SELECT O.* FROM ORDERS O WHERE O.MEMBER_ID IN ( SELECT M.ID FROM MEMBER M WHERE M.ID > 10 )
  • 30.
    N+1 문제 –Batch로 데이터 선행 페치 -- HQL select m from Member … -- 지연 로딩된 엔터티를 사용하는 시점의 SQL 실행문 (BatchSize개수 만큼 로딩하는 쿼리를 여러 번 수행) SELECT O.* FROM ORDERS O WHERE O.MEMBER_ID IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)