영속성 컨텍스트
: 메모리 저장소, 엔티티 타입과 식별자를 키로 사용하는 캐시
- Entity Manger로 DB와 영속성 컨텍스트 사이에서 데이터 관리
Q1. 왜 두 entity가 다를까?
- 상황: 하나씩 테스트를 했을 때는 문제가 없었지만, 통합테스트를 통과하지 못했음
영속성 컨텍스트 관리 상태
-
비영속(New)
: 영속성 컨텍스트로 관리가 안 됨
-
영속(Managed)
: 영속성 컨텍스트에게 관리됨
- DB와 동기화되고 트랜잭션 커밋시에 변경 사항이 지속
-
삭제(removed)
: 영속성 컨텍스트와의 연결이 끊기고,변경 사항 지속X
-
준영속(Detached)
: 예전에는 영속성 컨텍스트로 관리 되었지만 지금은 아님
- 유효한 entity지만 트랜잭션이 커밋될때 변경사항 지속되지 않음
save() 분기처리
준영속 entity와 영속으로 바뀐 entity는 다르다.
- 다시 영속이 될 때, id가 바뀌기 때문에 다른 entity로 인식
결론
- savaAndCompare() 때에는 비영속성-> 영속성
- id: null 이기 때문에 바로 DB에 저장이 됨
- DB에 저장된 entity와 PEPPER은 같음
- find() 때에는 준영속성 -> 영속성
- 앞의 테스트에서 저장되었기 때문에 id: 1
- 준영속성 -> 영속성이 되면서 id가 변경
- id가 달라졌기 때문에 PEPPER과 다른 entity로 인식함
Q2.삭제를 했는데 왜 조회가 되나?
- 상황: 삭제 -> 조회를 했는데 테스트가 통과함.
- 이상한 점: select 쿼리를 JPA가 날리지 않음
원인
- 삭제는 했지만, 영속성 컨텍스트 안에 남아있기 때문에
추가
- findById()를 통해서 entity를 가져와 달라고 하는 건 실패함
- DB에 없기 때문에 가져올 수는 없음.
Q3. 삭제를 했는데 왜 삽입이 안 되나?
- 실패 이유: unique에 위반되기 때문에 실행 불가
1차 캐시와 쓰기 지연
- id가 없는 경우(New, 비영속성)
- DB에 먼저 저장후 id를 넣어줌
- 그후 1차 캐시에 저장
- id가 있는 경우
- 1차 캐시에 먼저 저장
- 쓰기 지연 SQL 저장소
- 한번에 flush()를 통해서 DB에 반영
delete All
- 먼저 findAll을 통해서 entity를 가져옴
- 그 후에 for문을 통해서 delete를 실행
flush() 실행 조건
- 강제로 flush() 실행
- JPQL 쿼리를 실행
- 트랜잭션이 커밋
문제의 원인
- delete 문이 아직 쓰기 지연 저장소에 있음
- saveAll()의 entity들은 id가 없음
- 그래서 바로 DB에 저장을 시키려고함
즉, DB에 있는 entity를 다시 저장하는 꼴
해결법
- flush() 사용
- delete를 JPQL로 실행
- transact commit을 함
Q4. 수정을 했는데 왜 그대로지?
- 상황: save()를 했는데도 값이 변경되지 않았음
스냅샷
- 엔티티가 변경이 되었는지 비교를 위한 대조군으로 스냅샷 이용
언제나 JPQL은 flush를 날릴까?
- 아니다.
- JPQL이 특정 컬럼에 대한 것이라면, 그 컬럼에 대한 flush만 날림
결론
- JPQL이 적용되지 않는 이유
- age와 관련된 쿼리가 기존에 없어서 flush되는 것은 없음
- 또한 JPQL이 실행된다고 하더라도 자동으로 JPQL의 변경 내용이 flush 되는 건 아님.
해결법
- repository에 @Modifying(clearAutomatically = true) 사용
Q4. team이 왜 null인가?
- 상황: 팀을 넣어준 상태인데 sort가 되지 않는 이유는?
fetch = FetchType.LAZY
- Proxy 객체: 가짜 객체, 실제 entity를 꺼낼때 채우겠다는 의미
해결방법
- compare을 필드를 직접 가져오지 않고 getTitle로 가져옴