JPA란

황세호·2021년 4월 10일
0

JPA

목록 보기
1/3

객체 vs 관계형 데이터베이스

객체 지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등의 개념이 있지만 관계형 데이터베이스는 그렇지 못하다.

객체와 관계형 데이터베이스의 차이는 다음과 같이 크게 4가지로 볼 수 있다.

  • 상속
  • 연관관계
  • 데이터 타입
  • 데이터 식별 방법

상속

위와 같은 객체 상속 관계가 있을 때 각 물품을 조회하는 상황을 가정하자.
조회를 위해서는 각각의 테이블에 따라 다른 조인 SQL을 작성해야하며, 객체 또한 각각 생성해야 한다. 그래서 DB에 저장할 객체에는 상속 관계를 쓰지 않는다.

연관관계

  • 객체는 참조를 사용한다. (ex : member.getTeam())
  • 테이블은 외래 키를 사용한다. (ex : JOIN ON M.TEAM_ID = T.TEAM_ID)

객체 연관관계는 참조를 사용할 시 Member에서 Team을 참조하는 것은 가능하지만 Team에서 Member를 읽는 것은 불가능 하다. 반면,

테이블은 연관관계에서는 외래키로 참조가 가능하다.

class Member{
    Long id;
    Long teamId;
    String username;
}

위와 같이 테이블에 맞춰 객체를 저장하여도 teamId로는 아무 참조를 할 수가 없다.
결국 객체답게 모델링을 진행 할수록 개발자들의 매핑 작업만 늘어나는 결과를 초래한다. 이럴 때 사용하기 위해 나온 것이 JPA(Java Persistence API)다.

JPA란?

  • Java Persistence API
  • 자바 진영의 ORM 기술 표준이다.
  • 인터페이스의 모음이며 하이버네이트, EclipseLink, DataNucleus의 구현체를 가지고 있다.

ORM?

  • Object-relational mapping(객체 관계 매핑)
  • 객체는 객체대로 설계하고 관계형 데이터베이스는 관계형 데이터베이스대로 설계
  • ORM 프레임워크가 중간에서 매핑

JPA는 애플리케이션과 JDBC 사이에서 동작

JPA를 왜 사용해야 하는가?

1. SQL 중심적인 개발에서 객체 중심으로 개발

2. 생산성

  • 저장 : jpa.persist(member)

  • 조회 : Member member = jpa.find(memberId)

  • 수정 : member.setName("변경할 이름") -> 변경감지(Dirty Checking)

  • 삭제 : jpa.remove(member)

3. 유지보수

  • 기존 : 필드 변경 시 모든 SQL을 직접 수정해야 했다.

  • JPA : 필드만 추가하면 된다. SQL은 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와 연관관계
member.setTeam(team);
jpa.persist(member);
3) JPA와 객체 그래프 탐색
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
class MemberService {
    ...
    public void process() {
       Member member1 = memberDAO.find(memberId); 
       member1.getTeam(); // 엔티티를 신뢰할 수 없음
       member1.getOrder().getDelivery();
       
       Member member2 = jpa.find(Member.class, memberId);
       member2.getTeam(); // 자유로운 객체 그래프 탐색
       member2.getOrder().getDelivery();
    }
}
  • DAO를 통해 객체를 가져올 경우
    • DAO를 직접 확인하여 SQL을 확인하지 않는 이상 원하는 객체를 잘 가져온건지 확인할 수 없다.
    • 즉, 신뢰할 수 있는 엔티티, 계층이 아니다.
  • JAP를 통해 객체를 가져올 경우
    • 지연 로딩 전략을 사용해 실제로 객체를 조회하는 시점에 SQL에 채워진다.
4) JPA와 비교하기
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId); // DB에서 가져옴
Member member2 = jpa.find(Member.class, memberId); // 1차 캐시에서 가져옴

member1 == member2; //같다
  • 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다.
5) JPA의 성능 최적화 기능
  • 1차 캐시와 동일성 보장

    • 같은 트랜잭션 안에서는 같은 엔티티를 반환 -> 약간의 조회 성능 향상
    • DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장
  • 트랜잭션을 지원하는 쓰기 지연

    • 트랜잭션을 커밋할 때까지 INSERT SQL을 모음
    • JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
transaction.begin() // 트랜잭션 시작

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
// 아직 Commit되지 않았기에 SQL로 보내지 않는다.

transaction.commit(); // 커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
  • 지연 로딩과 즉시 로딩
    • 지연 로딩 : 객체가 실제 사용될 때 로딩
    • 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
// 지연 로딩

Member member = memberDAO.find(memberId); // SELECT * FROM MEMBER
Team team = member.getTeam();
String teamName = team.getName(); // SELECT * FROM TEAM

지연 로딩은 위와 같이 team 객체를 실제로 사용할 때 쿼리문이 나간다. 결국 Member와 Team 객체 따로 조회하기 때문에 DB 접근을 두번 하게 된다. (만약, Member 객체를 사용할 때 Team 객체 사용 빈도수가 많다면 즉시 로딩을 사용하는 것이 나음)

// 즉시 로딩

Member member = memberDAO.find(memberId); // SELECT * FROM M.*, T.*
Team team = member.getTeam();             // FROM MEMBER
String teamName = team.getName();         // JOIN TEAM ...

즉시 로딩은 JOIN을 이용해 객체를 동시에 가져온다. 실무에서는 지연 로딩으로 개발을 한 후에, 성능 최적화에 필요하다 판단 시에 즉시 로딩으로 옵션을 추가로 주는 것을 권장한다.

REFERENCE


인프런 자바 ORM 표준 JPA 프로그래밍

profile
Developer

0개의 댓글