"연관관계 매핑"은 객체 지향 프로그래밍과 관계형 데이터베이스 사이에서 관계를 매핑할 때 사용되는 개념입니다.
주로 ORM (Object-Relational Mapping) 프레임워크, Java의 JPA (Java Persistence API) 또는
Python의 SQLAlchemy에서 자주 사용됩니다.
연관관계 매핑 종류와 방향
연관관계를 맺는 두 엔티티 간에 생성할 수 있는 연관관계의 종류
일대일 (OneToOne): 한 엔티티가 다른 엔티티를 하나만 참조할 수 있습니다.
일대다 (OneToMany): 한 엔티티가 여러 개의 다른 엔티티를 참조할 수 있습니다.
다대일 (ManyToOne): 여러 엔티티가 한 개의 다른 엔티티를 참조할 수 있습니다.
다대다 (ManyToMany): 여러 엔티티가 여러 다른 엔티티를 참조할 수 있습니다.
다대다는 실제 데이터베이스 테이블에서는 잘 표현되지 않으므로,
중간 테이블을 사용하여 두 개의 일대다 관계로 분해하여 사용하는 것이 일반적입니다.
연관관계의 방향
단방향: 한 쪽만 다른 쪽을 알고 있는 관계입니다.
양방향: 양쪽 모두 서로를 알고 있는 관계입니다.
양방향 연관관계에서는 주로 "주인"과 "비주인"으로 관계의 주도권을 구분하여,
연관관계의 주인만이 데이터베이스 연관관계와 매핑되는 외래 키를 관리합니다.
일대일 매핑
일대일 단방향 매핑
일대일 단방향 매핑에서는 한 엔티티가 다른 엔티티를 참조하게 되지만, 반대 방향으로는 참조하지 않습니다.
회원(Member)과 회원의 프로필(Profile) 사이에 일대일 관계를 가정해보겠습니다.
각 회원은 하나의 프로필만 가질 수 있습니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "profile_id")
private Profile profile;
// ...
}
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;
// ...
}
일대일 양방향 매핑
일대일 양방향 매핑에서는 한 엔티티가 다른 엔티티를 참조하며, 그 반대 방향 엔티티도 첫 번째 엔티티를 참조합니다.
이때 주의할 점은 연관관계의 주인을 명확히 해야 합니다.
연관관계의 주인만이 데이터베이스 연관관계와 매핑되는 외래 키를 관리하게 됩니다.
Member와 Profile 사이의 일대일 관계에서 양방향으로 연결하겠습니다. 여기서 Member가 연관관계의 주인입니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "profile_id")
private Profile profile;
// ...
}
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String bio;
@OneToOne(mappedBy = "profile")
private Member member;
// ...
}
다대일, 일대다 매핑
다대일 단방향 매핑
다대일 단방향 매핑에서는 여러 엔티티가 한 개의 엔티티를 참조합니다. 참조하는 쪽에만 외래 키가 존재하게 됩니다.
회원(Member)이 여러 개 있을 때, 각 회원은 하나의 팀(Team)에만 속하게 되는 경우를 가정합니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
// ...
}
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// ...
}
다대일 양방향 매핑
다대일 양방향 매핑에서는 여러 엔티티가 한 개의 엔티티를 참조하며, 참조되는 엔티티도 여러 엔티티를 인식합니다.
위의 예시에서 Team 엔티티가 여러 Member 엔티티들을 알 수 있도록 매핑합니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
// ...
}
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// ...
}
일대다 단방향 매핑
일대다 단방향 매핑에서는 한 엔티티가 여러 엔티티를 참조합니다. 하지만 참조된 엔티티 쪽에서는 참조하는 엔티티를 알지 못합니다.
하나의 팀(Team)이 여러 회원(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<>();
// ...
}
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// ... 다른 필드들 ...
}
이 구성에서 Team 엔티티는 여러 Member 엔티티들을 참조합니다.
@JoinColumn을 사용하여 Member 테이블에 team_id 외래 키를 생성하며, 이를 통해 Team과 Member를 연결합니다.
단, 이런 방식의 일대다 단방향 매핑은 권장되지 않는 경우도 있습니다.
JPA의 내부 동작 방식에 따라 테이블에 의도치 않은 UPDATE SQL이 발생할 수 있기 때문입니다.
일대다 양방향 매핑이나 다대일 양방향 매핑을 사용하는 것이 더 효율적일 때가 많습니다
다대다 매핑
다대다 단방향 매핑
다대다 단방향 매핑에서는 한 엔티티가 여러 엔티티를 참조하며, 반대 쪽 엔티티는 참조하는 엔티티를 알지 못합니다.
실제 관계형 데이터베이스에서는 다대다 관계를 직접 표현할 수 없어서, 중간에 연결 테이블(매핑 테이블)이 필요합니다.
학생(Student)과 과목(Course)의 관계를 생각해보겠습니다. 학생은 여러 과목을 수강할 수 있습니다.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private List<Course> courses = new ArrayList<>();
// ...
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// ... 다른 필드들 ...
}
다대다 양방향 매핑
다대다 양방향 매핑에서는 한 엔티티가 여러 엔티티를 참조하고, 반대 쪽 엔티티도 참조하는 엔티티를 인식합니다.
마찬가지로 중간에 연결 테이블이 필요합니다. 양방향 매핑에서는 연관관계의 주인을 정해야 합니다.
학생(Student)과 과목(Course)의 관계에서, 과목 쪽에서도 해당 과목을 수강하는 학생들을 알 수 있도록 합니다.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private List<Course> courses = new ArrayList<>();
// ...
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "courses")
private List<Student> students = new ArrayList<>();
// ...
}
mappedBy 속성을 사용하여 연관관계의 주인이 Student 엔티티의 courses 필드임을 나타냅니다.
다대다 관계는 복잡하며, 실무에서는 연결 테이블을 엔티티로 변환하여 일대다, 다대일 관계로 분리하는 것이 관리하기 좋습니다.
영속성 전이
영속성 전이는 한 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만드는 것을 의미합니다.
CascadeType의 여러 값 중에서 특정 작업을 선택하여 연관된 엔티티에 전이시킬 수 있습니다.
영속성 전이를 사용하는 주요 상황은 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장하는 경우입니다.
주요 Cascade 타입
PERSIST: 부모 엔티티가 저장될 때 자식 엔티티도 함께 저장됩니다.
REMOVE: 부모 엔티티가 삭제될 때 자식 엔티티도 함께 삭제됩니다.
MERGE: 부모 엔티티가 병합될 때 자식 엔티티도 함께 병합됩니다.
REFRESH: 부모 엔티티가 새로고침될 때 자식 엔티티도 함께 새로고침됩니다.
ALL: 모든 변경사항이 자식 엔티티에도 전이됩니다.
예제
부모 엔티티인 Post와 자식 엔티티인 Comment 사이의 관계에서 영속성 전이를 적용하겠습니다.
Post를 저장하거나 삭제할 때 연관된 Comment도 함께 저장하거나 삭제됩니다.
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
@OneToMany(mappedBy = "post", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Comment> comments = new ArrayList<>();
// ...
}
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
@ManyToOne
private Post post;
// ...
}
'BACKEND > SPRING' 카테고리의 다른 글
스프링부트 유효성 검사와 예외처리 - 예외 처리 (1) | 2023.08.27 |
---|---|
스프링부트 유효성 검사와 예외처리 - 유효성 검사 (0) | 2023.08.27 |
Spring Data JPA 활용 - JPA Auditing 적용 (0) | 2023.08.13 |
Spring Data JPA 활용 - @Query 어노테이션과 QueryDSL (0) | 2023.08.13 |
Spring Data JPA 활용 - 정렬과 페이징 처리 (0) | 2023.08.13 |