JPA

JPA기본편 - 영속성 관리

땅콩콩 2023. 4. 1. 12:56
엔티티 매니저 팩토리와 엔티티 매니저

고객의 요청이 올 때마다 엔티티매니저를 생성하고, 이 엔티티 매니저는 데이터 커넥션을 사용해서 db에 접근하게 된다.

 

 

영속성 컨텍스트란?

영속성 컨텍스트란, 엔티티를 영구 저장하는 환경이다.

논리적인 개념으로 눈에 보이지는 않고 엔티티 매니저를 통해서 영속성 컨텍스트에 접근한다.

//엔티티를 영속성 컨텍스트에 저장한다.
EntityManager.persist(entity);

 

엔티티 생명주기

1. 비영속 (new/transient)
 = 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태

2. 영속 (managed) = 영속성 컨텍스트에 관리되는 상태

3. 준영속 (detached) = 영속성 컨텍스트에 저장되었다가 분리된 상태

4. 삭제 (removed) = 삭제된 상태

	   //비영속 상태
            Member member = new Member();
            member.setId(100L);
            member.setName("HelloJPA");

            //영속 상태 >> 이때 디비에 쿼리가 날아가는 것은 아님.
            em.persist(member);

            //준영속 상태. 회원 엔티티를 영속성컨텍스트에서 분리.
            em.detach(member);

            //객체를 삭제한 상태
            em.remove(member);
            
            tx.commit(); //이때 디비에 쿼리가 날아감.

 

 

영속성 컨텍스트 사용의 장점

 

1. 1차 캐시

영속성컨텍스트에서 1차캐시를 통해 db를 통하지않고도 조회가 가능하다. (1차캐시에 없을때만 db조회, 조회 후 1차캐시에 저장)

단, 1차 캐시는 데이터베이스 한 트랜잭션 안에서만 유지가 되는 캐시이다. (애플리케이션 전체에서 공유하는 캐시는 jpa에서 2차 캐시라고 부른다.)

 

2. 영속 엔티티의 동일성 보장

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true

마치 자바 컬렉션에서 같은 레퍼런스로 객체를 꺼내면 ==비교를 해도 동일한 객체를 반환해주는것처럼 영속 엔티티의 동일성이 보장된다.

이는 jpa가 1차캐시로 엔티티를 관리하기 때문이다.

 

3. 엔티티 등록할때 트랜잭션을 지원하는 쓰기 지연 가능

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

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

em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다. 쓰기 지연 sql 저장소에 저장해둠.

transaction.commit(); //트랜잭션 커밋, 이 순간 데이터베이스에 INSERT SQL을 보낸다.

 

4. 엔티티 수정할때 변경감지 (dirty checking)

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

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

// 영속 엔티티 데이터 수정 (jpa가 변경감지 > update쿼리 만들어 쓰기 지연 sql저장소에 넣어둠.)
memberA.setUsername("hi");
memberA.setAge(10);

//em.update(member) 이런 코드가 있어야 할것같지만 없어도 된다!

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

database transaction commit을 하는 시점에 내부적으로 flush()가 호출된다.

그리고나서 데이터베이스 엔티티와 스냅샷을 비교한다. 그리고 그 내용이 바뀌었다면 update 쿼리를 쓰기지연 sql 저장소에 만들어둔다.

그리고 후에 이 sql이 실행되며 db에 반영되는 것이다.

엔티티를 삭제할때도 매커니즘은 동일하며 em.remove(memberA); 와 같이 처리한다.

 

5. 지연 로딩 (Lazy Loading)

예를 들어 member.getTeam()으로 팀 정보를 가져오고 싶을 때 팀과 관련된 쿼리를 지금 당장 날리는 것이 아니라 이후에 날리는 것.

 

 

flush

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

database transaction이 commit되면 flush가 발생하고, flush가 발생하면 다음과 같은 일이 일어난다.

1. 변경 감지
2. 수정된 엔티티와 관련된 sql을 쓰기 지연 sql 저장소에 등록
3. 쓰기 지연 sql 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리 등등)

주의해야 할것은 flush가 영속성 컨텍스트를 비우는 것이 아니라는 점이다.

flush는 단지 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화시키는 것이다.

결국 트랜잭션이라는 작업 단위가 중요한 것이므로, 커밋직전에만 이러한 동기화가 일어나면 된다.

 

 

준영속 상태

영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 상태를 말한다.

이 상태에서는 영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.

준영속 상태로 만드는 방법은 다음과 같다.

em.detach(entity); //특정 엔티티만 준영속 상태로 전환.

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

em.close(); //영속성 컨텍스트를 종료.

 

 

*인프런 김영한님의 JPA 기본편 강의를 들으며 정리한 내용입니다.

*https://www.inflearn.com/course/ORM-JPA-Basic