[JPA] 영속성 컨텍스트

SIK407·2024년 8월 5일
0

spring

목록 보기
4/11
post-thumbnail

DB나 JPA를 한번쯤 사용해보셨다면, 저 제목인 영속성 컨텍스트나 영속성이란 단어를 들어봤을 것이다.

엥.... 그럼 영속성 컨텍스트가 머에요 ㅡㅡ

기본 개념

트랜젝션에는 크게 4가지의 특성이 있다.
1. 원자성 (Atomicity)
2. 일관성 (Consistency)
3. 독립성 (Isolation)
4. 영속성 (Durablity)

영속성 (persistence)
데이터를 생성한 프로그램이 종료되어도 영구적으로 잘 저장이 되어야 함


JPA는 영속성 컨텍스트라는 아주 중요한 기능이 있다.

영속성 컨텍스트 (Persistence Context)
엔티티를 영구 저장하는 환경
애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할
Entity Manager(em)를 통해 Entity를 저장하거나 조회하면 em은 영속성 컨텍스트에 Entity를 보관하고 관리


음, 이게 무슨 소리냐면....
우리는 Spring JPA 혹은 그냥 JPA를 사용하다 보면 이런 문장은 한번쯤은 꼭 봤다.
em.persist(entity);

"어...? 이거 DB에 저장하는 코드 아니야??"
반은 맞고 반은 틀렸다.

Spring에서 사용하는 JPA는 전에 글에도 적어놨지만,
tx.commit(); tx.begin();
얘네들을 생략한다.
Spring이 자동적으로 적용을 시켜주기 때문이다.


출처: https://doooyeon.github.io/2018/09/28/transaction.html

트랜잭션의 특성 중, 원자성에 의해 모든 트랜잭션 시작하면, 전부 다 실패하거나 성공해야 된다.

위에 이미지에서 시작 tx.begin()을 하게 되고 부분 완료가 되면,
커밋 tx.commit()을 하게 된다.

즉, em.persist(entity) 이게 작동이 되도 tx.commit() 커밋을 안하면 DB에 저장이 안된다.

그럼 이제부터 Entity의 생명주기에 대해 알아보자.



엔티티(Entity) 생명주기

? 이게 뭐시여 도대체....;;
총 4가지의 상태가 있다.

비영속(New)

객체를 생성한 상태.
그러나, 영속성 컨텍스트에 저장하지 않은 상태이다.

Member member = new Member();

그럼 이 상태에서 em.persist(member)을 하게 되면, 영속 상태가 된다.

영속(Managed)

em을 통해서 Entity를 영속성 컨텍스트에 저장한 상태
영속성 컨텍스트에 의해 관리된다는 뜻이다.

Member member = new Member(); // 비영속
member.setName("member1");
member.setTeam(team);

em.persist(member); // 영속 (컨텍스트에 저장)

영속 상태가 되면, 영속성 컨텍스트의 기능을 이용할 수 있다.

1. 1차 캐시

@IdEntity
1LMember

이런식으로 1차 캐시라는 곳에 저장이 된다.

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

원래는 이러면 DB에서 가져와야 하지만....
영속성 컨텍스트의 1차 캐시에 의해 DB에서 가져오는게 아니라, 1차 캐시에서 조회한다.

2. 변경 감지 (Dirty Checking)

진짜 신기한게, EntityManager(em)으로 select로 가져왔다고 치자.

Book book1 = em.find(Book.class, 1L);
book1.setAUTHOR("KIM");

tx.commit();

이런식으로 JPQL이나 뭔가를 사용하지 않아도,
트렌잭션을 commit 할 때, 자동으로 값이 업데이트 된다고 한다.
이 기능을 Dirty Checking 이라고 한다.

정확하게는 JPA를 통해서 조회한걸 가져오면, 영속성 컨텍스트가 그때부터 그 객체를 관리한다.
그 트렌잭션을 commit할 때, Dirty Checking이 수정사항을 자동으로 Update 해준다.

3. 영속 Entity의 동일성 보장

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

이거 출력하면 True가 나온다.

4. 쓰기 지연

transaction.begin(); // [트랜잭션] 시작

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

transaction.commit(); // [트랜잭션] 커밋

Entity Manager(em)은 트랜잭션을 내부 쿼리 저장소에 SQL을 모아둔다.
그리고 트랜잭션을 commit할 때, 모아둔 쿼리를 DB에 보낸다.

준영속 (Detached)

영속성 컨텍스트가 관리하던 영속 상태의 엔티티 더이상 관리하지 않는 상태

em.detach(member); // Entity를 영속성 컨텍스트에서 분리
em.claer(); 	   // 영속성 콘텍스트 비우기
em.close(); 	   // 영속성 콘텍스트 종료

이 상태가 되면, 영속성 컨텍스트가 제공하는 어떠한 기능도 작동하지 않는다.
1차 캐시 쓰기 지연 변경 감지 지연 로딩 등등...

삭제 (Removed)

Entity를 영속성 컨텍스트와 DB에서 삭제한 상태

em.remove(member);

플러시 (flush)

영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 기능.

이 조건일 때, flush가 작동한다.

  1. 변경 감지
  2. 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
  3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
  4. JPQL 쿼리 실행
  5. 직접 em.flush() 실행

근데, 왜 JPQL을 실행하면 플러시가 작동돼...?

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

//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();

이런 경우처럼, 만약에 1차 캐시에만 저장되고 조회를 한 경우....
오류가 발생한다.

그래서 기본적으로는 JPQL을 실행하면 em.flush()가 자동으로 실행된다.

em.setFlushMode(모드를 적으세용);

// FlushModeType.AUTO -> 커밋, JPQL 모두 다 플러시 실행
// FlushModeType.COMMIT -> 커밋할때만 플러시 실행

마지막으로....

  • 영속성 컨텍스트를 비우지 않음
  • 영속성 컨텍스트의 변경 내용을 DB에 동기화
  • 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화 하면 됨
profile
Spring 백엔드!

0개의 댓글

관련 채용 정보