JPA?
- Java Persistence API
- 자바 진영의 ORM 기술 표준
ORM?
- Object-Relational Mapping(객체 관계 매핑)
- 객체는 객체대로 설계, 관계형 데이터베이스느 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
- 대중적인 언어에는 대부분 ORM 기술이 존재
JPA는 애플리케이션과 JDBC 사이에서 동작함
개발자가 직접 JDBC를 사용하는 것이 아니고
JPA에게 명령을 하면 JPA가 JDBC를 사용해서 SQL을 호출하고 그 결과를 받아 동작함
JPA 동작 - 저장
1. 객체를 넘김
2. JPA가 객체를 분석
3. JPA가 적절한 쿼리 생성
4. JPA가 JDBC API를 사용해서 쿼리를 DB에 보내고 결과를 받아옴
5. 이로써 패러다임 불일치 해결해줌
JPA 동작 - 조회
1. PK값 가지고 적절한 SQL 생성(매핑 정보 바탕으로 생성)
2. JDBC API 사용해서 DB에 보냄. 결과를 받은 후
3. ResultSet을 가지고 객체에 매핑해줌
4. 패러다임 불일치 해결해줌
JPA는 표준 명세이다
- JPA는 인터페이스의 모음
- JPA 2.1 표준 명세를 구현한 3가지 구현체 : 하이버네이트, EclipseLink, DataNucleus
JPA 버전
- JPA 1.0(JSR 220) 2006년 : 초기 버전. 복합 키와 연관관계 기능이 부족
- JPA 2.0(JSR 317) 2009년 : 대부분의 ORM 기능을 포함, JPA Criteria 추가
- JPA 2.1(JSR 338) 2013년 : 스토어드 프로시저 접근, 컨버터(Converter), 엔티티 그래프 기능이 추가
JPA를 왜 사용해야 하는가?
- SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성
- 유지보수
- 패러다임의 불일치 해결
- 성능
- 데이터 접근 추상화와 벤더 독립성
- 표준
생산성 - JPA와 CRUD
• 저장: jpa.persist(member)
• 조회: Member member = jpa.find(memberId)
• 수정: member.setName(“변경할 이름”)
• 삭제: jpa.remove(member)
그냥 함수를 불러 쓰면 알아서 해줌!
수정 시 setter 함수를 사용하면 알아서 값을 바꿔줌
JPA는 자바 컬렉션을 사용하는 것처럼 만든 것임
'컬렉션에서 데이터를 불러오고 setter함수로 값을 바꾼다'
이것과 같은 원리임
유지보수 - 기존 : 필드 변경 시 모든 SQL 수정
기존에는 필드 변경 시 모든 SQL을 수정해야만 했다
하지만 JPA를 사용한다면
유지보수 - JPA : 필드만 추가하면 됨, SQL은 JPA가 처리
SQL 수정할 필요 없이 클래스에 필드만 추가해주면 끝!
SQL은 어차피 JPA가 알아서 만듦
JPA와 패러다임의 불일치 해결
1. JPA와 상속
2. JPA와 연관관계
3. JPA와 객체 그래프 탐색
4. JPA와 비교하기
JPA가 대단한 것은 관계형 데이터베이스와 객체 사이의 불일치를 해결해줌
JPA와 상속
객체의 상속을 따라 구현하기 위해 DB의 슈퍼타입 서브타입 관계를 지어 테이블을 설계함
JPA와 상속 - 저장
개발자가 할 일 :
jpa.persist(album);
개발자가 이렇게만 쓰면
JPA가 하는 일 :
INSERT INTO ITEM...;
INSERT INTO ALBUM...;
JPA는 SQL을 각각 쪼개서 날린다!
JPA와 상속 - 조회
개발자가 할 일 :
Album album = jpa.find(Album.class, albumId);
개발자가 이렇게만 써도
JPA가 하는 일 :
SELECT I.*, A.*
FROM ITEM I
JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
JPA가 알아서 조인해서 가져온다!
객체지향 프로그래밍과 RDB 사이의 안 맞는 부분을 JPA가 해결해줌
JPA와 연관관계, 객체 그래프 탐색
연관관계 저장 :
persist : 영구저장하다
member.setTeam(team);
jpa.persist(member);
객체 그래프 탐색 :
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
객체를 가져온뒤 getter 함수 써서 데이터를 가져올 수 있다
마치 자바 컬렉션을 사용하는 것처럼!
신뢰할 수 있는 엔티티, 계층
이전에 설명했을 때는 신뢰할 수 없었음
어떤 쿼리가 날라갔는 지 직접 봐야 신뢰할 수 있던 상황이였지만
JPA를 사용했다고 가정한다면, 이 코드는 신뢰할 수 있음
객체 그래프를 자유롭게 탐색할 수 있음
JPA에는 지연로딩 이라는 기능이 있어서,
get 함수를 사용하는 시점에 SQL을 조회해오는 기능이 있음
JPA와 비교하기
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //같다.
자바 컬렉션을 사용하는 것처럼 '==' 비교를 하면 동일하다고 나옴
동일한 트랜잭션에서 조회한 엔티티는 같음을 보장해줌
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 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(); // [트랜잭션] 커밋
실행해야 할 쿼리를 모아서 한꺼번에 실행하는 기능이 있는데, 그것을 JDBC BATCH 라고 함
이걸 쓰게 된다면 코드가 지저분해짐
JPA를 쓴다면 옵션만 키면 SQL을 모아서 한번에 보낼 수 있음
DB는 커밋하기 전에만 SQL을 실행하면 됨
JPA는 위의 코드처럼 member A B C가 들어오면 쌓아놓았다가
commit되는 순간 한꺼번에 SQL을 실행함
지연 로딩과 즉시 로딩
지연 로딩 : 객체가 실제 사용될 때 로딩
즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
데이터베이스에서 Member만 조회해옴
Team 객체가 필요할 시점에 실행할 때 그 떄 데이터베이스에서 조회해옴
지연로딩은 쿼리가 2번 실행됨
네트워크를 2번 탐
개발하다보니 Member 조회시 거의 다 Team도 같이 조회해야하는 상황이라면
한번의 쿼리로 Member와 Team 다같이 조회해옴(JOIN을 이용)
지연로딩/즉시로딩 옵션 설정을 통해 적용할 수 있음
ORM은 객체와 RDB 두 기둥 위에 있는 기술이다
JPA만 안다고 잘 할 수 있는게 아님
RDB만 잘 안다고 잘 할 수 있는 것도 아님
둘 다 잘해야 함
그래도 굳이 중요한 것을 꼽자면 RDB이다
RDB를 꾸준하게 공부해야 함
'JPA' 카테고리의 다른 글
JPA) 어플리케이션 개발 (0) | 2022.01.30 |
---|---|
JPA) 프로젝트 생성 (0) | 2022.01.29 |
JPA) SQL 중심적인 개발의 문제점 (0) | 2022.01.20 |