응애개발자
article thumbnail
Published 2023. 7. 9. 01:06
JPA 패러다임의 불일치 Spring/JPA
728x90

애플리케이션이 발전하며 그 내부 복잡성도 증가, 그래서 현대의 복잡한 애플리케이션은 대부분 객체지향 언어로 개발한다. 문제는 도메인 모델을 저장할때 발생한다. 예를들어 회원 객체를 저장해야 하는데 회원 객체가 팀 객체를 참조하고 있다면, 회원 객체를 저장할 때 팀 객체도 함께 저장해야 한다. 단순히 회원 객체만 저장하면 참조하는 팀 객체를 잃어버리는 문제를 발생한다. 

현실적인 대안은 관계형 DB에 객체를 저장하는 것인데, 객체와 관계형 DB는 지향하는 목적이 서로 다르므로 둘의 기능과 표현 방법도 다르다. 이것을 객체와 관계형 DB의 패러다임 불일치 문제라 한다. 따라서 객체 구조를 테이블 구조에 저장하는 데는 한계가 발생한다.

 

1.상속

객체 상속 모델

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

테이블 모델

그나마 DB모델링에서  슈퍼타입 서브타입 관계를 사용하면 객체 상속과 가장 유사한 형태로 설계 가능하다.

ex) DTYPE값이 MOVIE이면 영화 테이블과 관계가 있다.

 

ALBUM 객체를 저장하려면 이 객체를 분리해서 두 SQL을 만들어야한다. 다른 객체도 마찬가지이다.

 

JDBC API를 사용하여 이 코드를 완성하려면 부모 객체에서 부모 데이터만 꺼내서 ITEM 용SQL을 작성, 자식객체에서 자식 데이터만 꺼내서 ALBUM용 SQL을 작성해야 하는데, 코드가 많고, 자식 타입에 따라 DTYPE도 저장해야 한다. 조회하는 것도 쉽지 않은 일이다. 이런 과정에서 해당 객체들을 DB가 아닌 자바 컬렉션에 보관한다면 고민 없이 해당 컬렉션을 사용하면 된다.

 

자바 컬렉션 사용 예시

 

JPA와 상속

JPA는 상속과 관련된 패러다임 불일치 문제를 해결해준다. 밑은 그 예시를 작성

 

  1. JPA를 사용하여 ITEM을 상속한 ALBUM객체를 저장. (persist()메서드 사용)
    jpa.persist(album);
  2. SQL을 실행하여 객체를 ITEM, ALBUM 두 테이블에 나누어 저장.
    INSERT INTO ITEM ...
    INSERT INTO ALBUM ...
  3. 다음으로 ALBUM 객체 조회(find() 메서드를 사용하여 조회)
    String albumId = "id100";
    Album album = jpa.find(Album.calss , albumId);
  4. JPA는 ITEM과 ALBUM 두 테이블을 조인해서 필요한 데이터를 조회하고 그 결과를 반환

 

 

2.연관관계

연관관계

객체는 참조가 있는 방향으로만 조회할 수 있다. 위의 예시에서 member.getTeam()은 가능하지만 반대 방향인 team.getMember()는 참조가 없으므로 불가능하다. 하지만 테이블은 외래 키 하나로 MEMBER JOIN TEAM도 가능하지만 TEAM JOIN MEMBER도 가능하다.

 

 

객체를 테이블에 맞추어 모델링

테이블에 맞춘 객체 모델

 

관계형 DB에서는 조인이라는 기능이 있으므로 외래 키의 값을 그대로 보관해도 된다. 하지만 객체는 연관된 객체의 참조를 보관해야 다음처럼 참조를 통해 연관된 객체를 찾을 수 있다.

Member.teamId 필드처럼 TEAM_ID 외래 키까지 관계형 DB가 사용하는 방식에 맞추면 Member 객체와 연관된 Team 객체를 참조를 통해서 조회할 수 없다. 이런 방식을 따르면 좋은 객체 모델링은 기대하기 어렵고, 객체지향의 특징을 잃어버린다.

 

 

객체지향 모델링

참조를 사용하는 객체 모델

 

이처럼 객체지향 모델링을 사용하면 회원과 연관된 팀을 조회할 수 있다.

그런데 이처럼 객체지향 모델링을 사용하면 객체를 테이블에 저장, 조회가 쉽지 않음. 결국, 개발자가 중간에서 변환 역할을 해야 한다.

 

