본문 바로가기

Dev/JPA

JPA의 등장

객체를 마치 자바 컬렉션에 저장하고 불러오듯이 관계형 데이터베이스에 저장하고 불러올 수 있는 방법이 없을까?

고민 끝에 등장한 것이 JPA 이다.


- JPA(Java Persistence API)

- 자바 진영의 ORM 기술 표준

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

즉, 객체지향 개발자는 객체지향스럽게 개발하고 관계형 데이터베이스는 관계형 데이터베이스 답게 처리 진행

나머지는 ORM 프레임 워크한테 맡긴다!


 

JPA의 위치 및 역할

  1. JPA는 자바 애플리케이션과 JDBC 사이에서 동작한다.
  2. 개발자는 JDBC API를 직접 사용하는 것이 아닌 JPA에게 명령한다.
  3. JPA는 개발자의 명령에 따른 적절한 JDBC API를 사용하여 SQL을 호출하고 결과를 받는다.

JPA의 동작 / 입력

  1. MemberDAO에서 객체를 저장하고 싶을 경우 JPA에게 멤버객체를 넘겨준다.
  2. JPA는 분석 후에 INSERT 쿼리를 생성한다.
  3. JDBC API를 사용해서 쿼리를 디비에 보내고 그 결과값을 받아준다.

JPA의 동작 / 조회

  1. 조회할 때에도 마찬가지로 MemberDAO는 JPA에게 PK값만 보내고 조회 값을 요구한다.
  2. JPA는 요구사항(멤버객체)을 받고 SELECT 쿼리를 만들어서 JDBC API를 사용하여 DB에 보내고 결과값을 받는다.
  3. 결과값을 받아서 객체에다 전부 매핑해준다. 

JPA의 표준 명세

- JPA는 인터페이스의 모음

- JPA 2.1 표준 명세를 구현한 3가지 구현체

- JPA 2.0 버전 이후로는 왠만한 기능은 다 되니 2.0 이상 버전을 가져다가 사용 권함

- 하이버네이트, EclipseLink, DataNucleus 

쉽게 설명해서 어플리케이션에서 JPA를 사용하려면 Hibernate, EclipseLink, DataNucleus에서 구현을 할 수 있다

JPA를 쓰려면 세가지 방법에 구축되어있으니 가져다가 사용해라! 이말이다.

하지만 요즘은 대부분 Hibernate를 사용하니 별다른 이유없다면 Hibernate를 써서 사용하자!!


JPA를 왜 사용해야 하는가?

- 생산성적인 측면 (JPA와 CRUD) / JPA에서 다 만들어져있으니 그냥 가져다가 써라 이말이다!

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

- 유지보수적인 측면 / tel이라는 멤버변수가 추가되는 상황에서 필드만 추가하면, SQL은 따로 변경할 필요 없이 JPA가 알아서 처리해준다.

public class Member {
	private String memberId;
    private String name;
    private String tel;
    ...
    }


JPA와 패러다임의 불일치 해결

- JPA와 상속

객체 상속관계[왼쪽] / Table 슈퍼타입 서브타입 관계[오른쪽]

  • 오른쪽 그림과 같이 테이블 관계를 정규화하게 설계하여 나타내고 있다.

- 앨범 객체를 디비에 저장하고 싶을 경우 (저장)  - 앨범을 조회하고 싶을 경우 (조회)
- 연관관계 및 객체 그래프 탐색 방법  

- persist를 통해 member를 넣어줬을 때
- 자바 컬렉션에 넣은것 처럼 member.getTeam()을 통해 가져온다.

 

- JPA와 비교하기

String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);

member1 == member2; // 같다.

// 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장

JPA의 성능 최적화 기능

JPA를 사용하면 성능이 더 떨어지지 않을까?

  • 계층 사회에 어떤 하나의 중간 계층이 존재하면 읽어드리는 과정에서 한번에 모아서 쏘는 버퍼링과 읽을 때 캐싱하는 것을 할 수 있다.
  • JPA에서도 JPA가 중간계층 역할을 하기 때문에 이런 부분을 최적화 하므로 단순히 SQL 쓰는거보다 성능을 끌어올릴 수 있다. 

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

  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

//	JPA는 처음에 쿼리가 날라가서 m1을 가져오고 그걸 캐싱하여 가지고 있다가
//  두번째 동일한 PK 값이 들어가면 메모리에 첫번째 m1값을 반환해준다.

 

- 트랜잭션을 지원하는 쓰기 지연 (버퍼링 기능)

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

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
// JPA는 INSERT SQL을 데이터베이스에 보내지 않고 메모리에 보관하고 있는다.

//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); 트랜잭션 커밋

 

- 지연 로딩과 즉시 로딩

  • 지연 로딩 : 객체가 실제 사용될 때 로딩
  • 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
  • JPA에는 옵션이 있어서 지연 로딩 형태로 가져올 것인지 즉시 로딩 형태로 가져와서 쓸껀지 껐다 켤 수 있다.

Member를 사용할 때는 거의 Member만 쓰고 Team은 어쩌다가 사용한다? 할 경우 지연 로딩 방법처럼 사용

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

SELECT * FROM TEAM

 

99%가 Member 를 조회하면 Team 은 같이 사용하는 경우 즉시 로딩방법으로 사용

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

결국 지연로딩인지 즉시 로딩인지의 여부는 JPA에서는 스위치처럼 옵션을 껐다 켰다 함으로써 컨트롤을 할 수 있다! 라는 의미


TIP

어플리케이션을 개발할 때에는 지연 로딩 형태로 쭈욱 개발해 놓은 다음에 최적화가 필요한 경우에만 딱 맞춰서 하는 방식이 좋다!

JPA를 공부하더라도 / 관계형 데이터베이스는 중요하니 꾸준하게 공부해야한다!

 

 

'Dev > JPA' 카테고리의 다른 글

다양한 연관관계 매핑  (0) 2020.07.28
연관관계 매핑 기초  (0) 2020.07.15
엔티티 매핑  (0) 2020.07.12
JPA내부 구조(영속성 관리)  (0) 2020.07.06
SQL중심개발의 문제점  (0) 2020.06.30