작년 이직 준비 시절 .. 과제 면접을 실무에서 해보지 않은 JPA를 사용했었다
어찌저찌 과제 전형 통과하고 면접을 봤는데 'readOnly = true 사용하신 이유가 무엇인가요?'라는 질문을 받고 아무 말도 못 해버림 .. 면접 다녀와서 간략하게 메모해둔 게 다라 확실하게 이해하기 위해 이제라도 정리해본다 ... ✏️ 💭
@Transactional(readOnly = true)
true
인 경우 INSERT
, UPDATE
, DELETE
실행 시 예외 발생 INSERT
), 수정(UPDATE
), 삭제(DELETE
)를 예방하고 성능을 최적화 할 수 있다. false
INSERT
, UPDATE
, DELETE
실행 시 예외 발생하지 않는다.readOnly = true
설정에 따른 성능 향상Entity
조회 시 초기 상태에 대한 Snapshot
을 저장한다.Commit
될 때, 초기 상태의 정보를 가지는 Snapshot
과 Entity
의 상태를 비교하여 변경된 내용에 대해 update query
를 생성해 쓰기 지연 저장소에 저장한다.SQL query
를 flush()
하고 데이터베이스의 트랜잭션을 commit
함으로써 우리가 update와
같은 메서드를 사용하지 않고도 Entity
의 수정이 이루어진다. readOnly = true
를 설정하게 되면 스프링 프레임워크는 JPA
의 세션 플러시 모드를 MANUAL
로 설정한다.readOnly = true
를 설정하게 되면 JPA
는 해당 트랜잭션 내에서 조회하는 Entity
는 조회용임을 인식하고 변경 감지를 위한 Snapshot
을 따로 보관하지 않으므로 메모리가 절약된다.더티 체킹에 대한 자세한 기록은 ->[JPA] 더티 체킹(dirty checking)
Flush
)
flush()
는 어떤 작업들을 수행하는 것이고,flush
는 그 수행 작업들 중 하나다. 💭
Transaction Commit
시 내부적으로 flush()
호출SQL
생성SQL
을 쓰기 지연 SQL
저장소에 등록SQL
저장소에 등록된 쿼리를 DB
로 전송 -> flush
Transaction Commit
시 flush
가 자동 호출 되는 이유flush()
는 우리가 DB Editor
에서 SQL
문을 작성하는 것이라고 할 수 있으며, 트랜잭션 커밋은 말 그대로 commit
을 수행하는 것이다.SQL
문을 작성하지 않고 commit
을 수행한다면 당연히 어떤 일도 일어나지 않기 때문에, JPA
는 이런 문제를 예방하기 위해 트랜젝션을 커밋할 때 플러시를 자동으로 호출한다.MANUAL
모드트랜잭션 내에서 사용자가 수동으로 flush()
를 호출하지 않으면 flush()
가 자동으로 수행되지 않는 모드
트랜잭션 내에서 강제로 flush()
를 호출하지 않는 한, 수정 내역에 대해 DB
에 적용되지 않는다.
트랜잭션 commit
시 영속성 컨텍스트가 자동으로 flush()
되지 않으므로 조회용으로 가져온 Entity
의 예상치 못한 수정을 방지할 수 있다.
readOnly = true
설정에 따른 가독성 향상@Transactional(readOnly = true)
fun findAccountLog(memberDetailsVO: MemberDetailsVO): List<AccountLogFindResponse> {
/* ... */
}
⛔️ 모든 조회용 메서드에
@Transactional(readOnly = true)
를 설정하는 등 무분별한 사용은 스냅샷 유지,flush()
의 필요 등 관리적 / 메모리적 측면에서 오히려 좋지 않을 수 있고, 커넥션을 오래 가지고 있어 커넥션 부족 등의 문제가 발생할 수 있기 때문에. ⛔️
readOnly = true
설정으로 LazyInitializationException 해결LazyInitializationException
발생 원인JPA
를 이용하여 객체의 양방향 연관관계를 구성한 후에 엔티티를 조회했을 때 LazyInitializationException
예외가 발생할 수 있다.FetchType.LAZY
로 연관관계가 설정된 필드는 지연 로딩으로 조회 되는데, 이때 해당 필드의 엔티티를 프록시 객체로 조회하게 된다.LazyInitializationException
예외가 발생한다.🔎 프록시 객체
: 지연 로딩을 사용하여 엔티티를 조회했을 때, 실제 엔티티 객체 대신에Mock Object
역할을 하는 가짜 객체
📖 지연 로딩 (
LAZY LOADING
)
@ManyToOne(fetch = FetchType.LAZY)
설정하여 사용.- 실제 데이터가 필요한 시점에 데이터베이스를 조회해서 프록시 객체를 초기화한다.
@OneToMany
,@ManyToMany
와 같이 연관된 엔티티가 여러 개인 경우에 대한 디폴트 패치 전략
LazyInitializationException
해결 방법LazyInitializationException
예외는 JPA
의 영속성 컨텍스트가 종료된 후에 연관관계가 설정된 엔티티를 조회하려고 할 때 발생하기 때문에, 세션이 유지되도록 트랜잭션을 설정해준다.readOnly = true
설정에 따른 Replication
부하 분산Replication
) 방식DBMS
시스템을 Mater / Slave
로 나눠서 동일한 데이터를 저장하는 방식Master D
B의 장애 발생 시 Slave DB
를 Master DB
로 승격시켜 장애를 빠르게 복구할 수 있다.Slave DB
에서 수행하고, 수정 작업은 Master DB
에서 수행함으로써 트래픽을 분산할 수 있다.readOnly = true
가 설정되어있는 메서드의 경우 Slave DB
에서 데이터를 가져오도록 동작한다. @Transactional(readOnly = true)를 왜 붙여야 하나요
[JPA] commit, flush, Entity Manager의 clear()와 close()에서 궁금한 부분들 탐구 + 데이터 삭제 및 수정 시 1차 캐시에서 발생하는 현상 + 준영속과 비영속의 차이점
JPA에서 플러시(flush) 개념 및 호출 방법 3가지
Database의 리플리케이션(Replication)이란?
JPA LazyInitializationException 해결하기