일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 1차원 DP
- 2차원 dp
- 99클럽
- @BeforeAll
- @BeforeEach
- @Builder
- @Entity
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Query
- @Table
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- ApplicationEvent
- assertThat
- async/await
- AVG
- AWS
- Azure
- bind
- builder
- button
- c++
- c++ builder
- c03
- Today
- Total
기록
[자바 ORM 표준 JPA 프로그래밍] 다양한 연관관계 매핑: 일대다, 다대일, 다대다 (6장) 본문
시작하면서
Java ORM 표준 JPA에서 다양한 연관관계 매핑을 다루는 6장을 읽고 중요한 내용을 정리해 보았습니다. 이 글에서는 객체의 관계를 어떻게 매핑하고, 각각의 경우에 장단점은 무엇인지 살펴보겠습니다. 특히 연관관계의 종류와 주의할 점들을 중심으로 설명하겠습니다.
1. 다대일 관계 (N:1)
한쪽 객체가 여러 객체를 참조하는 관계로, 예를 들어 회원(Member)이 팀(Team)에 속해 있을 때, 여러 회원들이 하나의 팀에 속할 수 있으므로 다대일 관계가 성립됩니다.
다대일 단방향 매핑 (N:1)
다대일 단방향 관계에서는 한쪽에서만 참조가 가능합니다. 예를 들어 회원이 팀을 참조하지만, 팀은 회원을 참조하지 않는 경우입니다. 이런 경우에는 다음과 같이 매핑할 수 있습니다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
// Getter, Setter
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
// Getter, Setter
}
@ManyToOne
어노테이션을 사용해 다대일 관계를 정의하고, @JoinColumn
을 통해 외래 키 컬럼을 명시합니다.
다대일 양방향 매핑 (N:1, 1:N)
팀과 회원 간의 연관관계를 양방향으로 설정하려면 양쪽 엔티티가 서로를 참조해야 합니다. 이때 연관관계의 주인은 외래 키를 가진 쪽, 즉 "다"쪽인 Member
엔티티입니다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
// Getter, Setter
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// Getter, Setter
}
양방향 매핑에서는 mappedBy
속성을 사용하여 연관관계의 주인이 아님을 명시해야 합니다. 이를 통해 연관관계를 설정하고 관리할 수 있습니다.
2. 일대다 관계 (1:N)
하나의 엔티티가 여러 다른 엔티티를 참조하는 관계로, 예를 들어 하나의 팀이 여러 회원을 가질 수 있는 상황입니다. 이때는 보통 자바의 Collection
타입(List
, Set
, Map
)을 사용하여 매핑합니다. 가장 많이 사용되는 것은 List
이며, 데이터의 순서를 보장해야 하거나 중복을 허용할 때 유용합니다.
일대다 단방향 매핑의 단점
일대다 단방향 매핑은 외래 키가 다른 테이블에 존재하기 때문에 저장할 때 추가적인 UPDATE
쿼리가 발생합니다. 이는 성능상의 단점으로 이어질 수 있습니다. 예를 들어, 회원을 생성하고 나서 팀과의 연관관계를 설정하려면 INSERT
쿼리 이후에 추가로 UPDATE
가 발생하게 됩니다. 다음과 같은 예시를 통해 확인해보겠습니다.
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "team_id") // MEMBER 테이블에 외래 키가 생성됨
private List<Member> members = new ArrayList<>();
// Getter, Setter
}
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
// Getter, Setter
}
위와 같은 구조에서 Member
엔티티가 생성되고 나서 Team
과 연관관계를 설정한다는 것은 Member
객체가 어느 팀에 속하는지 지정하는 것을 의미합니다. 예를 들어, Member
객체의 team
필드에 특정 Team
객체를 할당하는 것입니다. 다음과 같은 자바 코드를 통해 연관관계를 설정할 수 있습니다:
Member member = new Member();
Team team = new Team();
team.setName("Team A");
// 팀을 먼저 저장
entityManager.persist(team);
// 회원에 팀을 설정
member.setTeam(team);
entityManager.persist(member);
위 코드에서는 member.setTeam(team)
을 통해 회원 객체에 팀을 지정하여 연관관계를 설정합니다. 이를 통해 JPA가 외래 키 값을 적절히 설정할 수 있게 됩니다. 다음과 같은 쿼리가 발생합니다:
INSERT INTO MEMBER (username) VALUES ('John Doe')
;UPDATE MEMBER SET team_id = 1 WHERE id = 1
;
이처럼 두 번의 쿼리가 발생하는 것은 성능에 부담을 줄 수 있습니다.
따라서 이런 경우에는 다대일 양방향 매핑을 사용하는 것이 더 좋습니다. 다대일 매핑을 사용하면 외래 키가 "다"쪽에 위치하기 때문에 연관관계를 설정하면서 한 번의 INSERT
로 저장할 수 있어 효율적입니다. 다음은 다대일 양방향 매핑의 예시입니다:
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
// Getter, Setter
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// Getter, Setter
}
회원 객체를 생성한 후 이를 특정 팀과 연결할 수 있습니다.
Team team = new Team();
team.setName("Team A");
entityManager.persist(team);
Member member = new Member();
member.setUsername("John Doe");
member.setTeam(team); // 연관관계를 설정하는 부분
entityManager.persist(member);
위 코드에서는 member.setTeam(team)
을 호출함으로써 Member
와 Team
간의 관계를 설정합니다. 이를 통해 JPA는 외래 키 값을 자동으로 지정하게 됩니다. 이렇게 설정하여 한 번의 INSERT
로 처리할 수 있습니다:
INSERT INTO MEMBER (username, team_id) VALUES ('John Doe', 1)
;
이렇게 하면 추가적인 UPDATE
쿼리가 발생하지 않아 성능적으로 더 효율적입니다.
3. 일대일 관계 (1:1)
일대일 관계에서는 양쪽 엔티티가 서로 하나의 관계만을 가집니다. 예를 들어, 회원은 하나의 사물함을 사용하고, 사물함도 하나의 회원에게만 할당되는 경우를 생각할 수 있습니다.
일대일 관계에서 외래 키를 주 테이블에 가질지, 대상 테이블에 가질지 선택할 수 있습니다.
- 주 테이블(회원)에 외래 키: 회원이 사물함을 참조(회원.사물함)하는 방식으로, 주로 사용되는 방식입니다.
- 대상 테이블(사물함)에 외래 키: 사물함이 회원을 참조(사물함.회원)하는 방식입니다. 이 경우 지연 로딩을 사용할 때 프록시로 관리하는 데 한계가 있습니다.
[참고] 일대일 관계에서는 외래 키가 대상 테이블에 있는 경우, 해당 테이블에 접근할 때마다 즉시 로딩을 해야 올바르게 참조할 수 있는 문제가 발생할 수 있습니다. 이를 해결하려면 프록시 대신에 바이트코드 조작(bytecode instrumentation)을 사용하거나, 주 테이블에 외래 키를 두는 설계가 더 유리할 수 있습니다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
// Getter, Setter
}
@Entity
public class Locker {
@Id @GeneratedValue
private Long id;
@OneToOne(mappedBy = "locker")
private Member member;
// Getter, Setter
}
4. 다대다 관계 (N:N)
JPA에서 다대다 관계는 중간 테이블을 사용하여 풀어내야 합니다. 예를 들어 회원이 여러 상품을 주문하고, 하나의 상품이 여러 회원에 의해 주문될 수 있다면 다대다 관계가 됩니다. 이를 표현하기 위해 중간 테이블(Member_Product
)을 추가합니다.
다대다 매핑의 한계와 극복
@ManyToMany
어노테이션을 사용하면 JPA가 중간 테이블을 자동으로 관리해 주기 때문에 매우 간편하지만, 실무에서는 중간 테이블에 추가적인 컬럼(예: 주문 날짜, 수량 등)이 필요할 때가 많습니다. 이 경우에는 @ManyToMany
를 사용할 수 없으며, 중간 엔티티를 직접 생성하여 매핑해야 합니다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
// Getter, Setter
}
@Entity
public class Product {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProducts = new ArrayList<>();
// Getter, Setter
}
// 중간 테이블
@Entity
public class MemberProduct {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
private int orderQuantity;
private LocalDate orderDate;
// Getter, Setter
}
이렇게 다대다 관계를 풀어서 매핑하면 중간 엔티티에 추가적인 정보를 담을 수 있어 실무에서 유용하게 사용할 수 있습니다.
마무리하며
JPA에서 연관관계를 매핑하는 다양한 방법들을 살펴보았습니다. 다대일, 일대다, 일대일, 다대다 관계를 각각 상황에 맞게 적절히 사용하는 것이 중요합니다. 특히 양방향 연관관계에서는 연관관계의 주인을 명확히 정하고, 효율적인 쿼리를 생성할 수 있도록 설계하는 것이 핵심입니다.
'교육 > 책' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] JPA 복합 키 매핑: @IdClass vs @EmbeddedId, 조인컬럼과 조인테이블 (7장-2) (0) | 2024.12.01 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] JPA 고급 매핑: 상속 관계 매핑(7장-1) (0) | 2024.12.01 |
[자바 ORM 표준 JPA 프로그래밍] JPA 연관관계 매핑 기초: 단방향과 양방향 연관관계 (5장) (0) | 2024.11.23 |
[자바 ORM 표준 JPA 프로그래밍] @Entity부터 기본 키 매핑까지 (4장) (0) | 2024.11.21 |
[자바 ORM 표준 JPA 프로그래밍] 영속성 컨텍스트와 엔티티 생명주기 (3장) (0) | 2024.11.16 |