[JPA] 기본정리

Dev_ch·2023년 5월 28일
0
post-thumbnail

JPA

Java Persistence API

  • 자바 진영의 ORM 표준 기술
  • 하이버네이트(hibernate)
  • JPA는 인터페이스의 모음
  • JPA 2.1 표준 명세를 구현한 3가지 구현체 중 하이버네이트

ORM

Object-relational mapping

  • 객체는 객체대로 설계
  • 관계형 데이터베이스는 관계형 데이터베이스대로 설계
  • ORM 프레임워크가 중간에서 매핑
    대중적인 언어에는 대부분 ORM 기술이 존재

JPA는 애플리케이션과 JDBC 사이에서 동작한다.

JPA는 패러다임 불일치를 해결

JPA를 사용하는 이유

  • SQL 중심에서 객체 중심으로 개발
  • 생산성
  • 유지보수
  • 패러다임의 불일치 해결
  • 성능

JPA와 패러다임의 불일치 해결

  • 상속
  • 연관 관계
  • 객체 그래프 탐색

JPA 성능 최적화

  • 1차 캐시와 동일성 보장 -> 같은 트랜잭션 안에서는 같은 엔티티를 반환
  • 트랜잭션을 지원하는 쓰기 지연
  • 지연 로딩
    - 객체가 실제 사용될때 로딩됨
    🔄 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회

영속성 컨텍스트

  • 엔티티를 영구 저장하는 환경
    • 영속 : 영속성 컨텍스트에 관리되는 상태 -> em.persist(member)
    • 비영속 : 영속성 컨텍스트에 전혀 관계가 없는 새로운 상태 (그냥 객체)

영속성 컨텍스트의 이점

1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경 감지, 지연 로딩

1차 캐시

id값을 기준으로 엔티티에 1차 캐시 안에 저장해둠 -> DB보다 우선순위로 접근, 없을 경우 DB에서 조회 후 1차 캐시에 저장 및 반환 -> 한 트랜잭션 안에서만 도는 경우가 대다수이기에 성능에서 얻는 이점은 딱히 없음

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

// 영속 상태로 전환
System.out.println("=== before ===");
em.persist(member);
System.out.println("=== after ===");

Member findMember = em.find(Member.class, 101L);

System.out.println("findMember = " + findMember.getId());
System.out.println("findMember.getName() = " + findMember.getName());
            

em.persist를 통해 영속성 컨텍스트에 member를 저장해놨기 때문에 Select 쿼리가 나가지 않음

Member findMember1 = em.find(Member.class, 101L);
Member findMember2 = em.find(Member.class, 101L);
            
tx.commit();

해당 코드도 마찬가지로 이미 101번 member를 영속성 컨텍스트에서 관리하고 있기에 쿼리가 2번 나가지 않음.

영속 엔티티의 동일성 보장

Member findMember1 = em.find(Member.class, 101L);
Member findMember2 = em.find(Member.class, 101L);

System.out.println("result = " + (findMember1 == findMember2));

tx.commit();

두 엔티티의 동일성은 true 이다.

트랜잭션을 지원하는 쓰기 지연

영속성 컨텍스트에는 안에 1차 캐시 뿐만 아니라 쓰기 지연 SQL 저장소가 있음, persist가 되면 해당 저장소에 SQL을 생성해둠.

transaction.commit()을 하는 순간에 flush를 해서 DB에 쿼리를 날림

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member member1 = new Member(150L,"A");
            Member member2 = new Member(160L,"B");
            
            // 영속성 컨텍스트에 저장 및 쿼리 생성
            em.persist(member1);
            em.persist(member2);

			// 이때 쿼리가 나감
            tx.commit();

변경 감지

Member member = em.find(Member.class, 150L);
member.setName("ZZZZZ");

commit을 하면 엔티티와 스냅샷을 비교, 값을 최초로 영속성 컨텍스트에 들어온 상태를 스냅샷에 저장되어있는데 Entity가 변경이 되면 스냅샷과 비교하여 변경을 감지해 쓰기 지연 SQL 저장소에 Update SQL을 생성하고 DB에 쿼리를 날림

Flush

영속성 컨텍스트의 변경내용을 데이터베이스에 반영

  • 변경 감지
  • 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
Member member = new Member(200L, "member200");
            em.persist(member);
            
            em.flush();
            
            tx.commit();

em.flush()를 통해 강제로 DB에 먼저 쿼리를 날리게함 -> 데이터베이스에 반영이 되는 과정

다만 commit()이나 JPQL의 경우 플러시가 자동으로 호출됨.
⚠️ 영속성 컨텍스트를 비우는 것이 아님
⚠️ 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
⚠️ 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화 하면 됨

준영속 상태

  • 영속 -> 준영속
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함
Member member = em.find(Member.class, 150L);
            member.setName("AAAAA");
            
            // 영속성 컨텍스트에서 제거
            em.detach(member);
            
            tx.commit();

해당 코드는 update 되지 않는다.
em.detach() / em.clear() / em.close()

profile
내가 몰입하는 과정을 담은 곳

0개의 댓글