8.5 KiB
title, source_type, url, archive_url, related_branches, related_projects, tags, created, last_reviewed
| title | source_type | url | archive_url | related_branches | related_projects | tags | created | last_reviewed | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| company-tech-blog / Spring read-only transaction Hibernate optimization — Vlad Mihalcea | company-tech-blog | https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/ |
|
|
|
2026-06-04 | 2026-06-04 |
Spring read-only transaction Hibernate optimization — Vlad Mihalcea
Layer:
raw/company-tech-blogs/— Vlad Mihalcea 의 기술 블로그 (vladmihalcea.com) 포스트 "Spring read-only transaction Hibernate optimization" (2018-09-25) 발췌. Hibernate 작동 전문가인 저자가 Spring 5.1 에서 개선된@Transactional(readOnly=true)의 Hibernate 세션 최적화를 설명.출처 신뢰도:
engineering-blog등급 — Vlad Mihalcea 는 Hibernate core committer 이자 "High-Performance Java Persistence" 저자. 개인 블로그이나 Hibernate 공식 contributor 의 기술 분석. Spring 공식 문서가 아님.company-case-study승급 불가 (사례 아닌 기술 분석).
Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| raw/branch-notes/feature-application-query-bypass-contract | D2 (transaction bypass — read-only transaction 을 쓰지 않을 때의 실제 cost) 의 기술적 근거 — readOnly=true 가 Hibernate session setDefaultReadOnly(true) 로 propagate 되어 loaded state (hydrated state) 를 discarded 하는 최적화의 실제 의미. 단순 single-entity SELECT 에는 dirty-check overhead 가 미미함을 시사. |
출처 / Source
- 원본 URL: https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/
- 아카이브 URL:
- 저자 / 조직: Vlad Mihalcea (Hibernate core committer, "High-Performance Java Persistence" 저자)
- 발행일: 2018-09-25
- 마지막 확인일: 2026-06-04
왜 저장했는지 / Why archived
ca-tmpl 의 transaction bypass 결정(D2)에서 "no-tx read 가 얼마나 위험한가" 의 반대 근거로 보관. readOnly=true 의 실제 최적화 내용이 메모리 절약과 dirty-check skip 이며, 단순 단일 SELECT 에는 이 최적화의 이득이 미미함을 시사. 결과적으로 skeleton 의 no-tx read 허용 범위를 정당화하는 보조 근거.
핵심 인용 / Key quotes (verbatim)
"Prior to Spring 5.1, when using Hibernate, the readOnly attribute of the @Transactional annotation was only setting the current Session flush mode to FlushType.MANUAL, therefore disabling the automatic dirty checking mechanism."
"the readOnly attribute did not propagate to the underlying Hibernate Session, I decided to create the SPR-16956 issue and provided a Pull Request...which after being Jürgenized, it got integrated"
"upon loading an entity, the loaded state is stored by the Hibernate Session unless the entity is loaded in read-only mode."
"the main advantage of the Spring 5.1 read-only optimization for Hibernate is that we can save a lot of memory when loading read-only entities since the loaded state is discarded right away"
"if the user tries to do a manual flush, entities that are virtually read-only won't be propagated"
Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| VM-READTX-C1 | Spring 5.1 이전에는 @Transactional(readOnly=true) 가 Hibernate Session flush mode 를 FlushType.MANUAL 로만 설정하고, underlying Hibernate Session 에 propagate 되지 않았다 |
"Prior to Spring 5.1...the readOnly attribute...was only setting the current Session flush mode to FlushType.MANUAL, therefore disabling the automatic dirty checking mechanism." | engineering-blog |
Spring 5.1 미만 + Hibernate 사용 환경 | Spring 5.1+ 이후에도 flush mode 설정이 일어나지 않는다는 뜻은 아님 — 5.1+ 에서는 추가로 setDefaultReadOnly(true) 도 호출됨 |
| VM-READTX-C2 | Spring 5.1+ 에서는 @Transactional(readOnly=true) 가 underlying Hibernate Session 에 setDefaultReadOnly(true) 로 propagate 되어, 로드된 entity 의 hydrated state (loaded state snapshot) 이 즉시 discard 됨 |
"the readOnly attribute did not propagate to the underlying Hibernate Session" (결함 진술) + "upon loading an entity, the loaded state is stored by the Hibernate Session unless the entity is loaded in read-only mode." | engineering-blog |
Spring 5.1+ + Hibernate JPA provider 사용 환경 (HibernateJpaDialect 경유) | EclipseLink 등 다른 JPA provider 에도 동일 최적화가 적용된다는 보장 없음. Spring 5.1+ 에서도 HibernateJpaDialect 를 사용해야 적용됨 |
| VM-READTX-C3 | @Transactional(readOnly=true) 의 주요 이득은 메모리 절약 — read-only entity 로드 시 loaded state 가 즉시 discarded 되어 persistence context 존속 기간 동안 보관되지 않음 |
"the main advantage of the Spring 5.1 read-only optimization for Hibernate is that we can save a lot of memory when loading read-only entities since the loaded state is discarded right away" | engineering-blog |
많은 entity 를 로드하는 read-heavy operation | 단순 단일 entity 또는 단일 DTO projection SELECT 에 동일한 이득이 있다는 뜻은 아님 — 로드되는 entity 수가 많을수록 이득이 커짐 |
| VM-READTX-C4 | read-only entity 로 로드된 경우 manual flush 를 호출해도 해당 entity 는 propagate 되지 않음 — dirty check 자체가 skip 됨 | "if the user tries to do a manual flush, entities that are virtually read-only won't be propagated" | engineering-blog |
Hibernate Session 의 flush 가 read-only entity 에 미치는 영향 | read-only entity 를 변경하면 예외가 발생한다는 강제 보증은 본 인용에 없음 — 변경 자체는 가능하나 flush 시 반영 안 됨 |
Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
VM-READTX-C1: Spring 5.1 이전readOnly=true의 제한된 동작 (flush mode MANUAL 만)VM-READTX-C2: Spring 5.1+ 에서 HibernateJpaDialect 경유 시setDefaultReadOnly(true)전파VM-READTX-C3: 주요 이득 = 메모리 절약 (많은 entity 로드 시). query 속도 개선이 아님VM-READTX-C4: dirty check skip 으로 flush 시 read-only entity 는 DB 반영 안 됨
- 이 자료가 증명하지 않는 것:
- 단순 단일 entity SELECT 에서
readOnly=true유무의 실제 성능 차이 — 본 포스트의 예시는 여러 entity 를 findAllByTitle 로 bulk 로드하는 시나리오 readOnly=true없이 실행(no-tx or REQUIRED write tx)하는 simple SELECT 가 응용 결과에 영향을 주는 케이스 (dirty entity 가 없으면 flush 로 인한 추가 DML 없음)- OSIV(open-in-view) enabled 환경에서의 동작 차이
- DB connection 유지 시간의 차이 (transaction 경계 = connection 점유 기간 이지만 본 포스트는 이 cost 를 다루지 않음)
- 단순 단일 entity SELECT 에서
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-tmpl 에서
TransactionPort.inRead없이 실행되는 repository read method 가 HikariCP autocommit=true 환경에서 개별 connection 을 점유하는지 측정 - Hibernate 6 (Spring Boot 3.x) 에서
setDefaultReadOnly(true)가 실제로 loaded state 를 즉시 discard 하는지 통합 테스트 검증
- ca-tmpl 에서
메모 / Notes
- 중요 해석: VM-READTX-C3 가 명시하듯
readOnly=true의 주요 이득은 메모리 절약이지 query latency 개선이 아님. 단순 ID-by-PK lookup 같은 single-entity read 에서는 hydrated state 가 1개이므로 메모리 이득이 미미. 따라서 ca-tmpl 의 no-tx bypass 가 허용되는 "단순 읽기" 정의에는 VM-READTX-C3 의 scope — 많은 entity 를 bulk 로드하는 연산은 readOnly=true 가 의미 있음. - 본 포스트는 Hibernate core committer 의 기술 분석이므로 engineering-blog 등급이나 Hibernate 내부 동작 설명의 신뢰도는 높음. 단 production case study 가 아니므로
company-case-study로 승급 불가.
Related / 관련
- raw/official-docs/spring-tx-management-reference — readOnly 속성의 공식 정의 (SPRING-TX-MGR-C6)
- raw/official-docs/spring-data-jpa-transactionality-spring-official — Spring Data CrudRepository 의 readOnly 기본 동작
- raw/official-docs/at-transactional-spring-official —
@Transactional전체 동작 - raw/branch-notes/feature-application-port-usecase-contract — TransactionPort.inRead 선행 계약