✅JPA 소개

Java Persistence API : 자바 진영의 ORM 기술 표준

📢ORM이란

  • Object-relational mapping(객체 관계 매핑)
  • 객체는 객체대로 설계하고
  • 관계형 데이터베이스는 관계형 데이터베이스대로 설계한 뒤
  • ORM 프레임워크가 중간에서 이 둘을 매핑해준다
  • 대중적인 언어에는 대부분 ORM 기술이 존재

👏JPA의 동작

JDBC API : 자바를 이용한 데이터베이스 접속 (Connection), SQL문의 실행, 실행결과 데이터 핸들링을 제공하는 방법과 절차에 관한 규약이다.

MemberDAO에서 객체를 저장하기 위해서 Member 객체를 JPA에 넘긴다.
그럼 JPA가 Member 객체를 분석 후 적절한 INSERT query를 생성한다.
그 후 JDBC API를 사용하여 INSERT query를 DB에 보낸다.
DB에서 결과를 받는다.

Member의 pk값 (id)만 JPA에 넘기면
JPA가 Member 객체를 분석해 적절한 SELECT query를 만들어낸다.
이를 JDBC API를 사용하여 쿼리를 DB에 보낸 뒤 결과를 반환 받는다.
결과로 받은 ResultSet을 객체에 매핑해준다.
👉 이 번잡한 과정이 코드 한줄 find(id) 만으로 실행된다.

📌저장, 조회 과정에서 가장 눈여겨 봐야할 점은 JPA의 패러다임의 불일치 해결 능력이다


👏JPA 소개

EJB (과거의 자바 표준 ORM) : 성능이 매우 구림 👉
EJB에 열받은 개발자가 하이버네이트 (오픈소스)를 개발함 👉
자바 진영에서 하이버네이트를 만든 개발자를 불러 이와 비슷한 JPA(자바 표준)을 만들게 됨

JPA의 코드를 들여다 보면 인터페이스밖에 없다 (껍데기 스펙의 모음)
JPA의 스펙을 구현한 구현체가 여러 개 존재한다. (하이버네이트, EclipseLink, DataNucleus)
이 중 대부분이 하이버네이트 구현체를 사용한다.

👏JPA를 왜 사용해야 하는가?

  • SQL 중심적인 개발에서 객체 중심으로 개발
  • 생산성
  • 유지보수
  • 패러다임의 불일치 해결
  • 성능
  • 데이터 접근 추상화와 벤더 독립성
  • 표준

📌생산성 - JPA와 CRUD

  • 저장: jpa.persist(member)
  • 조회: Member member = jpa.find(memberId)
  • 수정: member.setName(“변경할 이름”)
  • 삭제: jpa.remove(member)

이미 CRUD에 대한 코드가 만들어져있다.
특히 setName 메소드를 사용하면 그냥 "변경할 이름"으로 이름이 변경되어 DB UPDATE query가 나간다.

📌유지보수

기존에는 객체의 필드 하나만 추가, 수정하게 되면 모든 쿼리문들도 같이 수정해줘야 하는 번거로움이 있었다.

JPA를 사용하게 되면 객체에 필드 하나만 추가하면 된다. 쿼리문을 따로 수정해줄 필요가 없다.

📌관계형 DB와 객체간의 패러다임의 불일치 해결

  1. JPA와 상속
  2. JPA와 연관관계
  3. JPA와 객체 그래프 탐색
  4. JPA와 비교하기

✍ 1. JPA와 상속

개발자가 Album객체를 저장하기 위해 persist(album); 메소드를 사용하면
JPA는 ITEM, ALBUM에 대한 INSERT QUERY를 쪼개서 만든다

개발자는 Album을 pk로 조회하기 위해 find(Album.class, albumId) 메소드를 사용하면
ITEM, ALBUM 테이블을 JOIN한 테이블로 Item, Album 객체에 데이터를 넣어준다.

✍ 2. JPA와 연관관계, 객체 그래프 탐색

member에 team와의 연관관계를 세팅(setTeam)하고 저장(persist)한다.

memberId로 member를 find하여 얻은 Member 객체는 연관관계를 가진 Team객체를 참조(getTeam)할 수 있다.
👉 마치 Java Collection에 넣었던 것 처럼 참조할 수 있다.

이제 JPA와 함께라면 Member 객체로 Team객체를 탐색하듯 객체 그래프 탐색을 자유롭게 활용할 수 있다.
👉 지연로딩을 통해 member.getTeam()으로 Team객체를 참조하는 시점에 SQL문이 생성되어 member와 연관관계가 있는 team의 정보를 가져온다.

✍ 3. JPA와 비교하기

JPA는 같은 트랜잭션에서 조회한 엔티티는 같음을 보장한다.

📌성능 최적화 기능

  1. 1차 캐시와 동일성(identity) 보장
  2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
  3. 지연 로딩(Lazy Loading)

두 개의 서로 다른 계층 사이의 중간 계층에서는 버퍼링(모아서 쏘기), 캐싱(읽기) 기능이 가능하다.
(cpu와 memory, 객체와 관계형 DB사이의 JPA, … )

✍ 1. 1차 캐시와 동일성(identity) 보장

📢캐싱기능 : 같은 트랜잭션 안에서는 같은 엔티티를 반환한다. (약간의 조회 성능 향상)

Member m1 : JPA가 SELECT query를 날려서 DB로부터 받은 데이터를 담고 있다.
Member m2 : 같은 pk값으로 find한 경우, JPA는 메모리에 있는 Member m1을 그대로 반환해준다
(캐싱을 한다)
👉 결과적으로 SQL이 한번만 실행된다.
👉 굉장히 짧은 시간동안의 캐싱이다. (하나의 트랜잭션이 시작후 끝날 떄 까지만 적용할 수 있는 캐싱 방법)

✍ 2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)

📢버퍼링기능 : JDBC BATCH SQL을 통한 JPA의 쓰기 지연 기능이다

memberA, memberB, memberC를 메모리에 쌓아 둔 뒤 commit 되는 순간 한번에 INSERT query를 보낸다.

✍ 3. 지연 로딩(Lazy Loading)

📢지연 로딩 : 객체가 실제 사용될 떄 로딩

memberDAO.find(memberId); 를 통해 Member 객체의 정보만 가져온다.
이 때, Team 객체는 아직 빈 껍데기 뿐인 프록시 객체이다.
Member 객체와 연관관계에 있는 Team 객체의 name속성을 건들 시점(team.getName();) 에서 JPA가 Team에 대한 SELECT query를 날려 Team 객체에 데이터를 채워준다.
👉 지연로딩의 문제 : query가 여러 번 나간다.

📢즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회

쿼리가 여러 번 나가는 지연 로딩의 문제점을 보완한다.
보통 개발할 때, Member 객체와 함께 Team 객체를 사용하기 때문에 memberDAO.find(memberId); 를 통해 Member와 Team 정보를 한번에 가져온다.
👉 JPA의 옵션을 바꾸면 지연 로딩에서 즉시 로딩으로 변경할 수 있다.

결론

ORM은 객체관계형 DB 두 기둥위에 있는 기술이다.

객체는 다른 언어로 변경될 수 있지만 관계형 DB는 더 오래유지된다
👉 관계형 DB에 대한 공부가 중요하다

0개의 댓글