JPA & Querydsl/JPA

1. 영속성 관리

JPA는 스레드가 생설될 때 마다 EntityManagerFactory에서 EntityManager를 생성한다.
EntityManager는 내부적으로 DB 커넥션 풀을 사용해서 DB 접근한다.

 

 

영속성 컨텍스트란 

엔티티를 영구 저장하는 환경으로 눈에 보이지않는 논리적인 개념이다.

이러한 영속성 컨텍스트는 엔티티 매니저를 통해서 접근하게 된다.

 

엔티티의 생명주기

비영속(New)

영속성 컨텍스트와 관계가 없는 상태

 

영속(Managed)

영속성 컨텍스트에 저장된 상태

엔티티가 영속성 컨텍스트에 의해 관리되며 트랜잭션 커밋 시점에 DB에 쿼리를 날리며 저장된다.

 

준영속(Detached)

영속성 컨텍스트에 저장되었다가 분리된 상태

 

삭제(Removed)

삭제된 상태 트랜잭션 커밋 시점에 DB에 쿼리를 날리며 삭제된다.

// 비영속, 영속, 준영속
Member1 member = new Member1(102L, "helloC");
em.persist(member); // 비영속 -> 영속
em.detach(member); // 영속 -> 준영속

영속성 컨텍스트의 이점

 

1. 1차 캐시 & 영속성 엔티티의 동일성

엔티티를 조회할때 영속성 컨텍스트에 해당 엔티티가 있는지 먼저 조회한다. - 1차 캐쉬

반복 가능한 읽기 등급의 트랜잭션 격리 수준을 애플리케이션 차원에서 제공 - 동일성 보장

// 1차 캐시, 영속성 엔티티의 동일성
Member1 member = new Member1(103L, "helloC");
em.persist(member);
Member1 findMember = em.find(Member1.class, 103L); // 1차 캐시에 있는 값
System.out.println("result: " + (member == findMember)); // true

2.트랜잭션을 지원하는 쓰기 지원

영속성 컨텍스트에 영속화된 엔티티는 커밋, flush()메서드, JPQL 퀴리 실행시에 DB에 저장된다. 

// 엔티티 등록 지연 쓰기 - insert 문이 영속된 객체를 모아서 처리
Member1 member1 = new Member1(104L, "hello1");
Member1 member2 = new Member1(105L, "hello2");
em.persist(member1);
em.persist(member2);
System.out.println("=== 영속 시점 ===");

// flush - 플러쉬 살행 시점 (1. flush() 메서드, 2. 트랜잭션 commit, 3. JPQL 쿼리 실행)
Member1 member = new Member1(105L, "helloFlush");
em.persist(member);
em.flush();
System.out.println("flush 할 경우 더티 체크 및 지연 쓰기가 실행됩니다.");

3.변경감지

영속 엔티티를 조회 할 경우 영속성 컨텍스트는 해당 엔티티의 조회값을 스냅샷으로 가지고 있으며 해당 엔티티가 변경된 경우 DB에 저장되는 시점에 해당 변경내용을 update하는 쿼리를 자동으로 날린다.

4. 지연로딩 (해당내용은 차후 설명 예정)

 

 

영속성 컨텍스트의 플러쉬

플러시는 영속성 컨텍스트의 내용을 데이터 베이스에 반영합니다.(생성, 수정, 삭제)

이때 쓰기 지연 저장소에 쌓아 놨던 쿼리문이 데이터 베이스에 날라가게 됩니다.

 

영속성 컨텍스트를 flush 하는 방법

3번의 jpql은 영속성 컨텍스트의 관리를 받지 않으므로 DB와 같은 상태를 유지하기 위해서 jpql 실행 전 플러쉬가 일어난다.

// 1. 직접 호출
em.flush() // 플러쉬 시점


// 2. 트랜잭션 커밋
EntityTransaction tx = em.getTransaction();
tx.begin();
tx.commit(); // 플러쉬 시점


//3. 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();

 

준영속 상태로 만드는 방법

// 1. 특정 엔티티만 전환
em.detach(member);

// 2. 영속성 컨텍스트 초기화
Member1 member = new Member1(106L, "helloC");
em.persist(member);
em.clear();

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

 

