해당 수업은 김영한님의 JPA 수업을 통해 작성되었으며 약간의 저의 생각이나 추가적으로 제가 궁금했던 내용을 더 적었다는 사실을 밝힙니다.
💡 영속성 컨텍스트
- "엔티티를 영구 저장하는 환경"이라는 뜻으로 JPA를 이해하는데 가장 중요한 용어이다.
- 우리가 알고있는 persist메소드는 DB에 저장하는 것이 아닌 Entity 영속성 컨텍스트라는 곳에 저장을 하는 것이다.
- 영속성 컨텍스트는 논리적인 개념으로 눈에 보이지 않는다는 것이다. 또한 EntityManager로 영속성 컨텍스트에 접촉하는 것이다.
엔티티의 생명 주기
- 비영속(new / transient)
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 객체를 생성한 경우
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
- 영속(managed)
- 영속성 컨텍스트에 관리되는 상태
- persist메소드를 사용한다면 해당 상태로 넘어온다
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 여기가 영속된 상태
em.persist(member);
- 준영속(detached)
- 영속성 컨텍스트에 저장되었다가 분리된 상태
- 이는 객체가 넘어가기 전에 없앤다는 뜻이다
em.detach(member);
- 삭제(removed)
- 삭제된 상태
- 영속성 컨텍스트에 존재하는 내용을 지운다는 뜻이다.
em.remove(member);
💡 영속성 컨텍스트의 이점
1. 엔티티 조회, 1차 캐시
우리가 위에서 본 회원을 저장하는 과정에서 persist를 통해 컨텍스트에 제작된 회원 정보를 1차로 넣게된다. 여기서 저장이 어떻게 되는지를 봐야한다.
이때 entityManager에 1차적으로 저장을 해놓는 개념으로 변하는데 이것은 나중에 자체적인 조회가 가능한 캐시 기능이 될 수 있다. DB에 바로 접촉하지 않고 한다는 것이 아주 큰 장점이 될 수 있는 것이다.
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 1차 캐시에 저장
em.persist(member);
// 1차 캐시내에 있는 데이터를 조회
Member findMember = em.find(Member.class, "member1");
이때 중요한 것은 ID값을 기준으로 조회를 한다는 것이다.
- DB로 들어가기 전에 DB에서 가지고 올때 모두 1차 캐시 기능처럼 움직이는 영속성 컨텍스트에 들어오게 된다
- 이는 쿼리문을 반복적으로 수행하지 않아도 되는 장점이 있다
- 하지만 단점은 정말 짧은 찰나이기 때문에 많은 인원이 중복으로 사용할 수 없고 티가 많이 안난다는 것이다.
2. 영속 엔티티의 동일성 보장
1차 캐시로 반복 가능한 읽기 등급의 트랜잭션 격리 수준을 데이터 베이스가 아닌 애플리케이션 차원에서 제공한다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
// 동일성 비교 true
System.out.println( a == b);
3. 엔티티 등록(트랜잭션을 지원하는 쓰기 지연)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin();
em.persist(memberA);
em.persist(memberB);
// 여기까지는 INSERT SQL을 DB에 보내지 않는다.
// 커밋을 하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();
- persist가 되면 저장소에 하나씩 INSERT문이 저장된다.
- commit 을 하는 순간 컨텍스트에 쌓여있는 INSERT문이 동시에 DB에 접근하는 것을 알수 있다.
- 이렇게 지연 저장소를 사용하는 것의 장점이 또 있다. 바로 batch전략을 사용할 수 있다는 것인데 저장소의 크기를 정해주고 해당 갯수만큼의 INSERT문이 들어오게 되면 바로 보내는 것이다. 예를 들어 batch를 2로 정하고 4개의 INSERT문을 작성하게 되면 DB에는 2개씩 2번 접근하는 것이다.
- 하지만 Id 의 전략이 GeneratedValue라면 Auto로 설정되어있기 때문에 DB에 데이터를 넣고 바로 PK를 꺼내야 하는 이유때문에 batch전략을 사용할 수 없다.
4. 엔티티 수정(변경 감지)
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();
도데체 어떻게 update문 없이 JPA가 스스로 update문을 작성할 수 있는걸까? 이것은 변경 감지를 하는 Dirty Checking에 있다.
- commit을 하면 flush()가 발생한다.
- 엔티티와 스냅샷을 비교한다.
- 스냅샷 : DB를 최초로 가져온 상태
- 변경 감지를 하게 되면 이떄 update문을 작성을 하게 되는 것이다.
- 그 후 DB에 접근하여 기존의 데이터를 변경하게 되면서 update문이 실행되는 것이다.
4. 엔티티 삭제
삭제는 별 다를게 없다 그냥 삭제하는 것이다.
Member member = em.find(Member.class, "memberA");
// 이때 삭제 쿼리가 작성된다.
em.remove(memberA);
💡 준영속성 상태
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 것을 준영속 상태라고 한다
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
준영속 상태로 만드는 방법
- em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료한다.
'Spring > 🌵 JPA' 카테고리의 다른 글
🌵 플러시(flush) (0) | 2023.09.19 |
---|---|
🌵 JPA의 구동 방식과 코드 (0) | 2023.09.18 |
🌵 JPA(feat. ORM) (0) | 2023.09.16 |