티스토리 뷰

JPA

1 - JPA Study - 영속성 관리 - 내부 동작 방식

✨✨✨✨✨✨✨ 2022. 1. 16. 18:22
반응형

w영속성 관리 - 내부 동작 방식 정리

 

JPA에서 가장 중요한 2가지는 무엇일까?

 

첫번째, 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)이다.

- DB를 어떻게 설계할것인지 이에따라 JPA는 어떻게 사용할 것인지 중요하다.

 

두번째, 영속성 컨텍스트이다.

JPA는 어떻게 작동할 것인지에 대한 이해가 중요하다. 즉,
영속성 컨텍스트를 이해해야하는데 이는
엔티티영구 저장하는 환경이라는 뜻으로, 아래 명령어 진행 시 엔티티를 영속성 컨텍스트에 저장하겠다는의미 입니다.

EntityManager.persist(entity);
em.find(EntityClass.class, entity);

영속성 컨텍스트는 눈에 보이지 않는 논리적인 개념으로써, EntityManager를 생성하면 영속성컨텍스트가 생성이됩니다.
사용자의 요청이 있는 경우, EntityManagerFactory를 통해 EntityManager을 생성 할 수 있으며,
EntityManager는 커넥션풀에 접근하여 커넥션객체를 사용하여 DB에 접근하고 반납합니다.

 

그럼 자세하게 영속성 컨텍스트에 대해 알아봅시다!

아래 코드를 실행하면 EntityManager을 통해 영속성컨텍스트가 생성이 됩나다. (entityManager) 내부에는 1차캐시를 들고있습니다.

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

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

// 1차 캐시에서 조회
  Member findMember = em.find(Member.class, "member1");
  • @Id 는 해당 Table의 PK가 됩니다.
  • @Entity는 해당 객체자체가 됩니다 (ex: member)

 

만약 1차캐시에 없는 findMember2를 조회할 경우는?

Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
em.persist(member);
Member findMember = em.find(Member.class, "member1");

// 1차캐시에 없는 fincMember2
Member findMember2 = em.find(Member.class, "member2");

em.find를 통해 조회를 하려고하면 JPA는 영속성컨텍스트에서 DB를 보는게 아니라 1차캐시를 먼저 조회합니다.
만약 1차캐시에 없다는 DB에 조회를 합니다. (영한님 나와버렸네요..ㅈㅅㅠ)

하지만 트랜잭션 단위로 진행되고 소멸되기에
고객의 요청이 들어오면 entityManager은 생성되었다가 요청이끝나면 삭제되기에 큰 성능이점은 없다.

 

그렇다면 영속성 컨텍스와 결합된 엔티티의 생명주기란 어떻게 될것인가?

  • 비영속 (new/transient)
    • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
// 객체만을 생성한 상태
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
  • 영속(managed)
    • 영속상태는 persist로 member을 집어넣거나 find로 가져운 상태이다.
    • 영속성 컨텍스트에 관리되는 상태 이는 아직 DB에 저장된 상태가 아니다
    • flush하기 전까지는 DB에 접근하여 저장되지 않는다.
// 객체를 생성한 후,
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");

EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManger em = createEntityManger();
em.getTransaction().begin();

// 객채를 저장한 상태(영속)
em.persist(member);
  • 준영속(detached)
    • 영속성 컨텍스트에 저장되었다가 분리된 상태를 의미합니다
    • 아래와 같이 특정 엔티티만을 준영속 할 수도 있습니다.
//회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.setId("member1");
em.setUsername("회원1");
em.detach(member);

// 해당 명령어는 아무것도 실행되지 않는다.
- 그 외에도
// 1. 특정 엔티티만을 준영속상태로 전환
em.detach(entity);

// 2. 영속성 컨텍스트를 완전히 초기화
em.clear();

// 3. 영속성컨텍스트를 종료
em.close();
  • 삭제(removed)
    • 삭제된 상태
em.setId("member1");
em.remove(member);
em.getTransaction().commt();

// insert 후에 delete된다.
/**insert 
into Member (age, createdDate, description, lastModifiedDate, roleType, name, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: 
   delete from Member  where id=?
**/