저장

객체를 DB에 저장하려면 team 필드를 TEAM_ID외래 키 값으로 변환 해야 한다.

 

조회

조회할 때는 TEAM_ID 외래 키 값을 Member 객체의 team 참조로 변환해서 객체에 보관해야 한다. 먼저 MEMBER와 TEAM을 조회한다.

SQL 결과로 다음과 같이 객체를 생성하고 연관관계를 설정해서 반환하면 된다.

이런 과정들은 모두 패러다임 불일치를 해결하려는 비용이다. 만약 자바 컬렉션에 회원 객체를 저장하면 이런 비용이 전혀 들지 않는다.

 

JPA와 연관관계

개발자는 회원과 팀의 관계를 설정하고 회원 객체를 저장하면 된다. JPA는 team의 참조를 외래 키로 변환해서 적절한 INSERT SQL을 DB에 전달 한다. 객체를 조회할 때 외래 키를 참조로 변환하는 일도 JPA가 처리해줌.

 

3.객체 그래프 탐색

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

 

객체 연관관계가 다음과 같이 설계있다고 가정해보자.

 

다음은 객체 그래프를 탐색하는 코드다.

객체는 마음껏 객체 그래프를 탐색할 수 있어야 하지만

이렇게  SQL을 실행해서 회원과 팀에 대한 데이터만 조회했다면 member.getTeam()은 성공하지만

이렇게 다른 객체 그래프는 데이터가 없으므로 탐색할 수없다.

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

예시로 밑의 코드를 살펴보자.

이렇게 MemberService는 memberDAO를 통해 member 객체를 조회했지만 이 객체와 연관된 Team, Order, Delivery 방향으로 탐색할 수 있을지 없을지는 전혀 예측할 수 없다. 결국 DAO를 열어서 SQL을 직접 확인해야 한다. 이것은 SQL에 엔티티가 SQL에 논리적으로 종속되어 발생하는 문제이다. 그렇다고 member와 연관된 모든 객체 그래프를 DB에서 조회해서 메모리에 올려두는 것은 현실성이 없다. 결국 MemberDAO에 회원을 조회하는 메소드를 상황에 따라 여러 벌 만들어서 사용.

하지만 이것도 복잡하다. 그래서 JPA를 사용하여 이 문제를 해결

 

JPA와 객체 그래프 탐색

JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다. 따라서 JPA를 사용하면 연관된 객체를 신뢰하고 마음껏 조회 가능. 이 기능은 실제 객체를 사용하는 시점까지 DB 조회를 미룬다고 해서 지연 로딩이라고 한다.

 

다음은 지연 로딩을 사용하는 코드이다.

여기서 Member 를 사용할 때마다 Order를 함께 사용하면 한 테이블씩 조회하는 것보다는 Member를 조회하는 시점에 SQL 조인을 사용해서 Member와 Order를 함께 조회하는 것이 효과적이다.

 

4.비교

DB는 기본 키 값으로 각 row를 구분한다. 반면 객체는 동일성과 동등성 비교라는 두 가지 비교 방법이 있다.

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

따라서 DB와 객체를 구분하는 방법은 차이가 있다.

 

위 코드에서 동일성을 비교하면 false가 반환 된다. 왜냐하면 둘다 같은 DB에서 조회했지만 객체 측면에서 볼 때 둘은 다른 인스턴스 이기 때문이다. 따라서 객체를 컬렉션에 보관하면 동일성 비교에 성공했을 것이다. 

이런 패러다임의 불일치를 해결하기 위해 DB에서의 같은 로우를 조회할 때마다 같은 인스턴스를 반환하도록 구현하는 것이 쉽지 않다. 여기에 여러 트랜잭션이 동시에 실행되는 상황까지 고려하면 더 어려워 진다.

 

JPA와 비교

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

 

 

5. 정리

객체 모델과 관계형 DB 모델은 지향하는 패러다임이 서로 다르다. JPA는 패러다임의 불일치 문제를 해결해주고 정교한 객체 모델링을 도와준다. 그러므로 JPA를 사용하자! ㅎㅎ

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

엔티티 매핑  (0) 2023.07.11
영속성 관리  (0) 2023.07.10
JPA 시작  (0) 2023.07.09
JPA란 무엇인가?  (0) 2023.07.09
JPA 소개  (0) 2023.07.07
profile

응애개발자

@Eungae-D

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!