연관관계란 두 도메인(객체, 테이블) 이 서로 논리적인 의미를 갖고 양쪽을 참조하는 관계를 뜻 합니다.
객체 지향 프로그램에서의 객체들이 서로 연관관계를 맺는 방법과 RDB 에서의 테이블들이 연관관계를 맺는 방법이 다릅니다.
이 간극을 채워주기 위한 기술이 ORM 이며 ORM 기술의 Java 의 표준 명세가 JPA 입니다.
연관관계 매핑 시에는 아래와 같은 사항들을 생각하명 수행하여야 합니다.
JPA 에서 제공하는 연관관계 매핑에 대해서 알아보기 이전에, 잘못된 연관관계 방법을 살펴보고 해당 방법의 단점을 살펴보도록 하겠습니다.
위 테이블을 Java class 로 표현해보도록 하겠습니다.
class MEMBER {
private long id;
private long teamId;
private String userName;
}
class TEAM {
private long id;
private String teamName;
}
이와 같은 방식을 데이터 중심적으로 모델링이라 합니다.
MEMBER 테이블과 TEAM 테이블은 연관관계를 갖고 있으며, 해당 연관관계는 TEAM_ID 라는 외래키를 통하여 확인할 수 있습니다.
데이터 중심적 모델링 방법은 서로 다른 두 객체 사이의 연관관계를 활용할 수 없게됩니다.
즉, 객체지향 프로그래밍의 객체를 제대로 활용할 수 없게됩니다.
또한 해당 객체를 데이터베이스에 저장(영속화) 및 조회를 하는 과정에서 추가적인 코드가 필요합니다.
아래 코드 예시를 통해서 확인해볼 수 있습니다.
특정 Member 가 속한 Team 객체를 조회하는 코드
@RequiredArgsConstructor
public class Jpa {
private final EntityManagerFactory emf;
public void save() {
EntityManager em = emf.createaEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Team team = new Team();
team.setTeamName("teamA");
em.persist(team);
Member member = new Member();
member.setUserName("member1");
member.setTeamId(team.getId());
em.persiste(member);
tx.commit();
} catch(Exception e) {
tx.rollback();
} finally {
em.close();
}
}
public void find(Long memberId) {
EntityManager em = emf.createaEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 불필요한 과정이 많이 필요해진다.
Member member = em.find(Member.class, memberId);
Long teamId = member.getTeamId();
Team team = em.find(Team.class, teamId);
tx.commit();
} catch(Exception e) {
tx.rollback();
} finally {
em.close();
}
}
}
특정 Member 가 속한 Team 정보를 조회하기 위해서는 아래의 작업이 필요해보입니다.
즉, Member, Team 을 조회하는 2개의 쿼리문이 동작하게 됩니다.
연관관계가 무수히 많아진 상태에서 이와같은 코드를 적용해야 한다고 생각해봅시다. 머리가 아파진다.
관계형 데이터베이스의 테이블에는 방향이라는 개념이 없습니다.
하지만 객체지향프로그래밍에서는, 참조용 필드가 있는 쪽으로만 참조가 가능합니다.
class MEMBER {
private long id;
private String userName;
@ManyToOne
private TEAM team;
}
class TEAM {
private long id;
private String teamName;
}
아래의 다이어그램과 같이 표현할 수 있습니다.
아래의 다이어그램과 같이 표현할 수 있습니다.
위 도표의 객체 연관관계를 살펴보면 아래의 연관관계를 찾을 수 있습니다.
객체의 양방향 관계는 사실 서로 다른 단방향 관계 2개입니다.
객체를 양방향으로 참조하려면 억지로 단방향 연관관계를 2개 만들어야 합니다.
class Member {
private long id;
private String userName;
@ManyToOne
@JoinColumn(name="team_id")
private Team team;
}
class Team {
private long id;
private String teamName;
@OneToMany(mappedBy="team")
private List<Member> memberList = new ArrayList<>();
}
// member.getTeam(); 방식으로 조회
// team.getMemberList(); 방식으로 조회
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID;
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID;
이와같이 외래키를 관리하는 객체(연관관계의 주인)을 따로 정해주는 이유는 아래와 같습니다.
주인만이 외래키를 관리(등록, 수정) 합니다.
주인의 반대편 (즉, 외래키를 관리하지 않는 테이블) 은 외래키에 영향(변화)를 줄 수 없습니다
JPA 에서는 다양한 다중성을 위한 어노테이션을 제공합니다.
출처
- 자바 ORM 표준 JPA 프로그래밍 - 김영한 저
- https://ch4njun.tistory.com/274