Henu
개발냥발
Henu
전체 방문자
오늘
어제
  • 분류 전체보기 (411)
    • DevOps (52)
      • Kubernetes (19)
      • Docker (14)
      • AWS (3)
      • Nginx (4)
      • Linux (4)
      • ArgoCD (1)
      • CN (2)
      • NATS (0)
      • Git (5)
    • Back-End (30)
      • Django (18)
      • Spring (5)
      • JPA (1)
      • MSA (5)
    • CS (87)
      • SystemSoftware (20)
      • OS (25)
      • Computer Architecture (16)
      • Network (23)
      • Database (2)
    • Lang (21)
      • Java (9)
      • Python (4)
      • C# (8)
    • Life (12)
    • 블록체인 (2)
    • Algorithm (204)
      • BOJ (160)
      • 프로그래머스 (19)
      • LeetCode (4)
      • SWEA (1)
      • 알고리즘 문제 해결 전략 (8)
      • DS, algorithms (7)
      • Checkio (5)
    • IT (2)

블로그 메뉴

  • GitHub
  • 글쓰기
  • 관리자

공지사항

  • Free!

인기 글

태그

  • 백트래킹
  • BFS
  • docker
  • 프로그래머스
  • DFS
  • 다이나믹 프로그래밍
  • Kubernetes
  • django
  • boj
  • Network

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Henu

개발냥발

[JPA] Java Persistence API
Back-End/JPA

[JPA] Java Persistence API

2022. 1. 22. 04:32
 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

초급자를 위해 준비한 [백엔드, 웹 개발] 강의입니다. JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자

www.inflearn.com

 

김영한 님의 자바 ORM 표준 JPA 프로그래밍 강좌를 듣고 정리한 내용입니다.

 


ORM이란?

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

 


JPA의 동작

JPA가 JAVA 애플리케이션에서 어떤 순서로 동작하는지 간단하게 알아볼 수 있다.

 

 

개발자가 JPA에게 명령을 내리면, JPA는 JDBC API를 사용해서 명령에 알맞은 SQL을 DB로 보내고 결과를 받는다.

 

 

객체 저장

  1. JPA에게 member 객체를 넘긴다. (Entity Object)
  2. JPA가 다음 작업을 수행한다.
    1. Entity 분석
    2. INSERT 쿼리 생성
    3. JPA는 결국 JDBC API를 사용한다.
    4. 패러다임 불일치 해결

 

 

객체 조회

  1. JPA의 find(id) 기능을 호출한다.
  2. JPA가 다음 작업을 수행한다.
    1. SELECT 쿼리 생성
    2. JDBC API 사용
    3. ResultSet 매핑(객체의 필드 하나하나에 DB에서 가져온 값을 매핑해준다.)
    4. 패러다임 불일치 해결
  3. JPA는 ResultSet을 Entity Object로 리턴한다.

JPA를 사용하는 이유

1. SQL 중심적인 개발에서 객체 중심으로 개발

 

2. 생산성 - JPA와 CRUD

이미 모든 CRUD기능이 JPA에 만들어져 있다.

그냥 호출해서 사용하면 된다.

저장 : jpa.persist(member)

조회 : Member member = jpa.find(memberId)

수정 : member.setName("변경할 이름")
-> 수정은 특히 재미있다. member.setName("이름") 을 호출하면 알아서 이름이 바뀌어서 DB 업데이트 쿼리가 날아간다. JPA를 사용한다는 것은 마치 자바 컬렉션에 객체를 push, pop 하는것과 같이 만들기 위해 사용하는 것이다.

삭제 : jpa.remove(member)

 

 

3. 유지보수

기존 : 필드 변경시 모든 SQL 수정

기존 SQL

INSERT INTO
MEMBER(MEMBER_ID, NAME) VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
...

만약 MEMBER에 TEL 필드가 추가된다면?
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
...

모든 SQL에 TEL을 추가해야 할 것이다.

 

JPA : 필드만 추가하면 된다. SQL은 JPA가 처리한다.

public class Member {
    private String memberId;
    private String name;
    private String tel;
    ...
}

 

 

4. 패러다임의 불일치 해결

1. JPA와 상속

JAVA에서 Entity가 객체 상속관계를 가지고, RDB에서 Table 슈퍼타입 서브타입 관계를 가진다.

 

 

이때 개발자가 Album 객체를 DB에 저장하고 싶다면? 

jpa.persist(album);

개발자는 단 한줄만 작성하면 된다.

 

나머지는 JPA가 처리한다.

INSERT INTO ITEM ...
INSERT INTO ALBUM ...

 

Album을 조회하고 싶다면?

Album album = jpa.find(Album.class, albumId);

나머진 JPA가 처리한다.

SELECT I.*, A.*
 FROM ITEM I
 JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID

 

 

2. JPA와 연관관계

연관 관계 저장

member는 하나의 team을 갖는 관계이다. 하나의 team은 여러 멤버를 가질 수 있다.

member.setTeam(team);
jpa.persist(member);

 

 

3. JPA와 객체 그래프 탐색

객체 그래프 탐색

Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();

마치 Java 컬렉션에 member 객체가 있었던 것처럼 객체를 가져올 수 있다.

 

 

4. JPA와 비교하기

String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);

member1 == member2; //같다.​

동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다. (== 비교 사용)

 

 

5. 성능

1. 1차 캐시와 동일성 보장

  • 같은 트랜잭션 안에서는 같은 엔티티를 반환한다 - 약간의 조회 성능 향상
  • DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read를 보장한다.
 

[db] 트랜잭션 격리 수준(isolation level)

트랜잭션 격리수준(isolation level)이란 동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 것이다. 즉, 간단하게 말해 특정 트랜잭션이 다른 트랜잭션에

joont92.github.io

String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시

println(m1 == m2) //true

SQL은 1번만 실행된다.

 

 

2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)

트랜잭션을 커밋할 때까지 SQL을 모은다.

그리고 JDBC BATCH SQL 기능을 사용해서 한번에 SQL을 전송한다.

transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.

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

 

 

3. 지연 로딩(Lazy Loading)

지연 로딩 : 객체가 실제 사용될 때 로딩 -> LAZY

즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회 -> EAGER

 

 

더 알아보기!

즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.

"select m from Member m"

위 SQL은 Member만 SELECT 하게 된다.

MEMBER를 쭉 다 가져와서 보니까 Member 엔티티의 Team의 fetchType이 EAGER였다.

  • LAZY면 프록시(가짜 객체)를 넣으면 되겠지만, EAGER는 반환하는 시점에 다 조회가 되어 있어야 한다.
  • 따라서, Member를 다 가져오고 나서, 그 Member와 연관된 Team을 다시 다 가져온다.

따라서 Member들을 모두 조회해서 가져왔지만 각 Member들의 Team이 비어있기 때문에 Team을 채우기 위해 각 Member마다 Team을 가져오는 쿼리를 날려서 가져온다.

Member의 수가 10000명 이라면? 최소 10000개의 쿼리가 날아갈 것이다..

 

N + 1 문제의 의미는

위 설명처럼 member 정보를 가져오기 위한 쿼리를 1개 날렸는데, 그것 때문에 추가 쿼리가 N개 나간다는 의미이다.

 

    티스토리툴바