mojo's Blog
JPA 이란 본문
※ JPA
- Java Persistence API
- 자바 진영의 ORM 기술 표준이다.
※ ORM
- Object-relational mapping (객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
※ JPA는 표준 명세
- JPA 는 인터페이스의 모음
- JPA 2.1 표준 명세를 구현한 3가지 구현체
- 하이버네이트, EclipseLink, DataNucleus
※ JPA를 사용하는 이유
- SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성
- 유지보수
- 패러다임의 불일치 해결
- 성능
- 데이터 접근 추상화와 벤더 독립성
- 표준
① 생산성 (JPA 와 CRUD)
- 저장 : jpa.persist(member)
- 조회 : Member member = jpa.find(memberId)
- 수정 : member.setName("변경이름")
- 삭제 : jpa.remove(member)
CRUD 를 보면 미리 코드가 만들어져서 간단하게 활용만 하면 된다!
② 유지보수 (기존 : 필드 변경시 모든 SQL 수정)
public class Member {
private String memberId;
private String name;
private String tel;
...
}
Member 클래스에서 tel 이라는 String 타입 필드가 추가되었다고 하자.
그렇다면 모든 SQL 을 tel 이 추가되었다는 이유만으로 수정을 해줘야 한다.
- INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES
- SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
- UPDATE MEMBER SET ... TEL = ?
- ...
JPA 를 사용하게 된다면?
=> 필드만 추가하면 되며, SQL 은 JPA 가 알아서 처리해준다!
③ JPA 와 패러다임의 불일치 해결
1. JPA 와 상속
2. JPA 와 연관관계
3. JPA 와 객체 그래프 탐색
4. JPA 와 비교하기
1. JPA 와 상속
왼쪽은 객체 상속 관계, 오른쪽은 Table 슈퍼타입 서브타입 관계이다.
앨범 정보를 저장하고 싶을 때, 다음과 같이 코드를 작성하면 끝이다.
jpa.persist(album);
그리고 나머지는 JPA 가 처리해준다. (INSERT INTO ITEM ..., INSERT INTO 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 와 연관관계
JPA 와 연관관계, 객체 그래프 탐색에 대해 알아보도록 한다.
우선 연관관계 저장은 다음과 같이 이뤄진다.
member.setTeam(team);
jpa.persist(member);
Member 클래스를 상속한 Team 클래스가 있으며 member 는 team 을 참조하기 때문에 setTeam() 을 해준다.
그리고 참조가 완료되면 저장을 수행한다.
3. JPA 와 객체 그래프 탐색
객체 그래프 탐색은 다음과 같이 이뤄진다.
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
우선 멤버 클래스로 memberId 를 통해서 member 객체를 받아온다.
그런데 member.getTeam() 을 하면 team 을 받아올 수 있는데 이는 find 과정에서 참조까지 고려하여
객체를 생성한 것으로 볼 수 있다. (마치 컬렉션에 넣었던 것처럼)
4. JPA 와 비교하기
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; // same!
동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다.
※ JPA의 성능 최적화 기능
1. 1차 캐시와 동일성(identity) 보장
2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
3. 지연 로딩(Lazy Loading)
① 1차 캐시와 동일성 보장
1. 같은 트랜잭션 안에서는 같은 엔티티를 반환한다. - 약간의 조회 성능을 향상
2. DB Isolation Level 이 Read Commit 이어도 애플리케이션에서 Repeatable Read 보장
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId); // SQL
Member member2 = jpa.find(Member.class, memberId); // Cache
println(member1 == member2) // 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(); // [트랜잭션] 커밋
처음에 트랜잭션 시작을 통해 이뤄진다.
em.persist() 에서 memberA, memberB, memberC 에 대한 INSERT SQL 을 모은다.
INSERT SQL 을 모았으면 데이터베이스에 커밋하는 순간에 보낸다.
③ 지연 로딩과 즉시 로딩
- 지연 로딩 : 객체가 실제 사용될 때 로딩
- 즉시 로딩 : JOIN SQL 로 한번에 연관된 객체까지 미리 조회
지연 로딩
Member member = memberDAO.find(memberId); // SELECT * FROM MEMBER
Team team = member.getTeam();
String teamName = team.getName(); // SELECT * FROM TEAM
member 객체를 가져올 때 SELECT * FROM MEMBER 만 수행이 된다.
그리고 member.getTeam() 을 통해 team 객체를 가져오고 team.getName() 을 하는 순간,
그 시점에 SELECT * FROM TEAM 을 수행함으로써 실제 사용될 때 로딩이 되는 것을 알 수 있다.
즉시 로딩
Member member = memberDAO.find(memberId); // SELECT M.*, T.* FROM MEMBER JOIN TEAM ...
Team team = member.getTeam();
String teamName = team.getName();
위 방식은 한방에 team 까지 같이 가져오며 즉시 로딩되는 것을 알 수 있다.
'JPA' 카테고리의 다른 글
다양한 연관관계 매핑 (0) | 2022.08.10 |
---|---|
연관관계 매핑 기초 (0) | 2022.08.10 |
엔티티 매핑 (0) | 2022.08.09 |
영속성 관리 - 내부 동작 방식 (0) | 2022.08.08 |
JPA 시작 (0) | 2022.08.01 |