시뻘건 개발 도전기

DB 연동 시 유의할 점 #2 본문

API/JPA

DB 연동 시 유의할 점 #2

시뻘건볼때기 2020. 9. 18. 23:35
반응형

  애플리케이션을 개발할 때 상용계 운영과 유지보수도 함게 생각하면서 개발해야 한다. 결국, 내부 로직이 복잡하면 복잡할 수록 유지보수가 어렵고 운영에도 영향을 미치기 쉬울 수 있다. 데이터베이스를 연동 할 때도 복잡해서는 안된다는 이야기이다. 이전 글에서 언급한 것 처럼 개발을 하게 된다면, 유지보수가 말도 못하게 힘들 수 있다. 그렇다고 데이터베이스 연동을 안할 수 없지 않는가.

  데이터베이스는 객체지향 이야기 되는 추상화, 상속, 다형성의 개념이 없고, 구조 조차도 다르다. 즉, 객체와 RDB는 각각 지향하는 목적이 다르기 때문에 사용 방법과 표현방식에 차이가 있을 수 밖에 없다. 이 것을 패러다임 불일치 문제라고 이야기 한다. 이러한 문제때문에 객체 구조를 DB 테이블 구조에 저장하는데에 한계가 있을 수 있다. 이 한계 덕분에 개잘자가 많은 시간을 소비하면서 코드를 짜기 마련이다.

  패러다임 불일치 문제를 알아보자.

 

1. 상속

RDB의 테이블은 상속(객체의 상속 개념)이라는 기능이 없다. 알아보기 쉽게 코드를 보자.

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

class Album extends Item {
	String artist;
}

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

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

 만약 Album 객체를 저장하려면 쿼리를 두 번 날려야한다.

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


  JPA는 이러한 문제를 개발자 대신 해결해준다. 다음과 같은 기능으로 상위 두 쿼리를 한 번에 날려준주며 조회 또한 JOIN문이 삽입된 쿼리를 날려주는 기능도 있다.
Item을 상속 받는 모든 객체 모두
마찬가지다. 이런 식이라면 JDBC API를 사용한 자바 애플리케이션은 "코드"라고 하기에도 창피할 만큼 코드량이 엄청날 것이다. 조회 또한 JOIN문을 사용하여야 한다.

jpa.persist(album);
jpa.find(Album.class, id);

 

2. 연관관계
  객체는 참조를 사용해서 연관된 객체를 조회한다. 테이블은 외래 키와 조인을 사용해서 연관된 테이블을 조회한다. 객체의 경우에는 연관관계에 대해 특별한 문제가 있다. 객체는 참조가 있는 방향으로만 조회할 수 있다는 점이다.
이전 글에서 보았던 예제를 가지고 와보았다.

Class Member {
	private String name;
	private String teamId;
	private Team team;
}

Class Team {
	private String name;
	private String teamId;
}

// ...
logger.info(member.getTeam().getName()); // null

member.getTeam()은 가능하지만, team.getMember()은 불가능하다는 이야기다. 이를 유연하게 해결하기 위한 방법으로 두 가지가 있다.

- 객체를 테이블 연관관계에 맞추어 모델링 : 이 방법은 객체지향 특징을 버린다.

- 객체지향 모델링 : CRUD 하기가 쉽지 않다.

  JPA는 이러한 연관관계 문제를 해결해준다.

member.setTeam(team);
jpa.persist(member);

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

우리는 회원과 팀의 관계를 설정하고 객체를 저장하기만 하면 된다. JPA가 team의 참조를 외래키로 변환하여 INSERT문을 날린다. (정확하게는 JDBC에게 쿼리를 전달하고 JDBC가 DB에 날리게 된다.) 조회할 때도 마찬가지로 JPA가 SELECT문을 외래 키를 참조로 변환하여 처리한다.

 

3. 객체 그래프 탐색

  객체에서 회원이 소속된 팀을 조회할 때는 다음처럼 참조를 사용해서 연관된 팀을 찾는 탐색법이다.

Team team = member.getTeam();

객체 연관관계

 

 

member.getOrder().getOrderItem().getItem().getCategory() // x 
member.getOrder().getDelivery() // x 
member.getTeam() // o 

객체는 마음껏 객체 그래프를 탐색할 수 있어야한다. 그런데 항상 가능한 일일까? 만약 다음 쿼리를 날렸을 때를 보자.

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

해당 쿼리에서는 팀과 회원의 정보만 조회했기 때문에 member.getOrder()는 null이 되어 탐색할 수 없을 것이다.

  JPA는 객체 그래프를 마음껏 탐색할 수 있다. JPA는 연관된 객체를 사용하는 점에서 적절한 SELECT문을 전달한다. 그렇기 때문에 연관된 객체를 신뢰할 수 있고 탐색 또한 자유롭게 할 수 있는 것이다. 이 기능을 "지연 로딩"이라고 하는데, 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룬다는 의미다.

 

4. 비교

  데이터베이스는 기본 키의 값으로 각 row를 구분하지만 객체는 동일성 비교와 동등성 비교로 구분 할 수 있다.

- 동일성 비교 : "=="를 사용하여 인스턴스의 주소 값을 비교한다.

- 동등성 비교 : equals()를 사용하여 객체 내부의 값을 비교한다.

따라서 비교 방식에도 차이가 있다. 아래 예제를 보자.

String id = "dotori";
Member member1 = dao.getMember(id);
Member member2 = dao.getMember(id);

logger.info(member1 == member2); // false

같은 데이터베이스의 테이블에서 같은 row를 가지고 왔지만 인스턴스가 생성될 때 서로 다른 메모리에 할당되므로 결과는 false가 나올 것이다.

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

String id = "dotori";
Member member1 = jpa.find(Member.class, id);
Member member2 = jpa.find(Member.class, id);

logger.info(member1 == member2); // true

 

반응형

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

Entity와 영속성 #2  (0) 2020.11.29
Entity와 영속성 #1  (0) 2020.11.29
JPA를 사용하기 위한 설정과 동작 원리  (0) 2020.09.29
JPA란?  (0) 2020.09.24
DB 연동 시 유의할 점 #1  (0) 2020.09.18
Comments