반응형
N:M 관계 예제
- 상황 : 저자는 여러 책을 쓸 수 있고 학원도 여러 수험생을 받을 수 있는 N:M (다대다) 관계
- 관계형 DB는 N:M 관계를 표현할 수 없음 => 1:N, N:1 관계로 풀어내야 함
- EXAMINEE와 ACADEMY 사이에 EXAMINEE_ACADEMY 테이블을 추가로 생성해 풀어냄
- 이 상황에서는 EXAMINEE_ACADEMY가 두 관계에서 연관관계의 주인으로 지정
- 이렇게 하면 또다른 장점은 추가적인 정보를 더 집어넣을 수 있음
- 예를 들면 수험생이 학원을 등록한 날짜 등을 추가로 집어넣을 수 있음 => 예제에서 등록 날짜까지 구현
구현 코드
Examinee Entity
@Entity
@Data
public class Examinee {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "examinee_id")
private Long id;
private String name;
private int age;
// 연관관계 매핑
@OneToMany(mappedBy = "examinee")
private List<ExamineeAcademy> examineeAcademies = new ArrayList<>();
}
Academy Entity
@Entity
@Data
public class Academy {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "academy_id")
private Long id;
private String name;
// 연관관계 매핑
@OneToMany(mappedBy = "academy")
private List<ExamineeAcademy> examineeAcademies = new ArrayList<>();
}
ExamineeAcademy Entity
- 다대다 관계를 풀어내기 위한 객체(테이블)
@Entity
@Data
public class ExamineeAcademy {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "examinee_academy_id")
private Long id;
private LocalDateTime registerDate;
// 연관관계 매핑
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "examinee_id")
private Examinee examinee;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "academy_id")
private Academy academy;
}
Repository
- JpaRepository를 상속받은 ExamineeRepository, AcademyRepository 뿐만 아니라 ExamineeAcademyRepository 생성
초기 데이터 생성
- 수험생 (Tom, 18), (Alice, 17), (Harry, 16) 3명과 수학학원, 영어학원, 국어학원 생성
학원 등록 기능 구현
- examineeId와 academyId를 입력 받아 학원 등록
입력받을 때는 DTO 사용 (DTO 코드 생략) - 학원 등록 시 등록 시간으로 현재 시간 추가
@PostMapping("/register-academy")
public String registerAcademy(@RequestBody RegisterRequest request) {
Examinee examinee = examineeRepository.findById(request.getExamineeId()).get();
Academy academy = academyRepository.findById(request.getAcademyId()).get();
ExamineeAcademy newRegister = new ExamineeAcademy();
newRegister.setExaminee(examinee);
newRegister.setAcademy(academy);
newRegister.setRegisterDate(LocalDateTime.now());
examineeAcademyRepository.save(newRegister);
return String.format("%s 수험생이 %s에 등록하였습니다.", examinee.getName(), academy.getName());
}
학원 등록 기능 구현 결과
학원 등록 조회 기능 구현
- Tom => 수학학원, 영어학원 등록
- Alice => 수학학원, 영어학원, 국어학원 등록
- Harry => 수학학원 등록
- 위 상황에서 모든 수험생들의 등록 기록을 출력하는 기능
- 출력할때는 DTO 사용 => 모든 examineeAcademy를 불러오고 이를 모두 순회하면서 examineeId에 해당하는 수험생의 이름과 academyId에 해당하는 학원의 이름으로 변환 후 출력
@GetMapping("/register-academy")
public String showRegisterAcademy() {
List<ExamineeAcademy> examineeAcademies = examineeAcademyRepository.findAll();
List<ExamineeAcademyDto> dto = new ArrayList<>();
for(ExamineeAcademy examineeAcademy : examineeAcademies) {
dto.add(ExamineeAcademyDto.of(examineeAcademy));
}
return dto.toString();
}
// ExamineeAcademyDto
@AllArgsConstructor
@Builder
@ToString
public class ExamineeAcademyDto {
private String examineeName;
private String academyName;
private LocalDateTime registerDate;
public static ExamineeAcademyDto of(ExamineeAcademy examineeAcademy) {
return ExamineeAcademyDto.builder()
.examineeName(examineeAcademy.getExaminee().getName())
.academyName(examineeAcademy.getAcademy().getName())
.registerDate(examineeAcademy.getRegisterDate())
.build();
}
}
학원 등록 조회 기능 구현 결과
수험생 조회 기능 구현
- 수험생을 조회하면 이름, 나이 뿐만 아니라 등록한 학원의 이름들을 출력
- 이 때에도 DTO를 사용
- DTO에서 수험생이 등록학 학원 리스트를 조회하며 각각의 학원 이름만 추출하는 기능 구현
@GetMapping("/examinee/{examineeId}")
public String showExaminee(@PathVariable Long examineeId) {
Examinee examinee = examineeRepository.findById(examineeId).get();
return ExamineeDto.of(examinee).toString();
}
// ExamineeDto
@AllArgsConstructor
@Builder
@ToString
public class ExamineeDto {
private Long id;
private String name;
private int age;
private List<String> academyNames;
public static ExamineeDto of(Examinee examinee) {
return ExamineeDto.builder()
.id(examinee.getId())
.name(examinee.getName())
.age(examinee.getAge())
.academyNames(examinee.getExamineeAcademies().stream().map(list -> {
return list.getAcademy().getName();
}).collect(Collectors.toList()))
.build();
}
}
수험생 조회 기능 구현 결과
반응형
'Spring Boot > 문법 정리' 카테고리의 다른 글
[Spring Boot] Cookie를 사용한 로그인 구현 (1) | 2023.01.02 |
---|---|
[Spring Boot] 로그인 구현 방법 정리 (6) | 2023.01.01 |
[Spring Boot] 연관관계 매핑 - 1:1 관계 예제 (+ @PostConstruct를 사용한 초기 데이터 생성) (0) | 2023.01.01 |
[Spring Boot] 연관관계 매핑 - 1:N 관계 예제 (+FetchType.LAZY) (0) | 2022.12.31 |
[Spring Boot] 연관관계 매핑 - 1:1, 1:N, N:M (0) | 2022.12.31 |