JPA 소개

minjun kim·2025년 6월 18일

JPA

목록 보기
1/4
  • 해당 내용은 인프런에 김영한님의 강의를 듣고 작성한 시리즈입니다.

JPA가 나오게 된 배경

1.지루한 CRUD 작업
-> SQL에 의존적인 개발을 피하기 어렵다.
중간에 다른 사람이 추가해달라고하면 모든 레포지토리에 추가도 해야된다.

2.패러다임 불일치
객체지향은 추상화, 다형성, 캡슐화, 정보은닉, 상속 등 복잡성을 제어할 수 있는 다양한 장치를 제공하는데 관계형 데이터베이스에 저장을 할 때 SQL을 통해서 DB에 계속 저장해야하므로 SQL 매퍼작업이 필수인데 우리는 여기서에 객체지향과 DB에서의 차이가 발생한다.

1. 상속

상속이라 하면 DB에 앨범에 저장하더라도

1. 객체 분해
2. insert into item...
3. insert into Albbum...

조회를 한다면
1. 각각의 테이블에 따른 조인 SQL 작성...
2. 각각의 객체 생성...
을 통해 복잡한 과정을 거치게된다 .
-> DB에 저장할 객체는 상속관계 X

이게 자바 컬렉션이라면
Album album = list.add(AlbumId);

부모 타입으로 조회 후 다형성 활용 
Item item = list.get(AlbumId);

2. 연관관계

  • 객체는 참조를 사용 : member.get()
  • 테이블은 외래키를 사용 : JOIN ON M.Team_ID = T.Team_ID


객체를 이제 테이블에 맞춰서 모델링을 하게되고,

테이블에 맞춰서 저장하게 된다.
하지만 객체에서는 참조로 연관관계를 맺을 수 도 있다. -> Team team

그럼 insert를 할 때 팀에 teamid를 넣어줘야 할것이며,
객체 모델링을 조회하려고 한다면

join문과 정보를 입력후에 팀관계 설정을 해줘야한다.

객체 모델링, 자바 컬렉션에 관리를 한다면

list.add(member);

Member member = list.get(member_ID);
Team team = member.getTeam();

이런식으로 편하게 가져올 수 있다.

3.객체 그래프 탐색

객체는 자유롭게 객체 그래프를 탐색할 수 있어야하는데


처음 실행하는 SQL에 따라 탐색 범위 결정이 되어버리고 만다.

이로인해 SQL에 신뢰 문제가 발생하게 되고

모든 객체를 미리 로딩할 수 없으니까 상황에 따라 동일한 회원 조회 메서드를 여러개 생성 해야하는 경우가 생긴다.

memberDAO.getMember(); // member만 조회
memberDAO.getMemberWithTeam(); // member와 Team 조회
...

-> 진정한 의미의 계층 분할이 어렵다.

또 비교에서도 차이도 발생하게 되는데

DAO작업에서 new를 통해서 생성하기 때문에 동일성 입장에서 == 연산자를 비교하게 된다면 다르다

자바 컬렉션에서 조회를 한다면,

리스트에서 가져오기 때문에 같다.

이로써 객체작업을 진행할때마다 매핑작업이늘어나는 번거로움 때문에 JPA가 등장했다.

JPA의 성능 최적화 기능

JPA에서는 성능 최적화 기능이 있다
1. 1차 캐시와 동일성(Identity) 보장
2. 트랜잭션을 지원하는 쓰기 지연
3. 지연로딩

일단 JPA가 중간에 끼여있으니까 캐시역할과, 한번에 보내는 쿼리문을 보내는 특징을 가지게 된다.

1차 캐시와 동일성 보장

  1. 같은 트랜잭션에 있으면 같은 엔티티를 반환 - 약간의 조회 성능 향상
  2. DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시
println(m1 == m2) //true
SQL 1번만 실행

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

  1. 트랜잭션을 커밋할 때까지 insert sql을 모음
  2. JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // [트랜잭션] 커밋

update도 있는데 또 포스팅 하겠다!

지연로딩, 즉시로딩

  • 지연 로딩 : 객체가 실제 로딩될 때 사용
  • 즉시 로딩 : JOIN query로 한번에 연관된 객체까지 미리 조회

Team과 멤버가 있을때 서로 연관관계가 있다고하면, 해당 멤버에 팀이 계속된다고하면 JOIN으로 연관된 객체를 한번에 항상 조회하는게 좋지만,-> 즉시로딩

팀이 조회하는 수가 적다면 팀이 사용이될때 로딩되는게 좋다.-> 지연로딩


- PK값이 필요한 이유

  1. FK로 조건을 줄 때 해당 기본 컬럼을 준다면 (여러명이 있어서) update, delete에서 실수할 여지가 있음.

  2. JPA와 같은 ORM은 동작을 안함
    JPA는 객체와 테이블을 매핑할 때 이 객체는 이 테이블의 어느 행이다. 를 식별하려고 PK를 기준으로 삼기떄문에 없으면 JPA는 캐시, 변경감지, 병합 등 여러 기능을 정상적으로 사용 X

  3. FK 연결이 불안정
    A테이블에 특정 행을 B테이블에서 참조하려면 그 기준이 되는 유일한 값이 있어야하는데,
    PK가 없으면, 외래키도 제대로 못 만들거나 참조 무결성 제약을 걸 수 없다.

- 무결성

무결성 : 데이터가 논리으로 맞는 상태 인지 확인하고 그렇지 않은 데이터는 못 들어오게 막는것.

| 개체 무결성 | 테이블의 각 행이 유일해야 함 | → PK(Primary Key)가 필요함 |
| 도메인 무결성 | 컬럼 값이 정해진 범위 내여야 함 | → 나이는 0 이상, 이메일은 문자열 |
| 참조 무결성 | 외래키(FK) 값은 반드시 부모 테이블에 있어야 함 | → 주문의 user_id는 반드시 User 테이블에 존재해야 함 |

- 동일성과 동등성

동일성과 동등성 내용 참조

- "Read Committed인데 Repeatable Read 보장한다"

✅ 먼저, 격리 수준(Isolation Level) 요약
Read Uncommitted Dirty Read, 다른 트랜잭션의 미완료 데이터를 읽음
Read Committed ✔ Dirty Read, 방지 커밋된 데이터만 읽음 (하지만 반복 읽을 때 값이 바뀔 수 있음)
Repeatable Read ✔ + Non-repeatable Read 방지, 한 트랜잭션 내에서 같은 쿼리 결과는 항상 같음
Serializable 모든 현상 방지, 가장 강력하지만 성능 저하 큼

DB는 Read Committed 수준으로만 격리하지만,
애플리케이션(JPA 등)이 같은 데이터를 계속 똑같이 보장하도록 처리해서
사실상 Repeatable Read 수준으로 동작한다는 의미

profile
배움의 흔적을 남기고 싶습니다.

0개의 댓글