김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.
1. 다대일 & 일대다
구분 | 다대일 | 일대다 |
다대일 + 일대다
|
관계 방향 | 단방향 (N → 1) | 단방향 (1 → N) |
양방향 (N → 1 + 1 → N)
|
설명 | 여러 개의 엔티티가 하나의 엔티티 참조 | 하나의 엔티티가 여러 개의 엔티티 참조 |
서로 참조 (객체 탐색 유리)
|
외래 키 위치 | N쪽 | N쪽 | N쪽 |
연관관계 주인 | N쪽 | N쪽 | N쪽 |
장점 | 구조가 단순하고 성능에 유리함 | 구조가 단순하고 성능에 유리함 |
객체 그래프 탐색 유리
비즈니스 로직 구현 용이 |
단점 | INSERT시 외래 키 직접 관리 필요 | INSERT시 외래 키 직접 관리 필요 |
연관관계 주인 설정 필요
객체간 참조 유지 필수 |
사용 예 | 단순 조회 트랜잭션 설계가 명확한 경우 |
복잡한 비즈니스 로직 객체 탐색이 자주 필요한 경우 |
복잡한 비즈니스 로직 객체 탐색이 자주 필요한 경우 |
예제) 다대일
더보기
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name=”TEAM_ID”)
private Team team;
}
Team team = new Team("TeamA");
Member member = new Member("Member1");
member.setTeam(team);
em.persist(team);
em.persist(member);
예제) 일대다
더보기
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
public void addMember(Member member) {
members.add(member);
}
}
- Member 테이블의 TEAM_ID를 외래키로 참조하여 연관 member들을 List로 받아옵니다.
Team team = new Team("TeamA");
Member member1 = new Member("member1");
Member member2 = new Member("member2");
team.addMember(member1);
team.addMember(member2);
em.persist(team);
em.persist(member1);
em.persist(member2);
예제) 다대일 + 일대다 (양방향)
더보기
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
@ManyToOne // 연관관계의 주인
@JoinColumn(name = "TEAM_ID") // 외래키가 member 테이블에 생성됨
private Team team;
public Member() {}
public Member(String username) {
this.username = username;
}
public void setTeam(Team newTeam) {
// 기존 team과 관계 제거
if (this.team != null) {
this.team.getMembers().remove(this);
}
// 새로운 team과 연결
this.team = newTeam;
// 새로운 team에 현재 member가 없다면 추가
if (newTeam != null && !newTeam.getMembers().contains(this)) {
newTeam.getMembers().add(this);
}
}
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team") // 연관관계의 주인이 아님
private List<Member> members = new ArrayList<>();
public Team() {}
public Team(String name) { this.name = name; }
// 양방향 동기화 도우미
// 생략 가능 (연관관계 주인만 연산 가능하기 때문)
public void addMember(Member member) {
members.add(member);
member.setTeam(this); // 양방향 연관관계 설정
}
}
- 양방향은 외래 키가 있는 쪽이 연관관계의 주인입니다.
- 양방향 연관관계는 항상 서로를 참조해야 합니다.
Team teamA = new Team("TeamA");
Team teamB = new Team("TeamB");
Member member1 = new Member("member1");
Member member2 = new Member("member2");
// 양방향 연관관계 설정
member1.setTeam(teamA);
member2.setTeam(teamB);
em.persist(teamA);
em.persist(teamB);
em.persist(member1);
em.persist(member2);
2. 다대다
@JoinTable
- 두 테이블 사이에 존재하는 연결 테이블을 명시적으로 매핑할 때 사용
속성 | 설명 |
name |
연결테이블 이름을 지정합니다.
|
joinColumns |
현재 방향의 조인 컬럼 정보를 지정합니다.
|
inverseJoinColumns |
반대 방향의 조인 컬럼 정보를 지정합니다.
|
종류
항목 | 단방향 | 양방향 | 연결 엔티티 방식 (권장) |
애노테이션 | @ManyToMany + @JoinTable | 주인: @ManyToMany + @JoinTable 반대: @ManyToMany(mappedBy="...") |
@ManyToOne +
@IdClass or @EmbeddedId |
관계 표현 방식 | 엔티티 2개로 표현 (연결 테이블은 자동 생성됨) |
양쪽 모두 참조 가능 |
연결 테이블을 엔티티로 분리
|
주인 설정 | 단방향이므로 주인 개념 X | 외래키가 있는 쪽이 주인 (mappedBy로 지정) |
외래키를 가지는 필드를 주인으로 설정
|
추가 컬럼 | ❌ | ❌ |
✅ (등록일, 상태 등 컬럼 추가 가능)
|
식별 관계 | ❌ | ❌ |
✅ (복합 외래키 → 복합 기본키 방식)
|
사용 추천 | 단순한 관계 | 단순한 관계이지만 주의 필요 |
✅ 실무 권장 (확장성, 유연성)
|
예제
예제) 단방향
더보기
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT",
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<Product>();
//...
}
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private String id;
private String name;
...
}
예제) 양방향
더보기
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT",
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
private List<Product> products = new ArrayList<Product>();
//...
}
@Entity
public class Product {
@Id
@Column(name = "PRODUCT_ID")
private String id;
private String name;
@ManyToMany(mappedBy = "products")
private List<Member> members;
...
}
- 다대다 매핑이므로 역방향도 @ManyToMany 사용합니다.
예제) 연결 엔티티
더보기
복합 식별키 클래스
public class MemberProductId implements Serializable {
private String member; //MemberProduct.member와 연결
private String product; //MemberProduct.product와 연결
@Override
public boolean equals(Object o) {...}
@Override
public int hashCode() {...}
}
- Serializable, default constructor, equals(), hashCode() 구현 필수
연결 테이블
@Entity
@IdClass(MemberProductId.class) // 복합 기본키 매핑을 설정하는 어노테이션 입니다.
public class MemberProduct {
@Id
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member; //MemberProductId.member와 연결
@Id
@ManyToOne
@JoinColumn(name = "PRODUCT_ID")
private Product product; //MemberProductId.product와 연결
private int orderAmount;
private DateTime createdDate;
// ...
}
3. 일대일
- 양쪽이 서로 하나의 관계만 가집니다.
예제
더보기
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
Locker locker;
}
@Entity
public class Locker {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "LOCKER_ID")
private Long id;
private String name;
}
'Spring > Spring Data JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 8. 프록시와 연관관계 관리 (0) | 2023.12.28 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 7. 고급 매핑 (2) | 2023.12.28 |
[자바 ORM 표준 JPA 프로그래밍] 5. 연관관계 매핑 기초 (0) | 2023.12.27 |
[자바 ORM 표준 JPA 프로그래밍] 4. 엔티티 매핑 (1) | 2023.11.28 |
[자바 ORM 표준 JPA 프로그래밍] 3. 영속성 관리 (0) | 2023.11.28 |