[JPA] ORM, 패러다임의 불일치

2022. 3. 12. 18:33

학습 목표

  • ORM에 대해 간략하게 알아보자.
  • 패러다임 불일치에 대해서 알아보자.

JPA는 자바 진영에서 ORM 기술 표준으로 사용되는 인터페이스 모음이다. 즉, 실제 구현된 클래스가 아닌 구현 클래스와의 매핑을 위해 사용되는 일종의 프레임워크라고 할 수 있다. JPA는 애플리케이션과 JDBC 사이에서 동작하며, 관계형 데이터 베이스를 사용하는 방식을 따른다.

JPA를 이해하기 위해서는 ORM에 대해서 학습해야 한다.

 

ORM?

Object-relational mapping(객체 관계 매핑)

애플리 케이션 Class와 RDB(Relational DataBase)의 테이블을 매핑, 즉 어플리케이션의 객체를 RDB 테이블에 자동으로 영속화 해주는 과정이다. ORM이 갖는 장점은 다음과 같다.

  • 객체 지향적인 코드 구성
    •  ORM 을 통해 개발자는 객체 모델을 이용하여 객체지향적인 비즈니스 로직을 구성하는 데에 집중할 수 있다.
    •  Query와 같은 선언문 등의 코드가 줄어들어서, 각종 객테에 대한 코드를 별도로 작성하여 가독성을 높일 수 있다.
  • 재사용, 유지보수, 리팩토리의 용이성
    • ORM은 기존 객체와 독립적으로 구성되어 있고, 객체의 특성을 이용해서 재사용이 가능하다. 또한 매핑하는 정보가 명확해서 ERD를 참조하는 의존도를 낮출 수 있다. 
패러다임의 불일치

 

비즈니스 요구사항을 정의한 도메인 모델을 객체로 정의하면 객체가 지니는 장점을 이용할 수 있는데, 객체를 어떻게 DB에 저장할 지가 문제이다. 단순히 객체의 속성을 DB에 모두 저장 할 수도 있지만, 객체가 상속을 받거나 다른 객체를 참조하게 된다면 고민해봐야 할 문제들이 증가한다. 그 이유는 객체와 데이터베이스 간에는 '패러다임의 불일치'라는 개념이 존재하기 때문이다.

 

객체지향 프로그래밍의 특성(추상화, 캡슐화, 정보은닉, 상속, 다형성 등)을 이용하여 시스템의 복잡성을 제어할 수 있다.

그러나 RDB(관계형 데이터베이스)는 데이터 중심으로 구조화, 집합적인 사고가 필요하며, 추상화, 상속 다형성 같은 개념이 없다. 

자바언어는 객체지향으로 이뤄져 있고, 데이터베이스는 데이터 중심으로 구조화 되어 있기 때문에, 패러다임 불일치 문제를 해결하는 데에는 많은 시간과 코드들이 소비될 수 밖에 없다. 

 

결론부터 말하자면, JPA를 이용하면 패러다임의 불일치를 해결할 수 있다. 


A.상속

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

왼) 객체 상속 모델  오) 슈퍼타입 테이블 설계

객체 모델 코드

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;
}

 

Book 클래스는 Item 클래스를 상속받고 있다. Book 객체를 저장하기 위해서는 이 객체를 분해해서 SQL을 생성한다.

INSERT INTO ITEM..

INSERT INTO ALBUM..

 

JDBC API를 이용하면 부모 객체에서 부모 데이터를 꺼내서 ITEM에 쓰일 SQL을 작성하고, 자식 객체에서는 자식 데이터만 꺼내서 BOOK에 쓰일 SQL도 따로 작성해야 한다. 이런 방식으로 상속 받는 다른 자식객체들에 대한 SQL을 생성하려면 작성해야할 코드가 많아지고, 자식 타입에 따라 컬럼도 추가해야한다. 

 

JPA에서는?

개발자 입장에서는 자바 컬렉션에 객체를 저장하듯이 JPA에게 객체를 저장하면 패러다임 불일치 문제를 대신 해결해준다. 

 

//저장
jpa.persist(book);

//조회
String bookId = "book1";
Book book = jpa.find(Book.class, bookId);

JPA에 의해 객체가 저장이 되면, JPA는 두 객체를 각각의 테이블에 나누어 저장해준다.

INSERT INTO ITEM..

INSERT INTO ALBUM..

 

조회의 경우에도 find() 메소드를 사용해서 객체를 조회한다. JPA에서는 부모 객체(ITEM)과 자식 객체(BOOK)을 조인하여 필요한 데이터를 조회 및 결과를 반환할 수 있다. 

 

SELECT I.*, B.*

FROM ITEM I

JOIN BOOK B ON I.ITEM_ID = B.ITEM.ID

 

B.연관관계

  • 참조를 사용하여 다른 객체와 연관관계를 가지고 연관된 객체를 조회. 객체 연관관계의 경우 단방향적인 조회만 가능 >> 객체
  • 외래 키를 사용하여  테이블 조회, 테이블 연관관계의 경우 조인을 통해 양방향 조회 가능 >> 테이블

 

<관계형 DB 방식을 객체에 반영>

class Member {

    String id;      // MEMBER_ID 컬럼 사용
    Long teamId;    // TEAM_ID FK(forign key) 컬럼 사용
    String userName;
}

class Team {

    Long id;        // TEAM_ID PK(primary key) 사용
    String name;
}

관계형 데이터베이스는 조인 기능을 이용해서 외래 키 값을 그대로 보관할 수 있다. 

Team team = member.getTeam();

