[Spring/JPA] 영속성 관리

DoooongDong·2023년 1월 1일
0

JPA 기본편

목록 보기
1/7
post-thumbnail

🛠️ 영속성 컨텍스트(Persistence Context)

  • "엔티티를 영구 저장하는 환경" 이라는 뜻을 가집니다.

  • Entity Manager를 통해서 접근 가능합니다.

  • 영속성 컨텍스트에 관리되는 엔티티의 상태를 영속 상태라고 합니다.

  • 예) EntityManager.persist(entity);

1. Entity Manager Factory

Entity Manager Factory는 생성되는 시점에 DB 커넥션 풀을 생성 한 뒤, 요청이 들어올 때 마다 Entity Manager를 생성합니다.

Entity Manager는 DB 연결이 필요할 때 커넥션 풀에 있는 connection을 얻습니다.

🔧 Entity Manager Factory는 왜 Entity Manager를 사용할까?

생성 비용 때문입니다. Entity Manager Factory는 생성할 때 DB 커넥션 풀을 생성하기 때문에 생성 비용이 큽니다. 반면, Entity Manager생성 비용이 거의 들지 않기 때문에 Entity Manager FactoryEntity Manager를 사용합니다.

Entity Manager FactoryThread Safe 합니다.
즉, 여러 쓰레드가 접근하더라도 안전합니다.

반면에, Entity Manager는 여러 쓰레드가 동시에 접근할 경우 동시성 문제가 발생하기 때문에 절대로 공유하면 안됩니다.

🛠️ 엔티티의 생명주기

1. 비영속

  • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태입니다.
//객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

2. 영속

  • 영속성 컨텍스트에 관리되는 상태입니다.
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

//객체를 저장한 상태(영속)
em.persist(member);

3. 준영속

  • 영속성 컨텍스트에 저장되었다가 분리된 상태입니다.
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);

4. 삭제

  • 삭제된 상태입니다.
//객체를 삭제한 상태(삭제)
em.remove(member);

🛠️ 영속성 컨텍스트가 제공하는 기능

1. 1차 캐시

  • 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있습니다.

🔧 1차 캐시에서 조회

Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

//1차 캐시에 저장됨
em.persist(member);

//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");

Member findMember2 = em.find(Member.class, "member2");

조회 순서

  • "member1" 조회 요청 시 JPA는 영속성 컨텍스트의 1차 캐시에서 해당 PK 값을 가진 Member 객체를 찾습니다.
  • 1차 캐시에 존재하면 그 값을 반환하고, 존재하지 않으면 DB를 조회하여 값을 가져옵니다.
  • DB를 조회하여 값을 가져온 경우, 1차 캐시에 값을 저장합니다.
  • 그 이후에 "member1" 조회가 들어오면 DB를 거치지 않고 1차 캐시에서 값을 가져옵니다.

2. 동일성(identity) 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b); //동일성 비교 true
  • 1차 캐시로 반복 가능한 읽기 (Repeatable Read) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공합니다.
Member a = em.find(Member.class, "member1");

em.clear(); //영속성 컨텍스트를 비워줌

Member b = em.find(Member.class, "member1");

System.out.println(a == b); //동일성 비교 false
  • member1을 find를 통해 DB에서 가져와서 1차 캐시에 등록합니다.
    영속성 컨텍스트를 비우게 되면 b를 find를 통해 조회할 때 1차 캐시 또한 비워져있기 때문에 DB에서 다시 조회합니다.
    둘의 참조 주소가 다르기에 동일성 비교에서 false가 나오게됩니다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

em.clear(); //영속성 컨텍스트를 비워줌

System.out.println(a == b); //동일성 비교 true
  • 이 경우에도 영속성 컨텍스트를 비워주었지만 동일성 비교는 true 입니다. 왜냐하면 find를 하는 시점에서 같은 참조주소를 가진 뒤에 영속성 컨텍스트를 비워줬기 때문입니다.

3. 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 합니다.
transaction.begin(); // [트랜잭션] 시작

em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않습니다.

//커밋하는 순간 데이터베이스에 INSERT SQL을 보냅니다.
trasaction.commit(); // [트랜잭션] 커밋

  • 영속성 컨텍스트에는 1차 캐시 뿐만 아니라 쓰기 지연 저장소가 존재합니다.

  • em.persist(memberA), em.persist(memberB) 를 하면 INSERT SQL이 생성되어 쓰기 지연 저장소에 저장됩니다.

  • 변경이 일어나면 SQL을 생성하여 쓰기 지연 저장소에 저장했다가 트랜잭션이 커밋되는 순간 한번에 DB에 전송합니다.

4. 변경 감지 (Dirty Checking)

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작

//영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");

//영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);

//em.update(member) 이런 코드가 있어야 하지 않을까?

transaction.commit(); // [트랜잭션] 커밋]
  • JPA에서는 별도의 update 코드가 필요없습니다.
  • JPA는 1차 캐시에 들어있는 엔티티가 변경되면, 그 변경된 내용을 감지하여서 트랜잭션 커밋 시점에 변경된 내용을 DB에 반영합니다.
  • 이러한 방식을 변경 감지라고 합니다.

🔧 변경 감지의 원리

  • 1차 캐시에는 @Id, Entity 그리고 "스냅샷" 이 존재합니다.
  1. 엔티티가 1차 캐시에 저장될 때, 저장되는 시점의 상태를 스냅샷으로 만들어서 1차 캐시에 보관합니다.

  2. 트랜잭션 커밋 시점엔티티와 스냅샷을 비교합니다.

  3. 엔티티가 스냅샷과 차이가 있을 경우 JPA는 이를 감지하여 DB에 반영합니다.



[Reference]

자바 ORM 표준 JPA 프로그래밍 - 기본편 링크

https://ttl-blog.tistory.com/108

https://velog.io/@neptunes032/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80

profile
꺾이지 말자 :)

0개의 댓글