자바 ORM 표준 JPA 프로그래밍 - 기본편 #1 JPA 소개-첫번째

Lee Han Sol·2021년 9월 24일
0
post-thumbnail

자바 ORM 표준 JPA 프로그래밍 - 기본편

이 글은 김영한님의 Inflearn 강의를 학습한 내용을 정리하였습니다.

Java-Spring을 이용해 개발하면 ORM으로 JPA를 많이 사용합니다.
제가 사용하는 이유는 유행하는 기술 스택이라서, 문제 해결을 위한 검색이 편해서 등이 있습니다.
일단 사용하기로 했으니

  1. JPA가 해결해주는 문제점이 무엇이고
  2. 사용했을 때 어떤 이점이 있는지
  3. 잘 사용하려면 어떻게 해야될지

정도는 알아야겠다고 생각해서 공부를 시작합니다.

목표

이번 글에서는 SQL 중심적인 개발의 문제점과 객체와 관계형 데이터베이스의 차이에 대해서 알아본다.
그리고 JPA가 위 차이에서 발생하는 문제들을 어떻게 해결하는지 간단하게 알아본다.

SQL 중심적인 개발의 문제점

발생 문제

  • CRUD 쿼리 작성, 수정의 무한 반복
  • 자바 객체 ↔ SQL

JPA를 통한 CRUD 작성, 객체-SQL 해결

JPA를 사용하면 객체에 해당하는 CRUD SQL을 직접 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면 된다.
JPA는 객체에 매핑정보를 보고 적절한 SQL을 생성,실행 및 객체를 반환(조회)한다.

패러다임의 불일치

객체와 관계형 데이터베이스의 차이점은 아래와 같다.

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

이와 같은 차이 때문에 발생하는 문제점과 JPA를 통한 해결방법에 대해서 알아보자.

상속

객체는 명확하게 상속 관계가 존재한다.
RDB에는 슈퍼타입-서브타입 관계가 존재한다. (상속과 유사한 개념이지 상속은 아니다.)

객체 지향의 상속과 이를 ERD로 표현한 예를 그림으로 표현하면 아래와 같다.

발생 문제

이때 Album 객체를 저장하려면 어떻게 해야될지 생각해보자.

  1. Album 객체를 Album과 Item으로 분해
  2. INSERT INTO item 작성
  3. INSERT INTO album 작성

그리고 Album을 조회하려면 어떻게 해야될지 생각해보자.

  1. Item, Album 테이블의 조인 SQL 작성
  2. 각각의 객체 생성

Item-Movie, Item-Book에 대해서도 동일한 작업을 해야된다.

JPA를 통한 상속 해결

JPA는 하위 타입을 저장할 때 상위, 하위 타입에 해당하는 INSERT 실행해 두 테이블에 나누어 저장한다.
조회할 때는 필요한 테이블을 조인하여 데이터를 조회하고 반환해준다.

연관관계

객체는 참조를 사용해서 다른 객체와 관계를 맺는다.
테이블은 외래 키를 사용해서 다른 테이블과 관계를 맺는다.

그림으로 표현하면 아래와 같다.

발생문제

문제는 객체 모델링을 테이블 중심으로 할 경우 또는 객체 지향적으로 할 경우에 발생한다.

우선 테이블 중심으로 모델링을 했을 경우를 보자.

테이블 중심 모델링

테이블은 외래 키 컬럼을 사용해서 테이블간 관계를 맺는다.
이를 코드로 작성하면 아래와 같다.

class Member {
  String id;       //MEMBER_ID 컬럼
  Long teamId;     //TEAM_ID 컬럼 (FK)
  String username; //USERNAME 컬럼
}
class Team {
  Long id;     //TEAM_ID 컬럼 (PK)
  String name; //NAME 컬럼 
}

이 모델링의 장점은 테이블에 저장하거나 조회할 때 객체 필드를 그대로 사용하기 때문에 편하다.
단점은 Member에서 Team으로 객체 참조가 없다.
이 때문에 member.getTeam()을 통한 객체 그래프 탐색이 불가능하다.

객체지향 모델링

객체는 참조를 통해서 관계를 맺는다.
이를 코드로 작성하면 아래와 같다.

class Member {
  String id;       //MEMBER_ID 컬럼
  Team team;       //참조로 연관관계를 맺는다.
  String username; //USERNAME 컬럼
  
  Team getTeam() {...}
}
class Team {
  Long id;     //TEAM_ID 컬럼 (PK)
  String name; //NAME 컬럼 
}

객체지향 모델링의 장점은 객체 참조가 있어 member.getTeam()을 통한 탐색이 가능하다.
단점은 객체를 저장하기 위해 SQL 변환 작업이 늘어난다.

JPA를 통한 연관관계 해결

JPA는 관계 맺고 있는 객체 참조를 외래 키로 변환해서 INSERT SQL 작성을 알아서 해주고 조회할 때 외래 키를 참조로 변환하는 일도 JPA가 처리해준다.

객체 그래프 탐색

객체에서 회원이 소속된 팀을 조회할 때는 참조를 사용해서 연관된 팀을 찾으면 된다.
이것을 객체 그래프 탐색이라 한다.

Team team = member.getTeam();

객체 연관관계가 아래와 같이 설계되어 있다고 가정해보자.

그리고 member 객체부터 시작해 연결된 다른 객체를 가려면 아래 방법을 이용할 것이다.
member.getOrder().getOrderItem()...

발생문제

SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해진다.
아래의 SQL을 통해 객체를 조회한다면 member.getTeam()은 성공하지만 이외의 다른 객체 탐색은 불가능하다.

SELECT m.*, t.*  
  FROM Member m
  JOIN Team t ON m.teamId = t.id;

JPA를 통한 객체 그래프 탐색 해결

JPA는 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룰수 있다.
이를 지연 로딩이라고 한다.

비교

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

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

따라서 테이블의 로우를 구분하는 방법과 객체를 구분하는 방법에는 차이가 있다.

발생문제

동일한 키의 Member를 조회하는 예제를 통해 알아보자.

class MemberDAO {
  public Member getMember(String memberId) {
    String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
    ... // JDBC API, SQL 실행
    return new Member(...);
  }
}
String memberId = "010";
Member member1 = memberDao.getMember(memberId);
Member member2 = memberDao.getMember(memberId);

member1 == member2; //다르다

member1과 member2는 동일한 키로 조회한 결과 객체이다.
따라서, 테이블에서는 같은 Row에 해당한다.
하지만 member1과 member2는 다른 객체가 된다. (DAO에서 새로운 객체를 생성, 반환했기 때문이다.)

JPA를 통한 비교 해결

JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.
(같은 객체임을 보장하는 방법은 이후 강의를 통해 자세히 알아본다.)

정리

객체 모델과 관계형 데이터베이스 모델은 지향하는 패러다임이 다르다. 그래서 한쪽의 패러다임으로 치우친 개발을 하면 처리해야될 문제가 많아진다.
이를 해결해주는 것이 JPA이다.
JPA를 사용하면 패러다임의 불일치 문제를 해결하여 정교한 객체 모델링을 통한 애플리케이션 개발을 가능하게 해준다.

profile
주 7일, 배움엔 끝이 없다

0개의 댓글