Spring Boot에서 ManyToOne, OneToMany, ManyToMany 관계를 설정하는 방법에 대해서 알아보겠습니다. 다음은 전체 테이블 설계입니다.
1과 가까운 테이블은 일을 나타내고, *와 가까운 테이블을 다를 나타냅니다. 우선 Entity 구현 간에 사용되는 어노테이션들에 대해서 알아보겠습니다.
- @Id - 해당 변수를 Primary Key로 지정해준다.
- @GeneratedValue - Primary Key의 생성 전략을 명시한다.
- @Column - 실제 DB의 Column에 대응하는 변수라는 것을 명시한다.
- @Entity - 실제 DB의 테이블에 대응하는 클래스라는 것을 명시한다.
- @Getter - 변수를 선언하면 자동으로 Getter 코드를 만든다.
- @Setter - 변수를 선언하면 자동으로 Setter 코드를 만든다.
- @NoArgsConstructor - 파라미터가 없는 생성자를 만든다.
- @NoArgsConstructor(access = AccessLevel.PROTECTED) - protected로 선언된 파라미터가 없는 생성자를 만든다.
🚨 id는 모든 Entity에 공통으로 들어가므로 차이를 두기 위해 @Column을 사용하여 <클래스 이름>_id로 이름을 변경해줍니다.
다음은 Owner Entity 코드입니다. Owner는 반려동물의 주인을 저장하는 Entity입니다.
...
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Owner extends BaseTimeEntity{
@Id @GeneratedValue
@Column(name = "owner_id")
Long id;
private String telephone;
private String name;
@OneToMany(mappedBy = "owner")
private List<Pet> pet = new ArrayList<>();
}
1명의 주인은 여러 마리의 반려동물을 가질 수 있기 때문에 OneToMany의 관계를 가지게 됩니다. 따라서 연관관계의 주인은 Many 쪽인 Pet이 가지게 됩니다. 따라서 @OneToMany(mappedBy = "owner")를 추가하여 Pet의 owner 정보를 따르는 것으로 설정합니다.
다음은 Pet Entity 코드입니다. Pet은 반려동물에 대한 정보를 가지고 있는 Entity입니다.
...
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Pet {
@Id @GeneratedValue
@Column(name = "pet_id")
private Long id;
private String name;
private LocalDate birthDate; // 생일
@OneToOne(fetch = LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "pet_type_id")
private PetType petType;
}
반려동물은 생일과 이름을 입력받습니다. @OneToOne을 사용하여 PetType과 일대일의 관계가 있다는 것을 명시해주는 것과 동시에 Pet과 PetType의 연관 관계에서도 Pet이 주인이므로 @JoinColumn 정보를 설정해줍니다.
다음은 PetType Entity 코드입니다. PetType은 간단한 설명을 입력할 수 있게 만들었습니다.
...
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PetType {
@Id @GeneratedValue
@Column(name = "pet_type_id")
private Long id;
private String description;
@OneToOne(mappedBy = "petType", fetch = LAZY)
private Pet pet;
}
description을 통해 간단한 설명을 입력받을 수 있게 설정하였습니다. Owner 때와 마찬가지로 PetType은 연관관계의 주인이 아니기 때문에 @OneToOne(mappedBy = "petType", fetch = LAZY)를 설정해주어 Pet의 petType 정보를 따르는 것으로 설정해줍니다.
다음은 Chart Entity 코드입니다. Chart는 진료에 대한 정보를 가지고 있습니다.
...
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Chart extends BaseTimeEntity{
@Id @GeneratedValue
@Column(name = "hospitalization_id")
private Long Id;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "pet_id")
private Pet pet;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "vet_id")
private Vet vet;
private String diseaseName;
private String description;
@Enumerated(EnumType.STRING)
private ChartStatus status; // READY, ADMISSION, COMPLETE
}
1마리의 반려동물은 진료 횟수에 따라 여러 개의 chart를 가지고 여러 명의 vet을 가지게 됩니다. 반대의 경우도 가능합니다. 따라서 chart는 pet, vet을 모두 일대다의 관계를 가지게 됩니다. 이때 chart를 연관관계의 주인으로 설정하여 @JoinColumn을 추가해줍니다. status에 @Enumerated라는 어노테이션이 추가되어 있는데 Enum을 입력으로 받을 수 있는 필드로 선언해주기 위해서 추가된 것입니다. 따라서 다음의 ChartStatus Enum을 제작하여 줍니다. READY는 진료 접수, ADMISSION은 진료 승인, COMPLETE는 퇴원 후를 나타탭니다.
public enum ChartStatus {
READY, ADMISSION, COMPLETE
}
다음은 Vet Entity 코드입니다. Vet은 수의사의 정보를 가지고 있습니다.
...
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Vet extends BaseTimeEntity {
@Id
@GeneratedValue
@Column(name = "vet_id")
private Long id;
private String name;
private String major;
@ManyToMany(mappedBy = "vet", cascade = CascadeType.ALL)
private List<MedicalCare> medicalCare = new ArrayList<>();
}
수의사는 진료항목을 여러 가지로 가질 수 있고, 반대의 경우도 가능하기 때문에 Vet과 MedicalCare는 다대다의 관계를 가지게 됩니다. 이 경우 호출 횟수를 기준으로 연관관계의 주인을 설정합니다. 따라서 MedicalCare를 연관관계의 주인으로 설정하고, @ManyToMany(mappedBy = "vet", cascade = CascadeType.ALL)를 추가하여 MedicalCare의 vet을 따르는 것으로 설정해줍니다.
다음은 MedicalCare Entity의 코드입니다. MedicalCare는 진료항목에 대한 정보를 가지고 있습니다.
...
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MedicalCare extends BaseTimeEntity{
@Id @GeneratedValue
@Column(name = "medical_care_id")
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "medical_care_item",
joinColumns = @JoinColumn(name = "medical_care_id"),
inverseJoinColumns = @JoinColumn(name = "vet_id"))
private List<Vet> vet = new ArrayList<>();
}
다대다의 관계를 가지기 때문에 @JoinTable 어노테이션을 사용하여 joinColumns에는 연관관계의 주인이 되는 Column 정보를 넣어주고, inverseJoinColumns에는 연관관계의 주인이 아닌 Column의 정보를 넣어주면 된다.
일대일, 일대다, 다대일, 다대다와 같이 연관관계가 존재하는 경우에는 연관관계의 주인을 설정해야 합니다. 일반적으로 주인은 연관관계를 가지는 두 테이블에서 외래 키가 가까운 테이블 또는 일대다에서 다에 해당하는 테이블을 주인으로 설정합니다.
Pet, PetType은 일대일 관계이지만 테이블 설계를 보면 Pet이 더 많은 관계를 가지고 있고, 호출 횟수를 생각해보아도 Pet이 더 많기 때문에 Pet을 주인으로 설정하였습니다. 주인으로 설정하는 방법은 주인이 되는 테이블에 @JoinColumn(name = <다른 클래스의 Primary Key 이름>)을 추가하고, 다른 테이블에는 XToX(mappedBy = <주인 클래스에서 자신의 클래스를 부르는 이름>)를 추가해주면 됩니다.
더 자세한 사항은 다음의 강의를 참고해주시면 될 것 같습니다.
참고자료 - 실전! 스프링 부트와 JAP활용 1, 인프런
'Java, JavaScript > Spring Boot' 카테고리의 다른 글
[Spring Boot] 동물병원 진료 데이터베이스 설계 (0) | 2021.08.31 |
---|---|
[Spring Boot] @Autowired를 사용하지 않고 Bean 가져오기 (0) | 2021.08.27 |
[Spring Boot] IntelliJ Live Templates 생성하는 방법 (0) | 2021.08.26 |
[Spring Boot] Auditing은 뭐야? (0) | 2021.08.23 |
[Spring Boot] Intellij 유용한 단축키 모음 (0) | 2021.08.18 |