더티체킹을 적용하려고 개념을 되짚어 보다가 다시 정리해보려고 한다 !
더티체킹이란 ? )
JPA에서 영속성 컨텍스트가 관리 중인 엔티티 객체의 변경을 감지하고 자동으로 DB에 반영해주는 기능이다.
트랜잭션이 끝나기 전에 엔티티의 값이 바뀌었는지 감지하고, 바뀐 경우에만 UPDATE 쿼리를 자동으로 날린다.
나는 이 기능을 주로 정보 수정 API에 유용하게 사용하고 있었다.
더티체킹이 없다면 회원 정보 중에서 이름을 수정한다고 했을 때
@Transcational
public void updatedMember() {
Member member = memberRepository.findById(1L);
member.setName("newName");
memberRepository.save(member);
}
이렇게 바뀐 항목에 대해 다시 save를 통해 변경된 엔티티를 다시 저장해줘야 한다.
반면 더티체킹을 사용한다면 save() 없이도 JPA가 알아서 변경된 내용을 감지해 UPDATE 쿼리를 자동으로 날린다.
@Transcational
public void updatedMember() {
Member member = memberRepository.findById(1L); // 1. 영속상태
member.setName("newName"); // 2. 값 변경
}// 3. 트랜잭션 종료 시 -> JPA가 변경 감지 -> UPDATE 쿼리 실행
더티체킹의 조건
1. @Transactional이 필수적 :
트랜잭션 범위내에서 영속성 컨텍스트가 살아있어야 한다. (그래야만 트랜잭션이 커밋될 때 JPA가 변경사항을 감지해 반영)
2. 엔티티가 영속 상태여야 한다.
3. set등으로 값이 변경되어야 한다.
단순 조회로는 동작 X
그럼 여기서 자주 듣는 영속성 컨텍스트가 또 머냐 ,.... 하면
영속성 컨텍스트란 ? )
JPA에서 엔티티를 관리하는 일종의 1차캐시.
자바 객체(Entity)를 DB에 매핑하면 JPA가 그 객체를 관리해야하는데 그 객체가 관리되는 공간이 영속성 컨텍스트 !
이 저장소에 들어가야 == 영속성 컨텍스트에 올라가야 JPA가 변경감지, 자동 업데이트, 쿼리 생성을 해줄 수 있다.
영속성 컨텍스트의 특징
1. 1차 캐시 : 같은 ID로 엔티티를 두 번 조회하면 DB가 아닌 메모리에서 가져온다.
2. 동일성 보장 : 같은 트랜잭션 안에서는fidnById(1L) 두 번 해도 동일 객체 반환
3. Dirty Checking : 값만 바꿔도 자동으로 변경을 감지해서DB에 업데이트
4. 지연 로딩(Lazy Loading) : 연관된 엔티티는 필요할 때만 쿼리로 불러옴
5. 쓰기 지연(Write-behind) : insert/update 쿼리를 모아서 트랜잭션 종료시 한 번에 날림(flush)
엔티티의 생명주기(상태)
1. 비영속 (Transient) :
- DB와 아무 관련 없는 단순 자바 객체
- 아직 영속성 컨텍스트에 저장되지 않은 상태
- new 키워드를 통해 객체를 생성하면 이 상태임.
- 영속성 컨텍스트가 관리하지 않고 DB에 저장되지 않음.
2. 영속 (managed) :
- 영속성 컨텍스트에 의해 관리되는 상태
- EntityManager.persist() 또는 reposioty.save()하면 영속 상태가 됨.
3. 준영속 (detached) :
- 영속성 컨텍스트에서 관리가 중단된 상태
- 영속 상태에서 EntityManager.detach(), em.clear(), 트랜잭션 종료로 관리가 끊어지면 준영속.
- 더이상 변경 감지가 이뤄지지 않음. 수동으로 다시 영속화 해야 변경사항 반영 가능.
4. 삭제 (removed) :
- 영속성 컨텍스트에서 삭제가 예약된 상태
- em.remove() 또는 Repository.delete() 호출 시
- 트랜잭션이 커밋되면 DB에서 해당 데이터가 삭제됨.
아하 ! 그러면 JPA는 영속성 컨텍스트를 통해 엔티티를 관리해주고 있었던 거고
엔티티가 영속상태에 있어야 DirtyChecking등의 여러 동작들이 가능해지는구나 ~
memberRepository.findByXx 머시기로 조회해서 오면 find()가 호출되어 영속상태가 되는거라고 한다.
이제 개념은 알겠다 !
근데 직접적으로 영속성 컨텍스트를 다루지 않고 JPA 내부에서 자동으로 상호작용되니 어느 상황에 영속 or 준영속 상태가 되는지 와닿지가 않는 느낌이었다.
따라서 프로젝트에서 사용하는 코드 기준으로 정리해보려고 한다 !
영속상태가 되는 경우 -> save()나 fidnByXxx() 같은 조회 메소드를 사용할 때 !
1. memberRepository.save(entity)
Member member = new Member();
member.setName("버니");
memberRepository.save(member); //INSERT
신규엔티티면 persist()가 내부에서 호출됨.
영속성 컨텍스트에 등록되며 영속 상태가 됨.
Member member = memberRepository.fidnById(1L).get();
member.setName("번번이");
memberRepository.save(member)//Dirty Checking으로 자동 반영. (save 호출 안 해도 반영됨!)
기존 엔티티면 이미 영속 상태.
값 변경만 하면 Dirty checking에 의해 자동 업데이트
2. findById()등으로 조회할 때
Member member = memberRepository.findById(1L).get();
JPA가 내부적으로 EntityManager.find()를 사용
DB에서 조회되면 영속성 컨텍스트에 등록되어 영속상태로 관리됨.
이제 어느정도 이해가 된다 ! JPA가 엔티티를 관리하려고 영속성컨텍스트라는 일종의 1차 캐시공간을 사용하는데
프로젝트에서는 직접적으로 EntityManger를 호출해서 쓰지 않지만
레포지토리에 save()나 find()를 하면 해당 엔티티가 영속상태가 된다는 것 !
그런데 이게 @Transactional이랑은 무슨 관계이지 ?!?!?! 라는 의문이 또 들었다.
@Transcational은 영속성 컨텍스트의 생명 주기를 결정한다.
== 영속성 컨텍스트는 트랜잭션 안에서만 동작한다 !
트랜잭션이 시작되면 영속성 컨텍스트가 생기고, 트랜잭션이 끝나면 영속성 컨텍스트도 종료(=flush + detach) 된다 !
@Transactional 이 없으면 변경사항은 감지돼도 반영할 타이밍이 없기 때문이 무시된다.
트랜잭션 커밋 시점에 flush()가 일어나고 실제 SQL이 실행된다.
다시 더티체킹으로 돌아가서... ~
더티체킹을 위해서는 ?
1. @Transactional이 필수적.
Y?) JPA가 변경감지를 하려면 엔티티가 영속상태가 되어야 한다. 영속성 컨텍스트는 트랜잭션 안에서만 동작하므로!
2. 엔티티가 영속상태여야 함
Y?) 당연함. 결국은 다 똑같은 얘기를 다르게 표현하고있던 것 !
궁금했던 것들
Q. 단순 조회로도 엔티티가 영속 상태가 되는지
-> ㅇㅇ save()나 findBy() 쿼리 시 영속상태가됨.
Q. 영속성 컨텍스트에서 관리되지 않는 엔티티는 변경사항이 반영되지 않음?
-> ㅇㅇ 당연 JPA가 관리해주지 않기 때문에. 변경사항을 감지하도록 하려면 다시 영속화 하거나 새로운 엔티티 만들어 저장해야함.
+ Spring data JPA에서 조회 후 영속 상태를 벗어나는 경우
1. 조회된 엔티티가 영속성 컨텍스트의 범위를 벗어남 == 트랜잭션 종료일 때
2. Lazy Loading 필드 접근 시 준영속 문제 발생.
'공부 > Spring Boot' 카테고리의 다른 글
SpringBoot) 비동기 이벤트 기반으로 Elasticsearch와 RDB간 정합성 유지를 위한 노력 (0) | 2025.04.19 |
---|---|
SpringBoot 프로젝트에서 SSE를 활용한 알림 기능 개발 (0) | 2025.04.16 |
SpringBoot에 Flyway적용해서 DB 형상 관리 하기 + JPA 옵션, flyway 옵션 정리 (0) | 2024.08.26 |
Spring Boot) 백 & 프론트 카카오 로그인 API 구현하기 (4) | 2024.08.24 |
Spring Security + JWT 인증 인가 구현하기(로그인 API, 인증이 필요한 API) (0) | 2024.08.16 |