작년 이직 준비 시절 .. 과제 면접을 실무에서 해보지 않은 JPA를 사용했었다
어찌저찌 과제 전형 통과하고 면접을 봤는데 'readOnly = true 사용하신 이유가 무엇인가요?'라는 질문을 받고 아무 말도 못 해버림 .. 면접 다녀와서 간략하게 메모해둔 게 다라 확실하게 이해하기 위해 이제라도 정리해본다 ... ✏️ 💭
@Transactional(readOnly = true)true인 경우 INSERT, UPDATE, DELETE 실행 시 예외 발생 INSERT), 수정(UPDATE), 삭제(DELETE)를 예방하고 성능을 최적화 할 수 있다. falseINSERT, 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로 전송 -> flushTransaction 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 DB의 장애 발생 시 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 해결하기