영속성 컨텍스트란?
영속성 컨텍스를 쉽게 표현하자면 Entity를 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간입니다.
JPA에서는 테이블과 매핑되는 Entity 객체 정보를 영속성 컨텍스트를 통해 애플리케이션 내에서
오래 지속되도록 보관합니다.
영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요합니다.
영속성 컨텍스트 기능
- 1차 캐시
- 쓰기 지연 저장소
- 객체 동일성 보장
1차캐시
영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있는데 이를 1차 캐시라고 부릅니다.
캐시 저장소는 Map 자료구조 형태로 Key에는 @Id로 매핑한 기본키를,Value에는 Entity 객체를 저장합니다.
em.persist(memo); // 메서드 호출 시 memo Entity 객체를 캐시 저장소에 저장한다.
em.find(Memo.class,1) // 호출 시 캐시 저장소를 확인 한 후 해당 값이 없다면
// DB에 Select 조회 후 해당 값을 캐시 저장소에 저장하고 반환
1차 캐시 사용의 장점
1. DB 조회 횟수를 줄여준다.
@Test
@DisplayName("Entity 조회 : 캐시 저장소에 해당하는 Id가 존재하는 경우")
void test() {
try {
Memo memo1 = em.find(Memo.class, 1);
System.out.println("memo1 조회 후 캐시 저장소에 저장\n");
Memo memo2 = em.find(Memo.class, 1);
System.out.println("memo2.getId() = " + memo2.getId());
System.out.println("memo2.getUsername() = " + memo2.getUsername());
System.out.println("memo2.getContents() = " + memo2.getContents());
} catch (Exception ex) {
ex.printStackTrace();
} finally {
em.close();
}
emf.close();
}
예시로 먼저 find 메서드를 이용하여 id가 1인 메모객체를 조회 할 때에는 캐시 저장소에 값이 없어 DB로 select문을 날려 값을 가져오는 반면 다시 한번 id가 1인 메모객체를 조회 할때에는 따로 select문을 날리지 않고 캐시 저장소에서 값을 가져오는것을 확인할 수 있다.
2. 객체 동일성 보장
엔티티의 동일성을 보장한다.
@Test
@DisplayName("객체 동일성 보장")
void test() {
EntityTransaction et = em.getTransaction();
et.begin();
try {
Memo memo = new Memo();
memo.setId(2L);
memo.setUsername("Robbert");
memo.setContents("객체 동일성 보장");
em.persist(memo);
Memo memo1 = em.find(Memo.class, 1);
Memo memo2 = em.find(Memo.class, 1);
Memo memo3 = em.find(Memo.class, 2);
System.out.println("memo1 == memo2 = " + (memo1 == memo2));
System.out.println("memo1 == memo3 = " + (memo1 == memo3));
et.commit();
} catch (Exception ex) {
ex.printStackTrace();
et.rollback();
} finally {
em.close();
}
emf.close();
}
아래 사진과 같이 같은 값을 조회하는 memo1과 memo2 는 true를 반환하고,
서로 다른 값을 조회하는 memo1과 memo3은 false를 반환하는 것을 확인할 수 있습니다.
쓰기 지연 저장소
Entity의 값을 변경하면 DB에 바로 업데이트 하는 것이 아니라 쿼리 저장소에 쿼리문들을 모아둡니다.이후 EntityManger에 flush()나 트랜잭션의 커밋을 통해 한번에 보내지게 됩니다.
변경감지
JPA에서 Update처리는 변경감지(Dirty Checking)를 통하여 수행합니다.트랜잭션이 커밋되고 em.flush()가 호출되면 Entity의 현재상태와 초기 상태를 비교하여 변경 내용이 있다면update쿼리를 생성하여 쿼리 저장소에 저장합니다. 그 후 DB의 트랜잭션이 커밋되면서 변경된 값이 반영되게됩니다.이러한 과정을 더티체킹(Dirty Checking)이라고 합니다.
@Transactional
public void modify(Long id, String userName, HttpServletRequest request) {
// findById를 이용하여 데이트를 조회 후
User user = isValidUser(id,request);
// 값을 변경해주면 트랜잭션이 끝나는 시점에 변경된 값을 반영해준다.
user.modify(userName);
}
private User isValidUser(Long id, HttpServletRequest request) {
User user = userRepository.findById(id).orElseThrow(()-> new CustomException(ErrorCode.NOT_USER_ID));
User reqUser = (User)request.getAttribute("user");
if(!user.getId().equals(reqUser.getId())) throw new CustomException(ErrorCode.DIFFERENT_USER);
return user;
}
Entity 상태
비영속
객체를 생성했지만 영속성 컨텍스트에 저장되지 않은 상태를 비영속 상태라고 한다.
Memo memo = new Memo(); // 비영속 상태
영속
persist :비영속 Entity를 EntityManager를 통해 영속성 컨텍스트에 저장하여 관리되고 있는 상태로 만든다.
em.persist(memo);
준영속
영속성 컨텍스트에 저장되어 관리되다가 분리된 상태를 의미
em.detach(memo); // 특정 entity를 준영속 상태로만든다.
em.clear(); // 영속성 컨텍스트를 완전히 초기화한다.
// 영속성 컨텍스트의 모든 Entity를 준영속 상태로 전환
em.close(); // 영속성 컨텍스트를 종료한다.
// 해당 영속성 컨텍스트가 관리하던 영속성 상태의 entity들은 모두
// 준영속 상태로 변경된다.
준영속 -> 영속
em.merge(memo); // 전달받은 Entity를 사용하여 새로운 영속상태의 Entity를 반환
삭제
em.remove(memo); // 영속성 상태의 Entity를 파라미터로 전달받아 삭제상태로 전환
'TIL' 카테고리의 다른 글
JPA - 회원가입 로그인 (0) | 2024.10.17 |
---|---|
JPA - JWT와 필터 구현하기 (1) | 2024.10.16 |
페이징 조회 - Pageble / PageRequest (0) | 2024.10.15 |
JPA - Entity (1) | 2024.10.15 |
JPA - 댓글 CRUD (2) | 2024.10.14 |