-
ORM - ORM과 문제 해결2(연관관계)FrameWork/ORM 2024. 1. 22. 10:05
ORM과 문제 해결2
연관관계
객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회한다. 반면에 테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.
참조를 사용하는 객체와 외래 키를 사용하는 관계현 데이터베이스 사이의 패러다임 불일치는 객체지향 모델링을 거의 포기하게 만들 정도록 극복하기 어렵다.
Member 객체는 Member.team필드에 Team 객체의 참조를 보관해서 Team객체와 관계를 맺는다. 따라서 이 참조 필드에 접근하면 Member와 연관된 Team을 조회 할 수 있다.
class Member { Team team; ... Team getTeam() { return team; } } class Team { ... } member.getTeam(); //member -> team 접근
Member테이블은 MEMBER.TEAM_ID 외래 키 컬럼을 사용해서 TEAM테이블과 관계를 맺는다. 이 외래 키를 사용해서 MEMBER 테이블과 TEAM 테이블을 조인하면 MEMBER테이블과 연관된 TEAM 테니블을 조회할 수 있다.
SELECT M,*,T.* FROM `MEMBER` M JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
조금 어려운 문제도 있는데, 객체는 참조가 있는 방향으로만 조회할 수 있다. 즉 방금 예에서 member.getTeam()은 가능하지만 반대 방향인 team.getMember()는 참조가 없으므로 불가은한다. 반면에 테이블은 외래 키 하나로 MEMBEr JOIN TEAM도 가능하지만 TEAM JOIN MEMBER도 가능하다.
객체를 테이블에 맞추어 모델링
객체와 테이블의 차이를 알아보기 위해 다음 예제와 같이 객체를 단순히 테이블에 맞추어 모델링해보자.
class Member { Stirng id; //MEMBER_ID 칼럼사용 Long teamId; //TEAM_ID_FK 칼럼사용 String username; //USERNAME 컬럼 사용 } class Team{ Long id; //TEAM_ID PK사용 String name; //NAME 칼럼 사용 }
MEMBER 테이블의 컬럼을 그대로 가져와서 Member 클래스를 만들었다. 이렇게 객체를 테이블에 맞추어 모델링하면 객체를 테이블에 저장하거나 조회할 때는 편리하다. 그런데 여기에서 TEAM_ID외래 키의 값을 그대로 보관하는 teamId 필드에는 문제가 있다. 관계형 데이터베이스는 조인이라는 기능이 있으므로 외래 키의 값을 그대로 보과해도 된다. 하지만 객체는 연관된 객체의 참고 보관해야 다음 처럼 참조를 통해 연관된 객체를 찾을 수 있다.
Team team = member.getTeam();
특정 회원이 소속된 팀을 조회하는 가장 객체지향적인 방법은 이처럼 참조를 사용하는 것이다.
Member.teamId 필드 처럼 TEAM_ID외래 키까지 관계형 데이터베이스가 사용하는 방식에 맞추면 Member 객체와 연관된 Team객체를 참조를 통해서 조회할 수 없다. 이런 방식을 따르면 좋은 객체 모델링은 기대하기 어렵고 결국 객체지향의 특징을 잃어버리게 된다.
객체지향 모델링
객체는 참조를 통해서 관계를 맺는다. 따라서 다음 예제와 같이 참조를 사용하도록 모델링해야 한다.
class Member{ String id; //MEMBER_ID컬럼 사용 Team team; //참조로 연관관계를 맺는다. String username //USERNAME 컬럼 사용 Team getTeam(){ return team; } } class Team{ Long id; //TEAM_ID PK 사용 String name; //NAME칼럼 사용 }
Member.team 필드를 보면 외래 키의 값을 그대로 보관하는것이 아니라 연관된 Team의 참조를 보관한다. 이제 회원과 연관된 팀을 조회할 수 있다.
그런데 이처럼 객체지향 모델링을 사용하면 객체를 테이블에 저장하거나 조회를 하기 휩지 않다. Member 객체는 team 필드로 연관관계를 맺고 MEMBER 테이블은 TEAM_ID 왜래 키로 연관관계를 맺기 때문인ㄷ데, 객체 모델은 외래 키가 필요 없고 단지 참조만 있으면 된다. 반면에 테이블은 참조가 필요 없고 외래 키만 있으면 된다. 결국, 개발자가 중간에 변환 역할을 해야한다.
▼저장
객체를 데이터베이스에 저장하려면 team 필드를 TEAM_ID 외래 키 값으로 변환해야 한다.
다음처럼 외래 키 값을 찾아서 INSERT SQL을 만들어야 하는데 MEMBER 테이블에 저장해야 할 TEAM_ID 외래 키는 TEAM 테이블의 기본 키이므로 mebmer.getTeam(),getId()로 구할 수 있다.
member.getId(); //MEMBER_ID PK에 저장 member.getTeam(),getId(); //TEAM_ID FK에 저장 member.getUsername(); //USERNAME 컬럼에 저장
▼조회
조회할 때는 TEAM_ID외래 키 값을 Member 객체의 team 참조로 변환해서 객체에 보관해야 한다. 먼저 다음 SQL과 같이 MEMBER와 TEAM을 조회하자
SELECT M.*, T.* FROM MEMBER M JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SQL의 결과로 다음 예제와 같이 객체를 생성하고 연관과계를 설정해서 반환하면 된다.
예제 1.10 개발자가 직접 연관관계 설정
public Member find (String memberId){ //SQL 실행 ... Member member = new Member(); ... //데이터베이스에서 조회한 회원 관련 정보를 모두 입력 Team team = new Team(); ... //데이터베이스에서 조회한팀 관련 정보를 모두 입력 //회원과 팀 관계 설정 member.setTeam(team); return member; }
이런 과정들은 모두 패러당미 불일치를 해결하려고 소모하는 비용이다. 만약 자바 컬렉션에 회원 객체를 저장한다면 이런 비용이 전혀 들지 않는다.
JPA와 연관관계
JPA는 연관관계와 관련된 패러다임 불일치 문제를 해결해 준다.
member.setTeam(team); //회원과 팀 연관관계 설정 jpa.persist(member); //회원과 연관관계 함께 저장
개발자는 회원-팀 관계를 설정하고 회원 객체를 저장하면 된다. JPA는 team의 참조를 외래 키로 적절한 INSERT SQL을 데이터베이스에 전달한다. 객체를 조회할 때 외래 키를 참조로 변환하는 일도 JPA가 처리해준다.
Member member = jpa.find(Member.class,memberId); Team team = member.getTeam();
그래도 지금까지 설명한 문제들은 SQL을 직접 다루어도 열심히 코드만 작성하면 어느정도 극복할 수 있는 문제들이다. 다음 포스트에서 연관관계와 관련해서 극복하기 어려운 패러다임의 불일치 문제를 알아보자.
출처 - [자바 ORM 표준 JPA 프로그래밍 - 저, 김영한]
http://www.acornpub.co.kr/book/jpa-programmig\
'FrameWork > ORM' 카테고리의 다른 글
ORM - JPA설정 (1) 2024.02.14 ORM - JPA란 무엇인가? (0) 2024.01.22 ORM - ORM과 문제 해결3 (객체 그래프 탐색, 비교) (1) 2024.01.22 ORM - ORM과 문제 해결1(상속) (0) 2024.01.22 ORM- SQL을 직접 다룰 때 발생하는 문제점 (0) 2024.01.20