JPA

영속성 컨텍스트

쭈녁 2024. 1. 28. 00:43

 

 

엔티티 생명주기 

 

비영속 (new/transient)

  • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태

영속 (managed)

  • 영속성 컨텍스트에 관리되는 상태

준영속 (detached)

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

삭제 (removed)

  • 삭제된 상태

 

 

영속성 컨텍스트는 엔티티를 관리하는 환경으로 Key와 그에 해당하는 객체로 엔티티를 관리한다.

 

 

영속성 컨텍스트를 동해 얻는 이점

  • 1차 캐시에 저장하여 관리함으로 실제 DB에 커넥팅 하지 않고 DB의 값을 조회하는 등 성능상 이점
  • 캐시를 통해 관리함으로 트렌젝션 내 동일성(identity) 보장 
  • 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
  • 변경 감지(Dirty Checking)를 통한 저장
  • 지연 로딩(Lazy Loading)

 

 

Member 엔티티

@Entity
@Getter
@Setter
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    
    private String name;
    
    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

 

 

Team 엔티티

@Entity
@Getter
@Setter
public class Team {
    @Id
    @GeneratedValue
    private Long id;

    private String teamName;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
}

 

영속성 컨텍스트의 처리 테스트

@SpringBootApplication
public class Ex1HelloJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(Ex1HelloJpaApplication.class, args);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Team team = new Team();
            team.setTeamName("teamHello");

            Member member = new Member();
            member.setName("HelloA");
            member.setRoleType(RoleType.ADMIN);
            member.setTeam(team);
            
            //지연 쓰기확인을 위한 출력
            System.out.println("before");
            em.persist(team);
            em.persist(member);
            System.out.println("after");

            Member find = em.find(Member.class, 1);
            System.out.println(find.getName());

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}

 

1. Team객체와 Member객체를 생성한다.

 

2. 지연 쓰기 기능을 확인하기 위한 저장 전 후에 "before" , "after" 출력 테스트

 

3. 영속성 컨텍스트 내에 엔티티를 관리함을 확인하기 위한 조회

    - 실제 쿼리를 날리지 않고 관리되고 있는 컨텍스트 내에서 객체를 가져옴 

 

4. 이상이 없으면 커밋, 예외 발생 시 롤백, EntityManager 와 EntityManagerFactory의 커넥션 반환

 

 

실제 쿼리문

 

 

변경 감지 테스트

@SpringBootApplication
public class Ex1HelloJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(Ex1HelloJpaApplication.class, args);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            
            Member find = em.find(Member.class, 1);
            find.setName("newName");

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}

 

 

저장한 DB를 조회하여 내부 프로퍼티의 값을 변경하였다.

JPA의 더티 채킹 기능으로 persist하지 않았음에도 변경을 감지하여 업데이트 쿼리를 날린 것을 확인할 수 있다.

 

 

쿼리 로그

 

Member의 이름만 변경하기 위해 조회해 왔는데

자동으로 연관관계에 있는 Member를 ID로 조인해 오는 것을 확인할 수 있다.

 

 

 

지연 로딩

 

Member 엔티티의 연관관계 매핑되어 있는 Team의 조회를 지연로딩으로 변경(default = 즉시로딩)

@Entity
@Getter
@Setter
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    
    private String name;
    
    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

 

MemberDB 조회

@SpringBootApplication
public class Ex1HelloJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(Ex1HelloJpaApplication.class, args);
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            
            Member find = em.find(Member.class, 1);
            find.setName("newName2");

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}

 

쿼리 로그

 

지연로딩으로 설정함으로 Team이 필요하지 않다면 조회해 올 당시에 Join 하지 않고 Member의 엔티티만 조회해 온다.

 

 

코드 출처 및 참고 자료

인프런 / 김영한 / 자바 ORM 표준 JPA 프로그래밍 - 기본편

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

 

 

'JPA' 카테고리의 다른 글

Querydsl 정리  (0) 2024.03.29
JPA , QueryDsl 수정 중 에러 해결  (0) 2024.03.17
JPQL문법 2 (페치 조인,벌크 연산)  (0) 2022.12.23
JPQL 문법 1  (1) 2022.12.22
JPA 쿼리 언어 종류  (1) 2022.12.22