티스토리 뷰

Framework/Spring

[JPA] 패러다임 불일치

DUCKBAE's 2022. 10. 24. 21:50

객체와 관계형 데이터베이스는 지향하는 목적이 서로 다르므로 둘의 표현 방법은 다르다.

이것을 객체와 관계형 데이터베이스의 패러다임 불일치 문제라고 한다.

 

패러다임의 불일치로 인해 발생하는 문제를 살펴 본 후, JPA를 통한 해결책을 알아보자.

상속

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를 사용해서 이 코드를 완성하려면 부모 객체에서 부모 데이터만 꺼내서 ITEM 용 INSERT SQL을 작성하고,

자식 객체에서 자식 데이터만 꺼내서 ALBUM 용 INSERT SQL을 작성해야 하는데, 작성해야 할 코드량이 만만치 않다.

그리고 자식 타입에 따라서 DTYPE도 저장해야 한다. (조회하는 것도 동일)

 

이러한 과정이 모두 패러다임의 불일치를 해결하려고 소모하는 비용이다.

 

JPA 와 상속

JPA는 상속과 관련된 패러다임의 불일치 문제를 개발자 대신 해결해준다.

개발자는 마치 자바 컬렉션에 객체를 저장하듯이 JPA에 객체를 저장하면 된다.

Album album = new Album();
// set album
jpa.persist(album);

위 코드를 통해 JPA는 아래와 같은 SQL을 실행해서 객체를 ITEM, ALBUM 두 테이블에 나누어 저장한다.

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

다음과 같이 조회해보자.

String albumId = "id100";
Album album = jpa.find(Album.class, albumId);

그럼 JPA는 아래와 같은 SQL을 실행해서 ALBUM 과 ITEM의 테이블을 조인해서 필요한 데이터를 조회한다.

SELECT I.*, A.* FROM ITEM I JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID;

 

객체 그래프 탐색

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

class MemberService {
	..
    public void process() {
    	Member member = memberDAO.find(memberId);
        member.getTeam(); //member -> team 객체 그래프 탐색이 가능한가?
        member.getOrder().getDelivery(); //?
    }
}

MemberService는 memberDAO를 통해서 member 객체를 조회했지만, 이 객체와 연관된 Team, Order, Delivery 방향으로 객체 그래프를 탐색할 수 있을지 없을지는 해당 메서드만 보고 알 수 없다.

직접 데이터 접근 계층인 DAO를 열어서 SQL을 확인해야만 어디까지 탐색이 가능한지를 알 수 있다.

(그렇다고 member와 연관된 모든 객체 그래프를 데이터베이스에서 조회해서 애플리케이션 메모리에 올려두는 것은 현실성이 없다.)

 

JPA와 객체 그래프 탐색

JPA를 사용하면 객체 그래프를 마음껏 탐색할 수 있다.

연관된 객체를 사용하는 시점에 적절한 SELECT SQL 을 실행한다. -> 이 기능은 식제 객체를 사용하는 시점까지 데이터베이스 조회를 미룬다고 해서 지연 로딩(LAZY)이라고 한다.

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

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

 

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

//즉시 조회시 사용되는 SQL
SELECT M.*, O.* FROM MEMBER M JOIN ORDER O ON M.MEMBER_ID = O.MEMBER_ID;

 

 

비교

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

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

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

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

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(memberId);
Member member2 = memberDAO.getMember(memberId);

member1 == member2; //false

동일한 memberId로 회원 객체를 두 번 조회했다. 하지만 동일성(==) 비교를 했을 때 false 가 반환된다.

member1과 member2는 같은 데이터베이스 로우에서 조회했지만, 객체 측면에서 봤을 때 둘은 다른 인스턴스이기 때문이다.

 

JPA와 비교

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

그러므로 두 객체는 동일성(==) 비교에 성공한다.

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

member1 == member2; //true

 

 

참고

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

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

[JPA] 양방향 연관관계 매핑  (0) 2022.10.26
[JPA] 플러시  (0) 2022.10.25
[Spring] HTTP 통신 도구  (0) 2022.10.19
[Spring] 데이터 유효성 검증  (0) 2022.10.04
[Spring] Component Scan  (0) 2022.08.24
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함