스터디/JPA 스터디

[JPA] JPA 소개

제이온 (Jayon) 2021. 11. 28.

jpa-study에서 스터디를 진행하고 있습니다.

 

자바로 개발하는 애플리케이션은 대부분 관계형 데이터베이스를 사용하며, 해당 데이터베이스에서 데이터를 관리하려면 SQL을 사용해야 한다.

 

 

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

엔티티를 신뢰할 수 없다.

Member나 Team처럼 비즈니스 요구 사항을 모델링한 객체를 엔티티라 하는데, SQL에 모든 것을 의존하는 경우 개발자들은 엔티티를 신뢰할 수 없다. 이 이유에 대해서는 2번째 문제점을 살펴 보자.

 

진정한 의미의 계층 분할이 아니다.

개발자는 수정 사항이나 버그가 생겼을 경우 DAO를 열어서 어떤 SQL이 실행되고 어떤 객체들이 함께 조회되는지 일일이 확인해야 한다. 가령, 도메인 객체의 변화가 생겼음에도 불구하고 DAO를 확인할 수 밖에 없으므로 진정한 의미의 계층 분할이라고 할 수 없다.

 

SQL에 의존적인 개발을 피하기 어렵다.

물리적으로 SQL과 JDBC API를 데이터 접근 계층 (DAO)에 숨기는 데 성공했을지라도 논리적으로는 엔티티와 아주 강한 의존 관계를 지니고 있다. 이런 강한 의존 관계때문에 회원을 조회할 때는 물론이고 회원 객체에 필드를 하나 추가할 때도 DAO의 CRUD와 SQL 대부분을 변경해야 한다. 이것은 SOLID 원칙 중에 개방 폐쇄 원칙을 위반하는 사례다.

아래에서 자세히 이야기하겠지만, JPA는 위의 문제를 해결할 수 있다. JPA를 사용하면 객체를 데이터베이스에 저장하고 관리할 때, 개발자가 SQL을 작성하지 않고 JPA가 제공하는 API를 사용한다. 그러면 JPA가 개발자 대신에 적절한 SQL을 생성해서 데이터베이스에 전달한다.

 

패러다임의 불일치

객체 지향과 달리 관계형 데이터베이스는 데이터 중심으로 구조화되어 있고, 집합적인 사고를 요구한다. 그리고 객체 지향에서 이야기하는 추상화, 상속, 다형성 같은 개념이 없다. 두 개념이 지향하는 목적이 다르므로 둘의 기능과 표현이 다르고, 여기서 패러다임 불일치가 발생한다.

 

상속

 

객체는 위와 같이 상속의 기능을 지니지만 테이블은 상속의 기능이 없다. 그나마 데이터베이스 모델링에서 이야기하는 슈퍼 타입과 서브 타입 관계를 사용하면 객체 상속과 유사한 형태로 테이블을 설계할 수는 있다.

 

 

Item 테이블의 DTYPE 컬럼을 사용하면 어떤 자식 테이블과 관계가 있는지 정의할 수 있다.

 

abstract class Item {
    Long id;
    String name;
    int price;
}

class Album extends Item {
    String artist;
}

class Movie extends Item {
    String director;
    String actor;
}

class Book extends Item {
    String author;
    String isbn;
}

 

위는 앞서 말한 슈퍼 타입, 서브 타입을 이용한 데이터베이스에 맞춰 설계한 객체 모델 코드이다. 만약 Album을 저장하려면 이 객체를 분해 해서 다음 두 SQL을 만들어야 한다.

 

INSERT INTO ITEM ...
INSERT INTO ALBUM ...

 

JDBC API를 이용해서 코드를 작성하려면 부모 객체에서 부모 데이터를 꺼내고, 자식 객체에서 자식 데이터를 꺼내서 따로 SQL을 작성해야하므로 작성해야 할 코드가 많다.

JPA는 이와 달리 상속과 관련된 패러다임의 불일치 문제를 개발자 대신 해결해 준다. 개발자는 자바 컬렉션에 객체를 저장하듯이 JPA에게 객체를 저장하면 된다.

 

연관 관계

