JPA 뿌셔버리기 - 1부
들어가기 앞서 한가지 말하고 싶은건, 왜 진작 JPA 를 사용하지 않았냐는 것입니다..
진짜, RDBMS 쓰다가 ORM 쓰면 개 좋음. ㅎㅎ
JPA 란?
JPA
- Java Persistence API
- Java 진영의 ORM 기술 표준
ORM?
- Object-Relational Mapping (객체 관계 맵핑)
- Relational : 관계형 DB 를 뜻함
- 객체 관계 맵핑 : ORM Framework 가 관계를 맺어줌
JPA 동작
동작 원리
- JPA 는 Application 과 JDBC 사이에서 동작함
저장
- JPA에서 실제로 JDBC API 를 사용하여 동작
조회
JPA 꼭 사용해야되나?
생산성
- 저장
em.persist(member);
-
조회
Entity entity = em.find(id);
- 수정
entity.setName("name");
- 삭제
em.remove(entity);
유지보수
필드 변경 시
-
기존 (JPA 사용 X)
- 필드 변경시 모든 SQL 수정 필요
Mybatis
사용 시 추가된 필드가 있을 경우 해당 테이블의 Query 는 모두 변경해주어야 된다.
- 필드 변경시 모든 SQL 수정 필요
-
JPA 사용시
- 추가된 컬럼의 필드만 Entity Class 에 추가하면 됨
- 이것만으로도 유지보수 개편함
- 추가된 컬럼의 필드만 Entity Class 에 추가하면 됨
JPA 와 패러다임의 불일치 해결
JPA 와 상속
- 객체 상속 관계 맺으면 알아서 해줌
저장
- 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
- Fetch Type 이
Lazy
일 경우, 사용 시점에 SQL 이 실행된다.
- Fetch Type 이
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차 캐시에 해당
memberId
가 존재하는지 확인 후 없을 경우 SQL 실행하여 가져와 1차 캐시에 넣고, 반환 - 해당
memberId
가 1차 캐시에 존재하므로 SQL 를 실행하지 않고 1차 캐시에서 반환
- 따라서, SQL 는 1번만 실행됨
- 조회하기전 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 로 한번에 연관된 객체까지 미리 조회
⚠️ 개발할떄
- 처음에는 지연 로딩
- 최적화 필요시 부분만 즉시로딩으로 하자
댓글남기기