객체는 연관된 객체를 위와 같은 참조에 의한 방식을 통해 연관 객체를 찾을 수 있다. 

관계형 데이터 방식을 따라간다면 Member 객체와 연관된 객체를 참조를 통해 조회 할 수 없다. 따라서 좋은 객체 모델링을 이용할 수 없어서 객체 지향 프로그래밍을 기대할 수 없다. 객체모델을 Team 객체로, 데이터베이스는 TEAM_ID로 저장하기 때문에, 두 방식의 차이를 극복하려면 개발자가 직접 중간에서 변환 역할을 해줘야 한다.

 

조회의 경우는 다음과 같다.

public Member find(String memberId) {

//SQL 실행
...
Member member = new Member();
...
//DB에서 조회한 회원 관련 정보를 입력 

Team team = new Team();
//DB에서 조회한 팀 관련 정보를 모두 입력

//회원 및 팀 관계 매핑
member.setTeam(team);
return member;

 

JPA에서는?

 

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

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

회원과 팀간의 관계를 매핑하고 저장하면 JPA가 이후에 관리해준다. JPA는 team 참조를 외래키로 변환해 적절한 INSERT SQL문을 데이터베이스에 전송한다.

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

객체를 조회할 때, 외래키를 참조로 변환하는 작업들을 모두 제공해준다. 

 

c. 객체 그래프 탐색

참조를 통해 연관된 팀을 조회하는 과정을 객체 그래프 탐색이라고 한다. 객체가 아래와 같이 설계되어 있다고 가정해보자.

 

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

객체는 마음껏 객체 그래프를 탐색할 수 있어야 한다. 하지만 데이터베이스에서는 객체를 조회할 때, member와 order의 데이터만 조회할 경우, member.getTeam()의 값은 null이 된다. 처음 실행되는 SQL 문에 따라 객체 그래프의 탐색이 한정적으로 변하기 때문에, 객체 지향 프로그래밍에서는 제약이 걸릴 수 있다. 비즈니스 로직에 따라 사용하는 객체 그래프가 다르기 때문에, 함부로 예측할 수 없다.

class MemberService {
  ...
  public void process() {
  
    Member member = memberDAO.find(MemberId);
    member.getTeam(); 
    member.getOrder().getDelivery();  
    // 두가지 경우 모두, 탐색이 될지 예측할 수 없다.

 

객체 그래프의 탐색은 전적으로 SQL 문에 달려있기 때문에, 매번 MemberDAO에 메서드를 여러개 만들어두어야 하는데, 개발자 입장에서는 코드가 복잡해진다. 

 

JPA에서는?

JPA 에서는 객체 그래프를 마음껏 탐색할 수 있다.

class MemberService {

public class void Process() {

Member member = jpa.find(Member.class, memberId); //처음 조회 시점에 SQL 문 생성(즉시 로딩)

Order order = member.getOrder();
order.getOrderDate(); // Order를 사용하는 시점에 조회한다(지연 로딩)

JPA는 실제 객체를 조회해서 사용하는 시점에 SQL문이 생성되어 DB에 저장하는 지연 로딩(Lazy Loading)이란 기능이 있다. 이 기능을 사용하면 연관된 객체를 신뢰하고 조회할 수 있는데, 실제 객체가 사용할 때 까지 조회를 미루기 때문에 가능하다. 지연 로딩을 통해 가져온 객체는 투명하게 처리할 수 있게 되어 신뢰할 수 있다.

 

JPA와 연관된 객체를 즉시 조회할지, 실제 사용시점에 조회할 지 간단한 설정으로 정의할 수 있다. 

 

<지연로딩 / 즉시 로딩>

지연 로딩(Lazy Loading) : 객체가 실제 사용될 때 로딩

즉시 로딩(Eager Loading) : JOIN SQL로 한번에 연관된 객체까지 미리 조회한다.

 

 

d. 비교

객체는 동일성 비교, 동등성 비교가 존재하지만 데이터 베이스에서는 기본 키(pk)의 값으로 각 행들을 구분한다.

(동일성 비교 >> "==" 이용 / 동등성 비교 >> "equals() 메소드" 이용)

 

// MemberDAO 코드
class MemberDAO {

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

//조회 회원 비교
String memberId = "100";
Member member1 = memberDAO.getMember(Member.class, memberId);
Member member2 = memberDAO.getMember(Member.class, memberId);

member1 == member2; // false

같은 ID로 데이터를 조회했을 때 두 객체가 다른 결과값을 갖는다. 같은 행에서 조회했지만 객체 측면에서는 다른 인스턴스다. 그 이유는 getMember를 호출할 때마다 새로운 인스턴스가 생성되기 때문이다. 만약 컬렉션에 보관했다면 동일성 비교에 의해 같은 인스턴스값을 가질 것이다. 

 

JPA에서는?

 

JPA에서는 같은 트랜젝션 내부에서 동일한 객체가 조회되는 것을 보장한다.

//조회 회원 비교
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);

member1 == member2; // true

 

 

<출처>

https://jgrammer.tistory.com/76 

 

[JPA] 패러다임 불일치

애플리케이션은 발전하면서 점점 복잡성이 커진다. 지속 가능한 애플리케이션을 개발하는 일은 끊임없이 증가하는 복잡성과 의 싸움이다. 복잡성을 제어하지 못하면 유지보수하기 어려운 애플

jgrammer.tistory.com

 

'programming > Spring' 카테고리의 다른 글

[Spring/JPA]@Entity에서 final을 사용하면 안되는가?  (0) 2022.04.02

BELATED ARTICLES

more