ggoggo
[JPA] 연관관계 매핑 본문

객체 지향 프로그램에서의 객체와 RDB에서의 테이블이 서로 연관관계를 맺는 방법이 다르다.
따라서 이 둘의 차이를 채우기 위한 매핑 과정이 필요하고 이를 ORM인 JPA가 수행하게 된다.
ORM이란?
영속성(Persistence)
- 데이터를 생성한 프로그램이 종료되더라도 사라지지 않는 데이터의 특성을 말한다.
- 영속성을 갖지 않는 데이터는 단지 메모리에서만 존재하기 때문에 프로그램을 종료하면 모두 잃어버리게 된다.
- Object Persistence(영구적인 객체) : 메모리 상의 데이터를 파일 시스템, 관계형 데이터베이스 혹은 객체 데이터베이스 등을 활용해 영구적으로 저장하여 영속성을 부여한다.
- JDBC, Spring JDBC(ex. jdbcTemplate), Persistence Framework(ex. Hibernate, Mybatis)를 활용해 데이터를 데이터베이스에 영구적으로 저장할 수 있다.
ORM이란?
- Object Relational Mapping, 객체-관계 매핑
- 객체와 관계형 데이터 베이스의 데이터를 자동으로 매핑(연결)해주는 것을 말한다.
- Persistence API라고도 할 수 있다
ex) JPA, Hibernate
데이터 중심의 모델링
객체를 RDB의 테이블에 맞춰 데이터 중심적으로 모델링할 경우 객체 지향 프로그래밍에서의 객체를 제대로 활용할 수 없다.
즉!!! 객체 사이의 협력 관계를 만들 수 없고 굉장히 부자연스러워진다.
또한 해당 객체를 테이블에 저장하고 읽어오는 과정에서 여러가지 단점들이 있다.
예를 들어 Member
와 Team
엔티티에 대한 코드를 데이터 중심 설계로 작성할 경우
class Member {
private long id;
private long teamId;
private String userName;
}
class Team {
private long id;
private String teamName;
}
member의 team 정보를 알기 위해서는 member를 조회한 뒤 외래키로 가지고 있는 teamId를 통해서 team을 조외하는 과정을 반복해야 한다.
즉 member를 조회하는 쿼리와 team을 조회하는 쿼리 총 2개가 필요하게 된다.
위의 예시는 간단하지만 연관관계가 많아지면 아주 복잡해진다.
연관관계 매핑
여러가지 연관관계에서 객체와 데이터베이스의 테이블이 각각 어떻게 연관관계를 가지는지 살펴보자
1. 단방향 연관관계
- 객체
- 참조를 통해 연관된 객체를 찾는다.
- A가 B를 참조할 때 B->A는 불가능
- 테이블
- 외래키로 join해 연관된 테이블을 조회한다.
- 양방향으로 A->B, B->A 모두 가능하다.

단방향 연관관계에서는 객체와 데이터베이스의 테이블은 연관관계를 맺을 때 위와 같은 차이가 존재한다. 이를 극복하기 위해 객체를 데이터에 맞추어야 한다. 아래는 **객체 중심의 모델링**을 따른 것이다.
@Entity
public class Memeber {
@Id @GeneratedValue
private Long id;
private String userName;
@ManyToOne
@JoniColumn(name = "team_id")
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String teamName;
}
이때 ORM을 활용하면 member 조회 시 연관관계를 가지는 team 까지 가져와 객체 참조 형태로 매핑까지 해준다.
public void find(Long memberId) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// findMember 를 가져올 때 연관관계를 가지는 Team 까지 가져와서 객체 참조형태로 매핑까지 해준다.
Member findMember = em.find(Member.class, memberId);
Team findTeam = findMember.getTeam();
tx.commit();
} catch(Exception e) {
tx.rollback();
} finally {
em.close();
}
}
2. 양방향 연관관계
사실 양방향 연관관계는 없다! 그냥 두 객체가 서로 참조하는 단방향 연관관계가 2개인 것을 양방향 연관관계라 한다.
객체에서 양방향 연관관계란 단방향 연관관계 2개를 말하기 때문에 데이터베이스 테이블과의 차이점이 발생하고 이러한 차이점을 극복하기 위한 것이 양방향 연관관계 매핑이다.
객체에는 두 개의 단방향 연관관계가 있고, 테이블에는 하나의 양방향 연관관계가 있다. 그럼 DB의 관점에서 Member 객체의 team을 수정해도 외래키인 TEAM_ID가 수정되어야 하고, Team의 members를 수정해도 외래키인 TEAM_ID가 수정되어야 한다. 다시 말해 MEMBER 테이블의 외래키 TEAM_ID는 두개의 연관관계 매핑에 엮여 있다.
이러한 문제를 해결하기 위해 양방향 연관관계 매핑에서는 해당 연관관계의 주인을 결정해 객체의 두 개의 단방향 연관관계 중 외래키 TEAM_ID에 영향을 줄 하나의 단방향 연관관계를 지정해줘야 한다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String memberName;
@ManyToOne
@JoinColumn("team_id")
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String teamName;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<>();
}
자 위에서 연관관계 매핑의 주인은 누굴까?
바로 @OneToMany 어노테이션의 mappedBy 값인 team을 가지고 있는 Member다.
=> @OneToMany(mappedBy="team")의 의미는 team이라는 것에 매핑한다는 의미
연관관계 매핑의 주인이 정해지면 아래의 규칙이 생긴다.
1. 연관관계 매핑의 주인만이 외래키를 관리한다.(등록 & 수정)
2. 주인이 아닌 쪽에서는 오직 읽기만 가능하다.
3. 주인은 mappedBy 속성을 사용하지 않는다.
4 주인이 아닌 쪽에서는 mappedBy 속성을 통해 주인을 지정해줘야 한다.
이제 테이블에 수정한 값을 반영하려면 반드시 연관관계 매핑의 주인의 값을 변경해줘야 한다.
그럼 두 객체 중 어떤 걸로 연관관계 매핑의 주인을 지정해야 할까?
외래키를 가지고 있는 테이블을 연관관계 매핑을 주인을 설정하면 된다.(혼동 방지를 위한 권장 사항임)
'Spring > JPA' 카테고리의 다른 글
[JPA] 다대다관계 (0) | 2023.11.29 |
---|---|
[SpringDataJPA] SpringDataJPA란? (0) | 2023.11.27 |