프로젝트 만들다가 일대다 vs 다대일 단방향 관계를 만나다

LeeKyoungChang·2023년 12월 27일
0
post-thumbnail

🤔 현재 상황

✔️ 시험 문제에서 발생한 연관관계

  • 시험 문제마다 객관식, 주관식 문제들이 여러개 존재한다. => N : 1
  • 시험마다 키워드가 여러 개 필요하다. => N : 1
  • 객관식 문제마다 1 ~ 5번까지 답안이 있다. => 5 : 1

 

이로 인해서

스크린샷 2023-12-28 오전 5 12 56
  • 시험문제 <-> 객관식
  • 시험문제 <-> 주관식
  • 객관식 <-> 답안
  • 시험문제 <-> 문제 키워드

4개가 단방향 연관관계를 맺어야 하는 상황이다.

 

✔️ 연관관계

단방향 연관관계로는 일대다, 다대일이 존재한다.

 

 

✏️ 두 개의 단방향 연관관계 적용

📖 A. 일대다 단방향 연관관계

Exam <-> ExamProblem

@Entity  
@AllArgsConstructor(access = PRIVATE)  
@NoArgsConstructor  
@Getter  
@Builder  
public class Exam extends BaseEntity {  
  
    @Id  
    @GeneratedValue(strategy = IDENTITY)  
    private Long id;  
  
    @NotNull  
    private String subject;  
  
    @CreatedDate  
    private LocalDateTime examTime; // 시험 시작 시간  
  
    private LocalDateTime examEndTime; // 시험 응시 종료 시간  
  
    @NotNull  
    private LocalDateTime deadLine; // 시험 과제 마감일  
  
    @NotNull  
    private int memberCount; // 스터디원 수  
  
    @NotNull  
    private int submittedCount; // 문제를 제출한 멤버 수  
  
    @NotNull  
    private int multiChoiceProblemCount; // 팀원들이 객관식 만들어야하는 문제 수 (만들어야하는 문제 수보다 적게 입력된 경우 재 입력?)  
  
    @NotNull  
    private int shortAnswerProblemCount; // 팀원들이 주관식 만들어야하는 문제 수  
  
    @NotNull  
    private int totalMultiChoiceProblemCount; // 시험 객관식 문제 수  
  
    @NotNull  
    private int totalShortAnswerProblemCount; // 시험 주관식 문제 수  
  
  
    // 현재 이와 같은 entity에서 N : 1 관계일 때  
    // JoinColum  
  
    // fetch = LAZY : 기본적으로 지연로딩이 되어서 선언할 필요가 없다.  
	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<ExamProblem> examProblems = new ArrayList<>();  
  
    // 객관식 문제  
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<MultipleChoiceExam> multipleChoiceExams = new ArrayList<>();  
  
    // 주관식 문제  
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<SubjectExam> subjectExams = new ArrayList<>();  
  
    // 시험 키워드  
	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<ExamKeyword> examKeywords = new ArrayList<>();  
}
@Entity  
@AllArgsConstructor(access = PRIVATE)  
@NoArgsConstructor  
@Getter  
public class ExamProblem {  
  
    @Id  
    @GeneratedValue(strategy = IDENTITY)  
    private Long id;  
  
    @NotNull  
    private String examType; // 문제 유형  
  
    @NotNull  
    private String examTitle; // 스터디 문제 제목  
  
    @NotNull  
    private String examContent; // 스터디 문제 내용
    
}

 

Exam에서 @OneToMany 어노테이션을 통해 일대다 연관관계를 맺었다.

	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<ExamProblem> examProblems = new ArrayList<>();  
  
    // 객관식 문제  
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<MultipleChoiceExam> multipleChoiceExams = new ArrayList<>();  
  
    // 주관식 문제  
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<SubjectExam> subjectExams = new ArrayList<>();  
  
    // 시험 키워드  
	@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)  
    @JoinColumn(name = "exam_id")  
    private List<ExamKeyword> examKeywords = new ArrayList<>();  

 

📖 B. 다대일 단방향 연관관계

Exam <-> ExamProblem

@Entity  
@AllArgsConstructor(access = PRIVATE)  
@NoArgsConstructor  
@Getter  
@Builder  
public class Exam extends BaseEntity {  
  
    @Id  
    @GeneratedValue(strategy = IDENTITY)  
    private Long id;  
  
    @NotNull  
    private String subject;  
  
    @CreatedDate  
    private LocalDateTime examTime; // 시험 시작 시간  
  
    private LocalDateTime examEndTime; // 시험 응시 종료 시간  
  