객체는 참조를 사용해서 다른 객체와 연관 관계를 가지고 참조에 접근해서 연관된 객체를 조회한다. 반면에 테이블은 외래 키를 사용해서 다른 테이블과 연관 관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.

 

 

객체 연관 관계의 경우 member.getTeam() 으로 참조가 가능하지만 반대로 team.getMember() 는 불가능하다. 반면 테이블은 어느 쪽에서든 조인을 사용할 수 있다.

 

class Member {
 String id; //MEMBER_ID 컬럼 사용
 Long teamId; //TEAM_ID FK컬럼 사용
 String username; //USERNAME 컬럼 사용
}

class Team {
 Long id; //TEAM_ID PK사용
 String name; //NAME 컬럼 사용
 }

 

위와 같이 객체를 테이블에 맞추어 모델링할 수 는 있으나, Team 객체를 참조를 사용하여 조회할 수가 없다. 결국 객체 지향의 특징을 잃어버리게 되는 것이다.

요약하자면 객체 모델은 Team을 객체로, 데이터베이스는 Team을 TEAM_ID로 저장한다.

JPA는 연관 관계와 관련된 패러다임 불일치를 해결해 준다.

 

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

 

개발자는 단순히 회원과 팀의 관계를 설정하고 저장하기만 하면 된다. JPA는 Team의 참조를 외래 키로 변환해 적절한 INSERT SQL문을 데이터베이스로 전달해 주기 때문이다.

 

객체 그래프 탐색

객체에서 회원이 소속된 팀을 조회할 때 다음처럼 참조해서 사용하면 연관된 팀을 찾을 수 있다.

 

 

Team team = member.getTeam();

 

해당 코드와 같이 객체는 마음껏 객체 그래프를 탐색할 수 있다. 그런데 데이터베이스에서는 객체를 조회할 때 Member와 Team의 데이터만 조회했다면 member.getOrder() 은 null이 된다.

 

SELECT M.*, T.*
    FROM MEMBER M
    JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

 

SQL을 직접 다루면 처음 실행하는 SQL문에 따라 객체 그래프의 탐색이 한정된다.

 

