꿈꾸는 새벽하늘

01장. JPA 소개 본문

🌿 Spring & Spring Boot/📗 자바 ORM 표준 JPA 프로그래밍

01장. JPA 소개

rovemin 2023. 5. 22. 17:00

1. SQL을 직접 다룰 때 발생하는 문제점

자바로 개발하는 애플리케이션은 대부분 가장 대중적이고 신뢰할 만한 안전한 데이터 저장소인 '관계형 데이터베이스'를 데이터 저장소로 사용한다. 이러한 데이터베이스에 데이터를 관리하려면 SQL을 사용해야 한다. 자바 애플리케이션은 JDBC API를 사용해서 SQL을 데이터베이스에 전달하는데 이렇게 SQL을 직접 다루면 아래와 같은 문제들이 발생하게 된다.

  • 반복되는 수많은 SQL과 JDBC API코드 작성
  • SQL에 의존적인 개발 - 진정한 의미의 계층 분할이 어렵고 엔티티 신뢰 불가

2. 패러다임의 불일치

도메인 모델을 객체로 모델링하면 객체지향 언어가 가진 장점들을 활용할 수 있다. 그러나 이를 저장하는 과정에서 문제가 발생하게 된다.

객체는 관계형 데이터베이스에 저장되는데 관계형 데이터베이스는 데이터 중심으로 구조화되어 있고, 객체지향의 추상화, 상속, 다형성 개념이 없다. 즉, 객체와 관계형 데이터베이스는 지향하는 목적이 다르므로 기능과 표현 방법도 다른데 이것을 객체와 관계형 데이터베이스의 패러다임 불일치 문제라고 일컫는다.

1) 상속

객체는 상속이라는 기능이 있지만, 테이블은 상속 기능이 없다.

 

  • JDBC API를 사용해서 상속받은 객체를 저장하려면 SQL 코드에 상속하는 객체와 상속받은 객체를 분해해서 모두 작성해주어야 한다.
INSERT INTO ITEM ...	-- 상속하는 객체 (부모클래스)
INSERT INTO MOVIE ...	-- 상속받은 객체 (자식클래스)
  • 상속받은 객체를 조회하는 경우에는 상속하는 테이블과 상속받은 테이블을 조인해서 조회한 뒤, 상속받은 객체를 보여준다.
SELECT	I.*, M.*
FROM	ITEM I
JOIN	MOVIE M ON I.ITEM_ID = M.ITEM_ID

JPA와 상속

// 저장
jpa.persist(movie);

// 조회
String movieId = "id100";
Movie movie = jpa.find(Movie.class, movieId);

2) 연관관계

객체는 참조를 사용해서 다른 객체와 연관관계를 갖고, 참조에 접근해서 연관된 객체를 조회한다.

테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 갖고, 조인을 사용해서 연관된 테이블을 조회한다.

 

즉, 연관관계를 맺기 위해 객체 모델은 참조만 있으면 되고, 테이블은 외래 키만 있으면 된다.

따라서 객체지향 모델링의 경우 개발자가 직접 연관관계를 설정해서 반환해야 한다.

JPA와 연관관계

// 저장
member.setTeam(team);	// 회원과 팀 연관관계 설정
jpa.persist(member);	// 회원과 연관관계 함께 저장

// 조회
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();

3) 객체 그래프 탐색

SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해진다.

어디까지 객체 그래프 탐색이 가능한지 알아보기 위해서는 데이터 접근 계층인 DAO에서 SQL을 직접 확인해야 한다. 그리고 상황에 따라 다수의 메소드를 만들어서 사용해야 한다.

JPA와 객체 그래프 탐색

JPA를 사용하면 객체 그래프를 원하는 대로 탐색할 수 있다.

member.getOrder().gertOrderItem()...	// 자유로운 객체 그래프 탐색

JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다.

따라서 JPA를 사용하면 객체를 신뢰하고 마음껏 조회할 수 있다. 이 기능은 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룬다고 하여 '지연 로딩'이라고 한다.

Member member = jpa.find(Member.class, memberId);	// 처음 조회 시점에 SELECT MEMBER SQL

Order order = member.getOrder();
order.getOrderDate();	// Order를 사용하는 시점에 SELECT ORDER SQL

4) 비교

데이터베이스는 기본 키의 값으로 각 row를 구분한다.

객체 비교는 동일성(identity) 비교와 동등성(equality) 비교의 두 가지 방법이 있다.

  • 동일성 비교: == 비교. 객체 인스턴스의 주소 값 비교
  • 동등성 비교: equals() 메소드를 사용하여 객체 내부의 값 비교

아래는 객체의 동일성 비교의 예시이다. 비교 결과 false가 반환되는 이유는 member1과 member2는 같은 데이터베이스 row에서 조회되었지만, 객체 측면에서는 각각 다른 인스턴스이기 때문이다.

String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);

member1 == member2;	// false

JPA와 비교 

JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장하므로 member1과 member2의 동일성 비교는 성공하게 된다.

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

member1 == member2;	// true

3. JPA란 무엇일까?

JPA(Java Persistence API)는 자바 ORM 기술에 대한 API 표준 명세이고, 애플리케이션과 JDBC 사이에서 동작한다.

JPA를 사용하기 위해서는 JPA를 구현한 ORM 프레임워크를 선택해야 하는데 '하이버네이트'가 가장 대중적이다.

 

ORM(Object-Relational Mapping)은 객체와 관계형 데이터베이스를 매핑하는 것이다.

ORM 프레임워크를 사용하면 객체를 데이터베이스에 저장할 때 INSERT SQL을 직접 작성하는 것이 아니라 객체를 자바 컬렉션에 저장하듯이 ORM 프레임워크에 저장하면 된다. 그러면 ORM 프레임워크가 적절한 INSERT SQL을 생성해서 데이터베이스에 객체를 저장해준다.

jpa.persist(member);	// 저장

Member member = jpa.find(memberId);	// 조회

JPA를 사용해야 하는 이유

  1. 생산성
  2. 유지보수
  3. 패러다임의 불일치 해결
  4. 성능
  5. 데이터 접근 추상화와 벤더 독립성
  6. 표준 - JPA는 자바 진영의 ORM 기술 표준이다.