실습 전체 코드

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Member1 {

    @Id
    private Long id;
    private String name;

    public Member1(){}

    public Member1(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

 

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class MemberCRUDTest {

    public static void main(String[] args) {
        // persistence.xml 의 persistence-unit 을 받아서 EntityManagerFactory 생성 - 하나만 생성해서 전체 어플리케이션 공유
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test01");

        //EntityManagerFactory 에서 EntityManager 생성 - 쓰레드간에 공유 X (사용하고 버려야함)
        EntityManager em = emf.createEntityManager();

        // EntityManager 의 트랜잭션 받아옴
        EntityTransaction tx = em.getTransaction();

        // 트랜잭션 시작 - 모든 데이터 변경은 이 안에서 처리해야함
        tx.begin();

        try {
            /*
            // INSERT
            Member1 member = new Member1();
            member.setId(1L);
            member.setName("helloB");
            em.persist(member);*/


            /*
            // SELECT One
            Member1 findMember = em.find(Member1.class, 1L);
            System.out.println(findMember.getId());
            System.out.println(findMember.getName());
            */


            /*
            // SELECT List - JPQL 사용
            List<Member1> result = em.createQuery("select m from Member1 as m", Member1.class)
                    .setFirstResult(0)
                    .setMaxResults(8)
                    .getResultList();

            for (Member1 member : result){
                System.out.println(member.getName() + " " + member.getId());
            }
            */


            /*
            // UPDATE - 엔티티 변경 감지(Dirty Checking)
            Member1 findMember = em.find(Member1.class, 1L);
            findMember.setName("HelloJpa");
            */


            /*
            // DELETE
            Member1 findMember = em.find(Member1.class, 1L);
            em.remove(findMember);
            */


            // 트랜잭션 커밋
            tx.commit();
        } catch (Exception e){
            // 롤백
            tx.rollback();
        } finally {
            // EntityManager 닫음
            em.close();
        }

        // EntityManagerFactory 닫음
        emf.close();
    }

}

 

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class MemberPersistenceTest {

    public static void main(String[] args) {
        // persistence.xml 의 persistence-unit 을 받아서 EntityManagerFactory 생성 - 하나만 생성해서 전체 어플리케이션 공유
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test01");

        //EntityManagerFactory 에서 EntityManager 생성 - 쓰레드간에 공유 X (사용하고 버려야함)
        EntityManager em = emf.createEntityManager();

        // EntityManager 의 트랜잭션 받아옴
        EntityTransaction tx = em.getTransaction();

        // 트랜잭션 시작 - 모든 데이터 변경은 이 안에서 처리해야함
        tx.begin();
        try {
            /*
            // 비영속, 영속, 준영속
            Member1 member = new Member1(102L, "helloC");
            em.persist(member); // 비영속 -> 영속
            em.detach(member); // 영속 -> 준영속
            */


            /*
            // 1차 캐시, 영속성 엔티티의 동일성
            Member1 member = new Member1(103L, "helloC");
            em.persist(member);
            Member1 findMember = em.find(Member1.class, 103L); // 1차 캐시에 있는 값
            System.out.println("result: " + (member == findMember)); // true
            */


            /*
            // 엔티티 등록 지연 쓰기 - insert 문이 영속된 객체를 모아서 처리
            Member1 member1 = new Member1(104L, "hello1");
            Member1 member2 = new Member1(105L, "hello2");

            em.persist(member1);
            em.persist(member2);
            System.out.println("=== 영속 시점 ===");
            */


            /*
            // flush - 플러쉬 살행 시점 (1. flush() 메서드, 2. 트랜잭션 commit, 3. JPQL 쿼리 실행)
            Member1 member = new Member1(105L, "helloFlush");
            em.persist(member);
            em.flush();
            System.out.println("flush 할 경우 더티 체크 및 지연 쓰기가 실행됩니다.");
            */

             /*
             // 영속성 컨텍스트 초기화
            Member1 member = new Member1(106L, "helloC");
            em.persist(member);
            em.clear();
            */


            tx.commit();
        } catch (Exception e){
            // 롤백
            tx.rollback();
        } finally {
            // EntityManager 닫음
            em.close();
        }

        // EntityManagerFactory 닫음
        emf.close();
    }
}

 

Reference

자바 ORM 표준 JPA 프로그래밍 - 김영한님

'JPA & Querydsl > JPA' 카테고리의 다른 글

5. 고급 매핑  (0) 2021.02.24
4. 다양한 연관관계 매핑  (0) 2021.02.24
3. 연관관계 매핑 기초  (0) 2021.02.22
2. 엔티티 맵핑  (0) 2021.02.21
인텔리제이 자바 버전 변경  (0) 2021.02.16