스프링 영속성 컨텍스트

HeavyJ·2023년 3월 25일
0

스프링 이론 정리

목록 보기
1/5

영속성 컨텍스트

  • 영속성 컨텍스트는 엔티티를 메모리에 영구 저장하는 환경입니다.
  • 응용 프로그램과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 역할을 합니다.
  • 서비스별로 하나의 EntityManagerFactory가 존재하며 EntityManagerFactory에서 트랜잭션이 생길 때 마다 Thread 별로 Entity Manager를 생성하여 영속성 컨텍스트에 접근합니다.
  • 엔티티 매니저를 통해 영속성 컨텍스트에 엔티티를 보관하고 관리합니다.

즉, 영속성 컨텍스트는 EntityManager를 생성할 때 만들어지며 EntityManager를 통해 영속성 컨텍스트에 접근하고 관리합니다.

엔티티 매니저

  • EntityManager는 영속성 컨텍스트 내에서 Entity들을 관리합니다.
  • EntityManager는 JPA에서 제공하는 interface로 spring bean으로 등록되어 있기 때문에 @Autowired로 사용할 수 있습니다.
@Autowired
private EntityManager entityManager;
  • Query Method나 Simple JPA Repository는 직접적으로 entityManager를 사용하지 않도록 한 번 더 감싸준 것입니다.

Query Method

// Query Method

// 1. Repository 인터페이스에 간단한 네이밍 룰을 이용하여 메소드를 작성하면 메소드 이름을 분석하여 JPQL 쿼리를 실행하는 방법

// ex) findById(), findByName()

interface PersonRepository extends Repository<Person, Long> {
  List<Person> findByLastname(String lastname);
}

// 2. NamedQuery
// @Entity 클래스에 @NamedQuery 애너테이션을 추가하여 사용

// Member.class
import javax.persistence.*;

@Entity
@NamedQuery(
        name = "Member.findByUsername",
        query = "select m from Member where m.username = :username"
)
public class Member {
    ...
}

// MemberRepository.interface
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsername(@Param("username") String username);
} 

// 3. Query 어노테이션 사용
// @Query 어노테이션을 추가한 뒤 JPQL을 작성한 방식
// 조건이 많아질 경우 메서드 이름만으로 개발하기에 한계 발생할 수 있으므로 실무에서 많이 사용
public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query("select m from Member m where m.username= :username and m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);
}
  • EntityManage는 Entity Cache를 가지고 있습니다.

1차 Cache

  • DB Save와 같은 DB에 접근하여 DB를 변경하는 메서드를 실행할 경우 바로 DB가 업데이트 되지 않고 영속성 컨텍스트 내부에 있는 캐시를 거쳐서 DB가 업데이트 됩니다. 이 캐시가 1차 캐시입니다.
  • 1차 캐시에는 영속 상태의 엔티티를 저장합니다.
  • 1차 캐시는 Map 형태로 저장됩니다.
  • Map의 key는 id, value는 해당 entity 값이 들어있습니다.
  • key 값이 id라서 id로 조회를 하게 되면 영속성 컨텍스트에 있는 1차 캐시에 엔티티가 있는지 확인을 해보고 값이 있다면 캐시에서 바로 return 해줍니다. 만약 값이 없으면 쿼리문으로 DB에서 조회를 하고 1차 cache에 저장 후 return 해줍니다.
    - 특징으로 id가 아닌 다른 값을 이용해서 조회를 하면 1차 캐시가 적용되지 않습니다.
  • 결론적으로 하나의 트랜잭션에서 id 값으로 조회하는 데이터들은 1차 캐시에 저장을 하여 관리를 함으로써 JPA의 조회 성능이 올라갑니다.

동일성 보장

  • 영속성 컨텐스트는 영속 엔티티의 동일성을 보장합니다.
  • 1차 캐시로 반복 가능한 읽기 등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공합니다.
User findUser1 = entityManager.find("User.class", "1L");
User findUser2 = entityManager.find("User.class", "1L");
System.out.print(findUser1 == findUser2) // Result: true

변경 감지(Dirty Checking)

  • 엔티티의 수정이 일어나도 영속성 컨텍스트가 알아서 변경 사항을 체크해줍니다
  • 1차 캐시에 엔티티를 저장할 때 스냅샷 필드도 따로 저장하여 commit 혹은 flush를 할 때 해당 엔티티와 스냅샷을 비교하여 변경사항이 있으면 알아서 Update Sql을 만들어서 DB에 전송합니다.

Dirty Checking의 흐름
1. 트랜잭션을 커밋하면 entityManager의 내부에서 먼저 플러시가 호출됩니다.
2. 엔티티와 스냅샷을 비교하여 변경된 엔티티를 찾습니다.
3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 저장합니다. -> sql 쿼리를 한 번에 보내기 위해 쓰기 지연 저장소를 사용
4. 쓰기 지연 저장소의 SQL을 플러시합니다.
5. 데이터베이스 트랜잭션을 커밋합니다.

Flush와 Commit 차이

  • Flush는 쿼리를 DB에 전송하는 역할
  • Commit은 Flush를 수행한 뒤 트랜잭션을 끝내는 역할
    Flush로 전송된 쿼리는 Rollback이 가능하지만, Commit은 트랜잭션이 끝나기 때문에 Rollback이 불가능합니다.

정리

  1. Entity Manager가 영속성 컨텍스트를 관리합니다.
  2. 영속성 컨텍스트의 특징인 1차 캐시는 조회 성능을 올려주는 역할을 합니다.
  3. 영속성 컨텍스트는 영속된 인스턴스의 동일성을 보장해줍니다.
  4. 영속성 컨텍스트는 변경 감지 기능을 사용하여 알아서 update 쿼리를 만들어 줍니다.
profile
There are no two words in the English language more harmful than “good job”.

2개의 댓글

comment-user-thumbnail
2023년 3월 27일

드디어 영속성컨텍스트에 대해 학습을 시작하셨군요 !

영속성컨텍스트를 잘 활용하면 최적화 및 성능 개선에 큰 도움이 되실듯합니다

1개의 답글