JPA는 영속성 컨텍스트 내부에서 1차 캐시를 통해 엔티티를 저장하고 생명주기를 관리한다. 그렇기 때문에 1차 캐시를 통해 데이터의 변경을 감지하고 조회할 때도 데이터베이스를 통해 가져올 필요없이 1차캐시 내부에서 가져와서 사용하게 됩니다.
이러한 장점은 어플리케이션 반복 가능해서 읽기가 가능하게 한다. 덕분에 같은 엔티티를 조회시 데이터 베이스를 통해 다시 가져오거나 컨텍스트에 등록할 필요가 없이 재사용할 수 있다.
동일성 테스트
@Service
@Transactional
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
public Long join(Member member) {
memberRepository.save(member);
return member.getId();
}
}
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
public void save(Member member) {
em.persist(member);
}
public Member findOne(Long id) {
return em.find(Member.class, id);
}
}
@SpringBootTest
@Transactional
@ExtendWith(SpringExtension.class)
public class equalsTest {
@Autowired
private MemberRepository memberRepository;
@Autowired
private MemberService memberService;
@Test
public void 회원가입() {
//given
Member member = new Member("리오");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberRepository.findOne(saveId);
assertTrue(findMember == member);
}
}
위와 같이 테스트를 진행했을 때 True라고 테스트가 성공합니다.
정리
여기서 알아둬야하는 점은 1차캐시에서 가져온 정보는 동일성, 동등성, 데이터 베이스 동등성 까지 전부 같다는 것이다.
- 동일성: == 비교가 같다. (객체의 주소값까지 정확하게 일치한다.)
- 동등성: equals 비교가 같다. (객체의 값이 정확하게 일치한다.)
- 데이터 베이스 동등성: @Id가 같다. (디비 아이디가 정확하게 일치한다.)
동일성 테스트(다른 트랜잭션)
@SpringBootTest
@ExtendWith(SpringExtension.class)
public class equalsTest {
@Autowired
private MemberRepository memberRepository;
@Autowired
private MemberService memberService;
@Test
public void 회원가입() {
//given
Member member = new Member("리오");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberRepository.findOne(saveId);
assertTrue(findMember == member);
}
}
위와 같이 테스트를 진행했을 때는 결과는 빨간 막대를 볼 수 있다.
- 회원가입을 요청시 해당 요청의 스레드가 할당되고 영속성 컨텍스트를 생성한다.
- 해당 트랜잭션 범위 동안 Member의 정보를 flush하고 회원가입 영속성 컨텍스트는 종료된다.
- 영속성 컨텍스트가 종료되었기 때문에 Member의 정보는 준영속 상태로 전환된다.
- findOne()을 동작하는 순간 영속성 컨텍스트를 생성한다.
- 새로 생성된 영속성 컨텍스트는 1차캐시에 Member의 정보가 없기 때문에 디비에 조회를 하게된다.
- 조회가 끝난후 다시 영속성 컨텍스트가 종료된다. 조회된 Member는 준영속 상태로 전환
- 서로 다른 영속성 컨텍스트에서 준영속 된 상태가 되었기 때문에 동일성이 같지 않다.(메모리 주소가 다름)
정리
서로 다른 영속성 컨텍스트에서 생성됐으니 서로 동일성은 다르지만 동등성, 데이터 베이스 동일성은 같다. 그렇지만 equals()를 그냥 사용하게 된다면 Object로 변환하여 비교하기 때문에 Override하여 유일한 값(id, 연락처 등)을 기준으로 비교하는 것을 좋다. 유일성만 보장하면되기 때문에 유연하게 변경하여 사용하면 가능하다.
영속성 컨텍스트 단점
영속성 컨텍스트에서는 같은 Entity라도 다른 컨텍스트에서 요청하게 되면 1차캐시에 존재하지 않기 때문에 데이터베이스에 다시 요청하는 작업을 하게 된다. 하나의 요청이라도 영속화를 시키기 위해 새로운 트랜잭션이 생길 때 마다 리소스의 낭비가 일어난다는 것이 단점이다.
- OSIV를 사용하여 요청이 끝날 때 까지 하나의 영속성 컨텍스트를 사용한다. (동일성 보장 O)
- 2차 캐시에 저장하여 어플리케이션단에 저장해두고 2차캐시에 있는 정보를 영속화 시킨다. (동일성은 보장 X)
'JAVA > JPA' 카테고리의 다른 글
프록시 심화 (0) | 2022.11.26 |
---|---|
JPA 예외처리 (0) | 2022.11.26 |
<JPA> 네이티브 SQL 사용 (0) | 2022.10.22 |
<JPA> Criteria의 사용 방법 (0) | 2022.10.22 |
복합키와 식별관계 (1) | 2022.09.24 |