영속성 컨텍스트로 얻을 수 있는 이점은

    1. 1차캐시
      • find할 경우 1차캐시에 저장된 엔티티는 DB에서 조회해오지 않고 1차캐시에서 조회한다.
      • 사용자가 요청 시 각 사용자마다 1차캐시를 갖기때문에 큰 성능은 발휘되지 않는다.
    1. 동일성(identity) 보장
      // 마치 자바 컬렉션의 똑같은 객체가 있는것처럼 객체가 같다고 보장해준다.
      Member a = em.find(Member.class, "member1");
      Member b = em.find(Member.class, "member1");
      
      // true
      System.out.println(a==b);
    1. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
    1. 변경 감지
      트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 flush(플러시)가 호출된다.
      엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
      변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기지연 SQL저장소에 보낸다.
      쓰기지연 저장소의 SQL을 데이터베이스에 보낸다.
      데이터베이스 트랜잭션을 커밋한다.
    1. 지연 로딩(Lazy Loading)

플러시(flush)에 대해 자세히 알아보자

플러시(flush)란? 영속성컨텍스트의 변경내용을 데이터베이스에 반영하는것을 의미한다.

플러시가 발생하는 상황은 아래와 같다.

  • 영속성 컨텍스트의 스냅샷에 엔티티 찍힌 초기모습과 다름의 변경이 감지되었을 경우
    쓰기지연 SQL 저장소에 등록되고 트랜잭션 커밋하는 할 때,
    쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.

그렇다면 영속성컨텍스트를 플러시하는 방법은 어떻게 될까?

  • em.flush() - 직접 호출
  • 트랜잭션 커밋 - 플러시 자동 호출
  • JPQL 쿼리 실행 - 플러시 자동 호출

JPQL쿼리 실행 시 플러시가 자동호출되는 이유는 뭘까?

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
- JPQL 쿼리를 날림으로써 영속컨텍스트에 저장된 memberA,B,C가 조회가 안되면 이상한 흐름일 수 있으므로 flush가 실행된다.
  • 플러시는 영속성컨텍스트를 비우지않는다, 그렇기에 find시에 persis하였던 아이들이 계속 조회 가능할것이다.
tx.begin();
Member member = new Member(101L, "gbitkim", 25, "desc");
em.persist(member);
em.flush();
em.find(Member.class, member);
tx.commit();

위 명령어를 실행하면 flush실행 된 후에 find시 select문을 실행하지 않음으로써 영속성컨텍스트가 비워져있지 않음을 증명한다.


keyword

  • ORM : (Object-relational mapping) - OOP객체와 관계형 데이터베이스에서 사용되는 그 관계를 의미하는것
    • ORM을 통해 데이터베이스를 객체관점으로 반영해주고 조금 더 객체지향에 접근하기 위해 나온 프로그래밍 기술
  • JPA : 자바에서 제공하는 인터네스로 ORM기술에 대한 명세서
  • Hibernate : JPA 구현체의 한 종류로써, 하이버네이트를 사용하게되면,
    • 해당 메서드 안에는 JDBC API와 동작이 연결되어있어서 개발자가 SQL에 대한 작업보다는 비즈니스 로직에 조금 더 집중 할 수 있는 장점을 가졌다.
  • 영속성 컨텍스트
  • 1차 캐시
  • @Id Table에서 사용되는 PK
  • Entity 객체
  • 스냅샷 : Entity의 최초 상태를 저장해 두는 것이다.
  • 쓰기 지연 SQL
  • EntityManagerFactory
  • EntityManager
    • EntityManager.persist(entity);
  • 영속성 컨텍스트 생명주기
  • 비영속(new)
  • 영속(managed)
  • 준영속(detached)
  • 삭제(removed)
  • 플러시
  • 더티체킹(Dirty Checking)
  • em.flush();
  • JPQL
반응형

'JPA' 카테고리의 다른 글

다양한 연관관계 매핑  (0) 2022.02.06
양방향 연관관계와 연관관계의 주인2 - 주의점 정리  (0) 2022.01.27
영속성 컨텍스트2  (0) 2022.01.09
JPA 영속성과 생성주기  (0) 2022.01.06
JPA맛보기  (0) 2022.01.02
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함