6. 영속성 생명 주기 – 조금 더 단순하게…
비영속 객
체
(Transient)
영속 객체
(Persistent)
준영속 객
체
(Detached)
get(), load()
, any query
7. 영속성 생명 주기 – 객체 상태
• 비영속 객체(Transient)
– new 연산자로 생성된 객체
– DB나 영속성 컨텍스트와 연관이 없다.
• 영속 객체(Persistent)
– DB와 동일성을 지닌 엔터티(PK 기준)
– 플러시 시점에 DB에 동기화(변경 내용 자동 반영)
• 준영속 객체(Detached)
– 작업 단위가 완료되어 영속성 컨텍스트(세션)가 닫힘
– 재진입, 병합을 통해 영속 상태로 진입
• 삭제 객체(Removed)
– DB에서 삭제됨
9. 영속성 컨텍스트 특징
• 영속 객체 자동 변경 감지
• 1차 캐시
• 객체 동일성 보장(== 비교)
• 트랜잭션을 지원하는 지연 쓰기
• 지연 로딩
• 일반적으로 쓰래드 단위
10. 영속성 컨텍스트 – 자동 변경 감지
• 영속 객체의 현재 스냅샷과 이전 스냅샷을 비교
• 플러시 시점에 변경된 객체를 찾아 자동 업데이
트 수행
• 동적 업데이트 설정을 (dynamicUpdate = true)
통해 변경된 컬럼만 업데이트 가능
11. 영속성 컨텍스트 – 1차 캐시
• PK로 객체 조회시 영속성 컨텍스트에서 찾아
반환
– 컨텍스트에 존재 안 할 경우만 DB 쿼리 수행
– get(), load()
• 일반적인 쿼리를 수행 한 경우에도 쿼리 결과
집합은 영속성 컨텍스트와 상호 작용함
– 예) A가 이미 컨텍스트에 있는 상태에서 쿼리 결과
가 A, B, C이면 A는 컨텍스트에 이미 있는 것으로
대체 하여 결과 집합 반환.
– 값만 조회하는 스칼라 쿼리 제외
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
17.
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
25.
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)