JPA
JPA 영속성과 캐시의 관계 – 캐시처럼 동작하는 영속성 컨텍스트
초코너무조코
2025. 4. 24. 18:49
728x90
JPA = ORM + 캐시?
JPA를 쓰다 보면 마치 자동으로 캐시를 써주는 것 같은 경험을 하게 됩니다. 같은 데이터를 여러 번 조회해도 SQL 쿼리가 한 번만 날아가고, 트랜잭션 내에서 바뀐 값이 자동으로 DB에 반영되기도 하죠. 이 모든 비밀은 영속성 컨텍스트에 있습니다.
1. 영속성 컨텍스트는 1차 캐시다
JPA에서 영속성 컨텍스트는 1차 캐시 역할을 합니다.
- 1차 캐시란?
- 트랜잭션 범위 내에서만 유효한 메모리 캐시입니다.
- 동일한 EntityManager에서 같은 엔티티를 여러 번 조회해도 DB에서 재조회하지 않고 캐시에서 반환합니다.
2. persist()와 캐시의 관계
📍 persist() 호출 시
- 엔티티는 영속성 컨텍스트에 저장됩니다.
- 이후 해당 엔티티는 1차 캐시에 존재합니다.
- 같은 엔티티를 다시 조회하면 DB로 가지 않고 캐시에서 조회합니다.
em.persist(member); // 영속성 컨텍스트에 저장 (캐시에 들어감)
Member findMember = em.find(Member.class, member.getId()); // 캐시에서 조회
3. persist() 없이 조회하면?
📍 persist() 안 하고 find() 하면?
- 영속성 컨텍스트에 없으면 DB에서 조회합니다.
- 그리고 조회된 엔티티는 자동으로 영속성 컨텍스트에 저장됩니다. 즉, 한 번 DB에서 가져온 후에는 캐시에 저장돼서 다음엔 DB 안 가요!
Member findMember1 = em.find(Member.class, 1L); // DB 조회 → 캐시에 저장
Member findMember2 = em.find(Member.class, 1L); // 캐시에서 조회
4. 정리: 캐시 동작 흐름
상황 | 동작 방식 |
persist() 후 조회 | 캐시에서 조회 |
persist() 없이 첫 조회 | DB에서 조회 후 캐시에 저장 |
동일 트랜잭션 내 같은 엔티티 재조회 | 캐시에서 조회 (DB 접근 안 함) |
다른 트랜잭션에서 조회 | 새로운 영속성 컨텍스트, DB 재조회 |
5. 왜 이게 중요한가?
- 성능 최적화: 트랜잭션 내에서 동일한 엔티티는 한 번만 DB에서 가져오면 됩니다.
- 데이터 일관성: 같은 트랜잭션 내에서 항상 같은 객체 인스턴스를 반환합니다.
- 자동 변경 감지: 캐시된 엔티티의 값을 바꾸면 커밋 시 자동으로 UPDATE 됩니다.
6. 실전 예제
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Member member1 = em.find(Member.class, 1L); // DB 조회
Member member2 = em.find(Member.class, 1L); // 캐시에서 조회
System.out.println(member1 == member2); // true (같은 객체)
tx.commit();
em.close();
6에 대한 의문점 -> 예제에는 persist()가 없는데?
🔑 핵심 포인트: find()도 자동으로 영속성 컨텍스트에 저장해!
📍 정리하자면
- persist()는 새로운 엔티티를 영속성 컨텍스트에 등록하는 역할이야.
- 즉, 비영속 → 영속 상태로 바꿔줌.
- 반면 find()는,
- 먼저 영속성 컨텍스트(1차 캐시) 를 확인하고,
- 없으면 DB에서 조회 후, 그 엔티티를 영속성 컨텍스트에 자동으로 등록해.
💡 즉, find() → "조회 + 캐시에 자동 등록"
// DB 조회 후 -> 영속성 컨텍스트에 저장
Member member1 = em.find(Member.class, 1L);
// 캐시에서 바로 조회
Member member2 = em.find(Member.class, 1L);
System.out.println(member1 == member2); // true (같은 인스턴스)
- 첫 번째 find()는 DB에서 가져오고, 그 엔티티는 자동으로 영속성 컨텍스트에 등록됨.
- 두 번째 find()는 이미 캐시에 있으니, DB 접근 없이 캐시에서 반환.
🎯 persist()와 find() 차이
메서드역할
persist() | 새 엔티티를 영속성 컨텍스트에 등록 (DB에는 아직 INSERT 안됨) |
find() | DB에서 엔티티 조회 → 자동으로 영속성 컨텍스트에 등록 |
의문점에 대한 최종 정리
- persist()가 없어도 find()는 영속성 컨텍스트에 자동 등록하니까,
- 트랜잭션 안에서 한 번 DB 조회 → 이후 캐시에서 조회 구조가 되는 거야.
🔥 결론
- JPA에서 영속성 컨텍스트는 1차 캐시로 동작한다.
- persist()하면 캐시에 저장되고, find()도 내부적으로 캐시 확인 후 DB 조회한다.
- 트랜잭션 내에서는 DB 접근 최소화, 객체 일관성 유지가 가능하다.
728x90