본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.
JPA란?
- Java Persistence API의 준말
- 자바 진영의
ORM
기술 표준
ORM이란?
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
- 대중적인 언어에는 대부분 ORM 기술이 존재
JPA는 애플리케이션과 JDBC 사이에서 동작
개발자가 직접 JDBC API를 사용하는게 아닌 JPA를 사용하면 JPA가 JDBC API를 호출하는 방식이다.
JPA 동작 - 저장
예를 들어, MemberDAO에서 Member 객체를 저장하고 싶다하면 JPA가 Entity를 분석 후 Insert SQL 생성 후 JDBC API를 사용하여 DB에 저장한다. 중요한 건 쿼리를 개발자가 만드는게 아니고 JPA가 만들어준다는 것이다. 더 중요한건 이 패러다임의 불일치를 해결해준다는 것이다.
JPA 동작 - 조회
JPA가 Member객체를 보고 적절한 조회 쿼리를 만들고 JDBC API를 통해 DB로부터 결과를 받아온다. 그래서 ReesultSet 매핑을 수행 후 Entity Object를 반환한다. 여기서도 중요한건 패러다임의 문제를 해결해준다는 것이다.
JPA 소개
JPA의 역사
과거에도 ORM이 있었다. EJB라는게 있었고 엔티비 빈이라는게 자바표준으로 하고 있었다. 문제는 너무 기술이 아마추어적이여서 성능도 너무 안나오고 인터페이스도 엄청 많이 구현해야 했었다. 그래서 거의 잘 안쓰였다.
그러다 EJB를쓰던 외국의 SI 개발자(개빈 킹
)가 저녁에 퇴근하고 내가 만들어도 이것보단 낳겠다 해서 ORM 프레임워크(Hibernate)를 만들기 시작하였다. 이에 많은 사람들이 동참하고 오픈소스로 개발되기 시작되었다.
그러다 EJB가 망하고 Hibernate가 뜨게 된다. Java 진영에서 반성을 하고 개빈 킹을 그대로 잡아와서 Hibernate를 복사붙이기하다 싶이 만든게 JPA(자바 표준)
이다.
참고로, EJB의 불편함을 개선하려던 한 SI 개발자 덕분에 스프링 프레임워크도 탄생하게 되었다. EJB 의 문제점 덕분에 Spring 프레임워크와 JPA가 탄생하게 된 것이다.
JPA는 표준 명세
JPA는 인터페이스의 모음 이다. JPA 2.1 표준 명세를 구현한 3가지 구현체는 Hibernate
, 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), 엔티티 그래프 기능이 추가
Note: JPA 2.2를 사용할 거기에 왠만한 기능은 다된다고 보면 된다.
JPA를 왜 사용해야 하는가?
- 1)SQL 중심적인 개발에서 객체 중심으로 개발
- 2)생산성
- 3)유지보수
- 4)패러다임의 불일치 해결
- 5)성능
- 6)데이터 접근 추상화와 벤더 독립성
- 7)표준
1) SQL 중심적인 개발에서 객체 중심으로 개발
이전 강의 포스팅 을 참고하자.
2) 생산성 - JPA와 CRUD
- 저장: jpa.persist(member)
- 조회: Member member = jpa.find(memberId)
- 수정: member.setName(“변경할 이름”)
- 삭제: jpa.remove(member)
한 줄만 작성하면 되기에 좋은 생산성을 가질 수 있다. 뒤에 나오겠지만 JPA의 영속성 컨텍스트로 인해 컬렉션에서 단순하게 조회 후 setName만 호출하여 update 할 수 있다.
3) 유지보수 - 기존: 필드 변경시 모든 SQL 수정
기존 객체의 필드가 수정되면 쿼리도 일일이 수정해야만 한다. 하지만 JPA를 사용하면 필드를 수정하더라도 쿼리를 손댈 필요가 없다.
4) JPA와 패러다임의 불일치 해결
- 1.JPA와 상속
- 2.JPA와 연관관계
- 3.JPA와 객체 그래프 탐색
- 4.JPA와 비교하기
1. JPA와 상속
- 저장 시
- 개발자가 할일:
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와 연관관계
연관 관계 저장
1
2
member.setTeam(team);
jpa.persist(member);
3.JPA와 객체 그래프 탐색
객체 그래프 탐색
1
2
3
4
5
6
7
8
9
10
11
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); //자유로운 객체 그래프 탐색
member.getOrder().getDelivery();
}
}
JPA를 통해 객체를 가져온거면 자유롭게 객체 그래프를 탐색할 수 있다. 지연로딩을 통해 객체를 조회해서 사용하는 시점에 sql이 호출되서 데이터가 채워질 수 있다. 즉, JPA를 사용해서 가져온 객체는 신뢰하고 사용할 수 있게 된다.
4. JPA와 비교하기
1
2
3
4
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //같다.
JPA에서 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다.
5) JPA의 성능 최적화 기능
계층 사이에 중간 계층이 있으면 항상 모아서 쓰는 버퍼링과 캐슁하는 것을 할 수 있다. cpu나 메모리 구조도 마찬가지이다.
- 1)1차 캐시와 동일성(identity) 보장
- 2)트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
- 3)지연 로딩(Lazy Loading)
1. 1차 캐시와 동일성 보장
- 같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약간의 조회 성능 향상
- DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장
1
2
3
4
5
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) - UPDATE (버퍼링 기능)
- UPDATE, DELETE로 인한 로우(ROW)락 시간 최소화
- JDBC BATCH SQL 기능을 사용해서 한 번에 SQL 전송
1
2
3
4
5
6
transaction.begin(); // [트랜잭션] 시작
changeMember(memberA);
deleteMember(memberB);
비즈니스_로직_수행(); //비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않는다.
//커밋하는 순간 데이터베이스에 UPDATE, DELETE SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
3. 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading)
- 지연 로딩: 객체가 실제 사용될 때 로딩
- 실제 Team 객체의 필드를 접근할때 Team 테이블로부터의 조회 쿼리를 날려서 데이터를 얻어옴
- 즉시 로딩: JOIN SQL로 한번에 연관된 객체까지 미리 조회
- 한 번에 join을 활용하여 연관된 모든 데이터를 얻어옴
Note: Member를 사용할때 무조건 Team객체를 같이 사용한다? 그러면 즉시 로딩 옵션을 설정해주면 무조건 한 번에 같이 가져오게 할 수 있다. 하지만 어쩌다가 Team객체를 사용하다면 지연 로딩 옵션을 설정해주면 된다.
지연로딩을 기본으로 코드를 작성한 다음에 최적화가 필요할때만 최적화를 하는 방식으로 작성한다.
6) 데이터 접근 추상화와 벤더 독립성
7) 표준
정리
ORM은 객체와 RDB 두기둥위에 있는 기술이다. 객체와 RDB 둘 사이의 밸런스를 정말 잘맞춰야 한다. 둘 다 정말 잘할 줄 알아야 한다. 둘 중에 더 중요한걸 굳이 꼽자면 RDB다. RDB에서 데이터라는게 훨씬 오래 살아남는다. 꾸준히 관계형 DB에 대해 학습을 수행해야 한다.