class MemberService {
  ...
  public void process() {

    Member member = memberDAO.find(MemberId);
    member.getTeam(); //member-> team 객체 그래프 탐색이 가능한가?
    member.getOrder().getDelivery(); //???이게 가능할지 확신할 수 있는가?

 

위의 코드만 가지고 객체 그래프를 어디까지 탐색할 수 없으므로 매번 MemberDAO에는 상황에 따라 메소드가 늘어난다.

 

memberDAO.getMember();
memberDAO.getMemberWithTeam();
memberDAO.getMemberWithOrderWithDelivery();

 

JPA는 객체 그래프를 마음껏 탐색한다. JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL문을 실행하며, 이를 lazy loading이라고 한다.

 

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

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

 

비교

데이터베이스는 기본 키의 값으로 row를 구분한다. 반면에 객체는 동일성 비교와 동등성 비교라는 두 가지 비교 방법이 있다.

 

  • 동일성 비교는 == 비교이며, 객체 인스턴스의 주소 값을 비교한다.
  • 동등성 비교는 equals() 메소드를 사용해서 객체 내부의 값을 비교한다.

 

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

member1 == member2; // false

 

같은 ID로 데이터를 조회했을 때 두 객체가 다르다. 같은 row에서 조회했지만 객체측 면에서 둘은 다른 인스턴스이다. getMember를 호출할 때마다 새로운 인스턴스를 만들기 때문이다. 만약 객체를 컬렉션에 보관했다면 동일성 비교에 성공했을 것이다.

JPA는 같은 트랙잭션일 때 같은 객체가 조회되는 것을 보장한다.

 

정리

객체 모델과 관계형 데이터베이스 모델은 지향하는 패러다임이 서로 다르다. 이 차이를 극복하려고 개발자가 너무 많은 시간과 코드를 소비한다.

 

JPA란 무엇인가?

JPA (Java Persistence API)는 자바 진영의 ORM 기술 표준이다. JPA는 아래와 같이 애플리케이션과 JDBC 사이에서 동작한다.

 

 

ORM이란?

ORM (Object-Relational Mapping)은 이름 그대로 객체와 관계형 데이터베이스를 매핑한다는 뜻이다. 즉 ORM 프레임워크는 위에서 이야기한 패러다임 불일치를 해결해 준다.

 

JPA 소개

과거 자바 진영은 엔터프라이즈 자바 빈즈라는 기술 표준을 만들었으나, 내장 ORM이 다루기 복잡하고, 기술 성숙도가 떨어졌다. 그래서 많은 개발자들이 하이버네이트 오픈 소스를 사용하였고, 결국 자바 진영은 이를 받아 들여서 새로운 ORM 기술 표준인 JPA를 만들었다.

 

 

JPA는 자바 ORM 기술에 대한 API 표준 명세이며, 인터페이스라고 생각하면 된다. 따라서 JPA를 사용하기 위해서 JPA를 구현한 ORM 프레임워크를 선택해야 하는데, 주로 하이버네이트를 사용한다.

 

JPA를 사용해야 하는 이유

생산성

  • 자바 컬렉션에 객체를 저장하듯이 JPA에게 저장할 객체를 전달하면 된다. SQL을 작성하고 JDBC API를 사용하는 반복 행위는 JPA가 대신해 준다.

 

유지 보수

  • 엔티티에 필드를 추가하거나 삭제해도 수정해야 할 코드가 줄어 든다. 특히 이전에 작성하던 SQL과 JDBC API를 JPA가 대신 처리해 준다.

 

패러다임 불일치 해결

  • JPA는 상속, 연관 관계, 객체 그래프 탐색, 비교 등 패러다임의 불일치를 해결해 준다.

 

성능

JPA는 애플리케이션과 데이터베이스 사이에서 동작하므로 최적화 관점에서 시도해 볼 수 있는 것이 많다.

 

String memberId = "helloId";
Member member1 = jpa.find(memberId);
Member member2 = jpa.find(memberId);

 

위는 같은 트랜잭션 안에서 같은 회원을 두 번 조회하는 코드다. JDBC API를 사용해서 해당 코드를 작성했다면, 회원을 조회할 때마다 SELECT SQL문을 활용해서 데이터베이스와 통신했을 것이다. 하지만 JPA는 SELECT SQL문을 한 번만 데이터베이스에 전달하고 두 번째는 조회한 회원 객체를 재사용한다.

 

데이터 접근 추상화와 벤더 독립성

 

JPA는 데이터 접근 계층을 제공해서 애플리케이션이 특정 데이터베이스 기술에 종속되지 않도록 한다.

 

표준

JPA는 자바 진영의 ORM 기술 표준이므로 다른 구현 기술로 손쉽게 변경이 가능하다.

 

출처

김영한 - 자바 ORM 표준 JPA 프로그래밍

 

JPA는 무엇인가?

JPA (Java Persistence API)는 자바 진영의 ORM 기술 표준이다. 여기서 ORM (Object-Relational Mapping)은 이름 그대로 객체와 관계형 데이터베이스를 매핑한다는 뜻이다. ORM 프레임워크는 패러다임 불일치를 해결해 준다는 장점이 있다.

 

JPA를 왜 사용하는가?

이 부분은 경험 위주로 답하는 것이 좋다.

 

  • 패러다임의 불일치 해결 : 객체 지향에서는 상속, 다형성, 참조 등과 같은 개념이 있지만, 데이터베이스에는 그러한 개념이 없다. 이러한 패러다임이 일치하지 않는 상황에서 객체를 데이터베이스에 저장해야 한다. 이 때문에 객체 지향적으로 코드를 설계하지 못하고 테이블에 맞게 객체를 설계해야 하는데, 이러한 패러다임의 불일치를 JPA가 해결해 준다.
  • 생산성 : 반복적인 SQL을 개발자가 직접 작성하지 않아도 되므로 생산적인 측면에서 장점이 있다.

'스터디 > JPA 스터디' 카테고리의 다른 글

[JPA] 고급 매핑  (0) 2021.12.15
[JPA] 다양한 연관 관계 매핑  (0) 2021.12.11
[JPA] 연관 관계 매핑 기초  (0) 2021.12.09
[JPA] 엔티티 매핑  (0) 2021.12.04
[JPA] 영속성 관리  (0) 2021.11.28

댓글

추천 글