    @NotNull  
    private LocalDateTime deadLine; // 시험 과제 마감일  
  
    @NotNull  
    private int memberCount; // 스터디원 수  
  
    @NotNull  
    private int submittedCount; // 문제를 제출한 멤버 수  
  
    @NotNull  
    private int multiChoiceProblemCount; // 팀원들이 객관식 만들어야하는 문제 수 (만들어야하는 문제 수보다 적게 입력된 경우 재 입력?)  
  
    @NotNull  
    private int shortAnswerProblemCount; // 팀원들이 주관식 만들어야하는 문제 수  
  
    @NotNull  
    private int totalMultiChoiceProblemCount; // 시험 객관식 문제 수  
  
    @NotNull  
    private int totalShortAnswerProblemCount; // 시험 주관식 문제 수
    
}
@Entity  
@AllArgsConstructor(access = PRIVATE)  
@NoArgsConstructor  
@Getter  
public class ExamProblem {  
  
    @Id  
    @GeneratedValue(strategy = IDENTITY)  
    private Long id;  
  
    @NotNull  
    private String examType; // 문제 유형  
  
    @NotNull  
    private String examTitle; // 스터디 문제 제목  
  
    @NotNull  
    private String examContent; // 스터디 문제 내용  
  
    @ManyToOne  
    @JoinColumn(name = "exam_id", nullable = false)  
    private Exam exam;  
  
}

 

ExamProblem에서 @ManyToOne 어노테이션을 통해 일대다 연관관계를 맺었다.

    @ManyToOne  
    @JoinColumn(name = "exam_id", nullable = false)  
    private Exam exam;  

 

💡 참고

참고로, 현재는 ExamProblem 엔티티만 표현했지만 ExamKeyword, SubjectExam, MultipleChoiceExam 엔티티 모두에 Exam엔티티 관련 ManyToOne 어노테이션이 선언되어 있다.

 

 

📑 두 개의 단방향 연관관계 장단점

📖 A. ManyToOne의 장단점

✔️ 장점

  • 성능 : ManyToOne은 일반적으로 OneToMany 보다 성능이 좋다. 자식 테이블에 외래 키를 유지하므로, 부모 엔티티를 조회할 때 추가 조인이 필요 없다.
  • 데이터 일관성 : 자식 엔티티에서 부모 엔티티로의 단방향 참조는 데이터 일관성 관리가 더 간단하다.

 

✔️ 단점

  • 접근성 : 부모 엔티티에서 자식 엔티티로 직접 접근할 수 없어서, 부모 엔티티를 기준으로 자식 엔티티를 찾으려면 별도의 쿼리가 필요한 상황이다. (ExamProblem 테이블 조회 -> 받은 부모 id -> Exam 테이블 조회)

 

📖 B. OneToMany의 장단점

✔️ 장점

  • 직관적인 접근 : 부모 엔티티에서 자식 엔티티로의 접근이 직관적이고 간단하다. 부모 엔티티 로드할 때 자식 엔티티도 함께 접근할 수 있다.
  • 엔티티 간의 관계가 명확하다.

 

✔️ 단점

  • 성능 이슈 : OneToMany 관계는 종종 성능 문제를 야기할 수 있다. 특히 Lazy 로딩을 사용하지 않으면, 부모 엔티티를 로드할 때마다 자식 엔티티도 함께 로드될 수 있다.
  • 조인의 복잡성 : 자식 엔테티가 많은 경우, 부모 엔티티를 조회할 때 많은 조인이 발생할 수 있다.

 

✏️ 현재 상황에서 데이터 접근을 고려했을 때

  • 객관식 문제와 주관식 문제 유형은 서로 다른 특성을 가질 수 있으며, 별도의 엔티티로 관리하는 것이 자연스럽다. : 일대다 연관관계가 좋은 것 같다. (문제를 추가하거나 삭제할 때 유연하게 처리할 수 있다.)
  • 객관식 문제마다 옵션이 1 ~ 5까지 정확하게 존재하는 경우 : 일대다 연관관계가 좋은 것 같다.
  • 각각의 시험키워드은 여러 개의 시험에서 추가될 수 있기 때문에 : 일대다 연관관계가 좋은 것 같다. (만약, 시험키워드가 하나의 시험에만 해당되는 경우 다대일이 좋다.)

 

💡 참고
현재와 다르게 자식 엔티티의 수가 많은 경우 OneToMany 관계가 성능 저하를 초래할 수 있다.
데이터의 일관성과 트랜잭션 관리의 복잡성도 고려해야 한다.

 

 

profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"

0개의 댓글