JPA 뿌셔버리기 - 1부

Date: 2021-12-09 Updated: 2021-12-11

들어가기 앞서 한가지 말하고 싶은건, 왜 진작 JPA 를 사용하지 않았냐는 것입니다..

진짜, RDBMS 쓰다가 ORM 쓰면 개 좋음. ㅎㅎ

JPA 란?

JPA

  • Java Persistence API
  • Java 진영의 ORM 기술 표준

ORM?

  • Object-Relational Mapping (객체 관계 맵핑)
    • Relational : 관계형 DB 를 뜻함
    • 객체 관계 맵핑 : ORM Framework 가 관계를 맺어줌

JPA 동작

동작 원리

  • JPA 는 Application 과 JDBC 사이에서 동작함 Alt ch01-01

저장

  • JPA에서 실제로 JDBC API 를 사용하여 동작 Alt ch01-02

조회

  • Alt ch01-03

JPA 꼭 사용해야되나?

생산성

  • 저장
    em.persist(member);
    
  • 조회

    Entity entity = em.find(id);
    
  • 수정
    entity.setName("name");
    
  • 삭제
    em.remove(entity);
    

유지보수

필드 변경 시

  • 기존 (JPA 사용 X)

    • 필드 변경시 모든 SQL 수정 필요
      • Mybatis 사용 시 추가된 필드가 있을 경우 해당 테이블의 Query 는 모두 변경해주어야 된다.
  • JPA 사용시

    • 추가된 컬럼의 필드만 Entity Class 에 추가하면 됨
      • 이것만으로도 유지보수 개편함

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

JPA 와 상속

  • 객체 상속 관계 맺으면 알아서 해줌 Alt ch01-04

저장

  • code 는 다음과 같다
    em.persist(movie);
    
  • 나머진 JPA 가 처리
    INSERT INTO ITEM ...
    INSERT INTO MOVIE ...
    

조회

  • 조회도 간단하다.
Movie movie = em.find(Movie.class, movieId);
  • 나머지 JPA 가 처리
SELECT I.*, M.*
FROM ITEM I
  JOIN MOVIE M ON I.ITEM_ID = M.ITEM_ID

JPA 와 연관관계 및 객체 그래프 탐색

  • 연관관계 저장

    member.setTeam(team);
    
    em.persist(member);
    
  • 객체 그래프 탐색

    Member member = em.find(Member.class, memberId);
    
    Team team = member.getTeam();// 1
    
    1. Fetch Type 이 Lazy 일 경우, 사용 시점에 SQL 이 실행된다.

JPA 와 비교하기

  • JPA 는 동일한 Transaction 에서 조회한 Entity 는 동일함을 보장해버림

    Long memberId = 1001;
    
    Member member1 = em.find(Member.class, memberId);
    Member member2 = em.find(Member.class, memberId);
    
    member1 == member2 // true
    

JPA 의 성능 최적화 기능

1차 캐시와 동일성 보장

  • 같은 Transaction 안에서는 같은 Entity 를 반환

    • 약간의(?) 조회 성능 향상
    • 사실상 실무에서는 거의 효과가 없다고 함
  • DB Isolation Level 이 Read Commit 이어도 Application 에서 Repeatable Read 보장

    Long memberId = 1001;
    
    Member member1 = em.find(Member.class, memberId); // 1
    Member member2 = em.find(Member.class, memberId); // 2
    
    println(member1 == member2); // true
    
    1. 조회하기전 1차 캐시에 해당 memberId 가 존재하는지 확인 후 없을 경우 SQL 실행하여 가져와 1차 캐시에 넣고, 반환
    2. 해당 memberId 가 1차 캐시에 존재하므로 SQL 를 실행하지 않고 1차 캐시에서 반환
    • 따라서, SQL 는 1번만 실행됨

Transaction 을 지원하는 쓰기 지연

Insert

  • Trancaction 을 커밋할 때까지 Insert SQL 을 모음
  • JDBC Batch SQL 기능을 사용해서 한번에 SQL 전송
transaction.begin(); // transaction 시작

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

// 여기까지 진행해도 SQL 은 실행되지 않는다.

// 해당 Transaction 의 Commit 하는 순간 DB 에 Insert SQL 를 모아서 보낸다.
transaction.commit(); // transaction 커밋

Update / Delete

  • UPDATE 또는 DELETE 로 인한 Row Lock 시간 최소화
  • Transaction Commit 시 UPDATE 또는 DELETE SQL 실행하고, 바로 Commit
transaction.begin(); // transaction 시작

changeMember(memberA);
removeMember(memberB);

비즈니스_로직_실행() // 비즈니스 로직 수행 동안 DB 의 Row Lock 이 발생하지 않는다.

// 해당 Transaction 의 Commit 하는 순간 DB 에 UPDATE, DELETE SQL 를 보낸다.
transaction.commit(); // transaction 커밋

지연 로딩과 즉시 로딩

  • 지연 로딩
    • 객체가 실제로 사용될 떄 로딩된다.
  • 즉시 로딩
    • Join SQL 로 한번에 연관된 객체까지 미리 조회

⚠️ 개발할떄

  • 처음에는 지연 로딩
  • 최적화 필요시 부분만 즉시로딩으로 하자

댓글남기기