Skip to content

[Spring Data JPA] 김진학 미션 제출합니다. #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: iampingu99
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ab543f9
feat(member): 조회 결과가 없는 경우 예외 추가
iampingu99 Feb 15, 2025
eff71de
feat(reservations): 예약 엔티티에 멤버 외래키 추가
iampingu99 Feb 15, 2025
da43ffd
feat(reservations): 내 예약 목록 반환 dto 추가
iampingu99 Feb 15, 2025
e6cef74
feat(reservations): 내 예약 목록 조회 기능 추가
iampingu99 Feb 15, 2025
9cad9dd
feat(reservations): 5단계 내 예약 목록 조회 기능 테스트 추가
iampingu99 Feb 15, 2025
dc58c31
feat(reservations): 내 예약 목록 조회 시 N+1 문제 해결
iampingu99 Feb 15, 2025
aad3799
feat(waiting): 예약 대기 엔티티 추가
iampingu99 Feb 16, 2025
b39a720
feat(waiting): 예약 대기 생성 기능 요청/응답 dto 추가
iampingu99 Feb 16, 2025
d6225a4
feat(waiting): 예약 대기 생성 기능 추가
iampingu99 Feb 16, 2025
4eaca4a
feat(waiting): 대기 순위를 포함한 예약 대기 목록 조회 쿼리 추가
iampingu99 Feb 17, 2025
0827b2d
feat(waiting): 예약 목록 조회시 예약 대기 목록 포함하도록 수정
iampingu99 Feb 17, 2025
cbda84c
refactor(reservation): 예약 응답 객체 record 사용 및 팩토리 메서드 추가
iampingu99 Feb 17, 2025
5f18f38
feat(waiting): 예약 대기 확인 쿼리 추가
iampingu99 Feb 18, 2025
d5748cf
feat(waiting): 예약 대기 중복 검사 로직 추가
iampingu99 Feb 18, 2025
9313fb6
refactor(waiting): 예약 대기 생성 테스트 리팩토링
iampingu99 Feb 18, 2025
40890a3
feat(reservation): 예약 확인 쿼리 추가
iampingu99 Feb 18, 2025
918a6c7
feat(reservation): 시간, 날짜, 테마에 대한 예약 조회 쿼리 추가
iampingu99 Feb 18, 2025
169e0e7
feat(waiting): 예약 대기 생성시 예약 검증 로직 추가
iampingu99 Feb 18, 2025
51ccab8
refactor(reservation): 예약 생성 객체 record 로 변환
iampingu99 Feb 18, 2025
4dbc06c
refactor(reservation): entity graph 사용으로 수정
iampingu99 Feb 18, 2025
9fffdc2
refactor(reservation): 사용자 예약과 예약 대기 조회 메서드 분리
iampingu99 Feb 20, 2025
f70b93f
refactor(reservation): 사용자 예약 생성 이름 검증 메서드 분리
iampingu99 Feb 20, 2025
44b821d
refactor(waiting): 예약 대기 및 순서 조회 쿼리 수정
iampingu99 Feb 24, 2025
6daf5ba
refactor(reservation): 불필요한 불변 객체 메서드 사용 제거
iampingu99 Feb 26, 2025
7abfe2d
refactor(reservation): 생성 시 검증 메서드 접근자 변경
iampingu99 Feb 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/roomescape/member/MemberRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,13 @@
import org.springframework.data.repository.CrudRepository;

public interface MemberRepository extends CrudRepository<Member, Long> {

default Member findByEmailOrThrow(String email) {
return findByEmail(email).orElseThrow(
() -> new IllegalArgumentException(String.format("Member not found: %s", email)));
}

Optional<Member> findByEmail(String email);

Optional<Member> findByEmailAndPassword(String email, String password);
}
33 changes: 33 additions & 0 deletions src/main/java/roomescape/reservation/MyReservationResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package roomescape.reservation;

import roomescape.waiting.Waiting;
import roomescape.waiting.WaitingWithRank;

public record MyReservationResponse(
Long id,
String theme,
String date,
String time,
String status
) {
public static MyReservationResponse from(Reservation reservation) {
return new MyReservationResponse(
reservation.getId(),
reservation.getTheme().getName(),
reservation.getDate(),
reservation.getTime().getValue(),
"예약");
}

public static MyReservationResponse from(WaitingWithRank waitingWithRank) {
Waiting waiting = waitingWithRank.getWaiting();
return new MyReservationResponse(
waiting.getId(),
waiting.getTheme().getName(),
waiting.getDate(),
waiting.getTime().getValue(),
waitingWithRank.getRank() + 1 + "번째 예약대기"
);
}
}

21 changes: 10 additions & 11 deletions src/main/java/roomescape/reservation/Reservation.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import roomescape.member.Member;
import roomescape.theme.Theme;
import roomescape.time.Time;

Expand All @@ -21,24 +22,18 @@ public class Reservation {
private Time time;
@ManyToOne(fetch = FetchType.LAZY)
private Theme theme;
@ManyToOne(fetch = FetchType.LAZY)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memberId 가 아니라, Member 라는 객체를 넣은 이유가 있나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Member 객체를 넣게 되는 경우 Reservation 과 Member의 객체 관계를 명확히하여 그래프 탐색이나 연관 객체의 변경에 대한 감지에 용이합니다.
Long 객체로 id 만 저장하는 경우 Member 가 필요한 경우 추가 조회 쿼리가 요구됩니다.

현재는 예약 비즈니스 로직에서 Member 의 id 만을 다루기도 하고 조회 쿼리가 대부분이며 추가 쿼리가 필요하다고는 하나, 영속성 컨텍스트에서 조회될 수 있으므로 Member 객체를 가지기 보단 Long 객체를 가지는 것이 좋아보입니다.
하지만 Member 객체를 사용한다면 조회 시에 프록시를 통해 불필요한 데이터를 가져오지 않을 수도 있으며 명확하게 연관관계를 알 수 있습니다. 또한 요구사항의 추가로 예약 도메인에서 사용자의 다른 필드를 다룰 가능성이 크다고 생각하므로 사용하게 되었습니다.

private Member member;

public Reservation(Long id, String name, String date, Time time, Theme theme) {
this.id = id;
this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
public Reservation() {
}

public Reservation(String name, String date, Time time, Theme theme) {
public Reservation(String name, String date, Time time, Theme theme, Member member) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금 로직은
예약이나 대기가 생성될때마다 중복적인 요소들이 발생할거 같네요.
( Date - Time - Theme들이 중복 )

2월 19일 10:00 공포테마 예약
2월 19일 10:00 공포테마 대기1
2월 19일 10:00 공포테마 대기2
2월 19일 10:00 공포테마 대기3

이에 대한 의견을 들려주세용

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재의 예약 테이블과 예약 테이블에서 중복적인 요소를 제거하기 위해 날짜, 시간, 테마 필드를 스케줄 로 네이밍한 테이블로 분리한 방식에 대한 차이에 대해서 고민한 결과입니다.

  1. 사용자의 예약 목록 조회

    • 분리 하지 않는 경우:
      예약 테이블에서 사용자에 대한 필터링을 통해 가져올 수 있습니다. 2번의 조인으로 예약 필드를 모두 조회가 가능하지만, 예약 테이블에 날짜, 시간, 테마에 대한 중복이 발생할 수 있습니다.

    • 분리하는 경우:
      스케줄 테이블과 예약 테이블에서 사용자에 대한 필터링을 통해 가져올 수 있습니다. 분리를 통해 중복적인 요소가 제거 되었으나, 분리된 예약 필드를 모두 조회하기 위해 예약 테이블과의 조인이 추가로 필요하게 됩니다.

  2. 예약 생성

    • 분리 하지 않는 경우:
      예약 생성을 모든 날짜, 시간, 테마의 조합으로 생성하므로 날짜, 시간, 테마 테이블만 초기화되어 있다면 예약할 수 있지만, 모든 방탈출 예약이 동일한 날짜, 시간, 테마 범위를 가지게 됩니다.

    • 분리하는 경우:
      스케줄 테이블에 생성 되어있는 날짜, 시간, 테마 조합으로만 생성할 수 있습니다. 이처럼 스케줄을 미리 초기화 해야 하지만 가능한 조합을 미리 만들어 둘 수 있으며 서비스가 확장되어 방탈출 제공자의 기능이 추가되었을 때 제공자에 맞는 방탈출 예약 조합을 만들 수 있게 됩니다.

  3. 정규화

    정규화를 통해 불필요한 데이터를 없앨 수 있고, 삽입/갱신/삭제 시 발생할 수 있는 각종 이상현상 들을 방지할 수 있습니다. 하지만 정규화가 잘되더라도 불필요한 데이터나 중복되는 요소가 발생할 수 있습니다. 현재 모든 테이블은 정규화가 잘 되어 있으므로 추가적인 분리를 고려할 시에 중복과 조인 성능 중에서 선택이 필요하다고 생각합니다.

분리를 통해 날짜, 시간, 테마를 매개변수로 받던 메서드들이 스케줄의 주키만을 받아 동작하여 가독성이 좋을 것으로 생각됩니다. 또한 날짜, 시간, 테마 조회가 많이 발생하므로 이를 복합키로 만들기 보다는 새 테이블로 분리해서 조인이 필요하지만 주키의 인덱싱을 사용하는 것이 더 좋을 것이라는 생각이 듭니다.

다른 의견도 궁금합니다 😄

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

질문한 의도는
제가 코드를 작성하며 느낀점으론 객체지향DB설계 는 조금 다른걸 느껴서 입니다.
근데, 이게 완벽하게 객체 지향을 안지켜서 발생하는 문제인가? 싶어서 질문합니다.
객체지향적으로 예약과 대기가 상태를 기반으로 엔티티라면 예약정보는 온전히 분리된 VO인거 같기도 한디? 싶기도 하더라구요.

해당 요소와 같을때는 중복적 + 커버링 인덱스로 검색을 커버 가능
하다는 생각이 들어서 분리를 시도하려고 할 거 같긴 해요.

( 물론, 예약 관련된 내용들이 더 추가될 가능성이 있다면 안할거 같긴 해요. DB 는 최대한 수비적으로 )

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가로, 이를 엔티티로 분리하는 방법도 있고
엔티티는 분리는 하지 않되 객체를 가지게 하고 싶다면 @Embedded 를 학습해도 좋을거 같네요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 코드를 작성하며 느낀점으론 객체지향과 DB설계 는 조금 다른걸 느껴서 입니다.
근데, 이게 완벽하게 객체 지향을 안지켜서 발생하는 문제인가? 싶어서 질문합니다.
객체지향적으로 예약과 대기가 상태를 기반으로 엔티티라면 예약정보는 온전히 분리된 VO인거 같기도 한디? 싶기도 하더라구요.

말씀해주신 내용이 잘 이해가 되지 않는데 조금 더 자세하게 이야기해 주실 수 있으실까요?

  1. 예약과 예약 대기에 대한 분리: 객체지향 관점으로 상태를 기준으로 분리될 수 있다.
  2. 예약 과 (시간, 날짜, 테마) 필드에 대한 분리: 예약 정보가 온전히 분리되어 두 테이블로 분리할 수 있으며 정규화와 인데스를 통해 중복을 줄이고 성능을 개선할 수도 있다.

@Embedded 를 통해서 데이터베이스는 그대로 두고 객체부분에서 응집도를 높일 수 있군요! 한번 학습해보겠습니다. 👍

this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
}

public Reservation() {

this.member = member;
}

public Long getId() {
Expand All @@ -60,4 +55,8 @@ public Time getTime() {
public Theme getTheme() {
return theme;
}

public Member getMember() {
return member;
}
}
12 changes: 8 additions & 4 deletions src/main/java/roomescape/reservation/ReservationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ public List<ReservationResponse> list() {
return reservationService.findAll();
}

@GetMapping("/reservations-mine")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 경로를 reservations-mine 이라고 한 이유가 있나요?
혹시나 타인의 토큰을 받아서 이를 조회하는 경우가 있다면
( 같이 가는 멤버의 토큰을 공유한다. )
이는 어떻게 되나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서비스의 역할에 대해서 고민한 결과입니다.
예약 목록의 경우 민감한 정보로 비공개 즉, 사용자가 다른 사용자의 예약 목록을 조회하지 못해야 한다고 생각했습니다. 따라서 본인의 예약 목록만 조회할 수 있다고 가정한 결과 본인의 의미가 담긴 접미사를 활용하여 /reservations-mine 로 설계하게 되었습니다.

만약에 일행의 토큰을 받아 예약을 조회하는 경우가 있다면, 조회할 사용자의 id 와 파라미터로 토큰을 받아 id 와 토큰의 정보가 일치하는지 검사한 후에 예약 목록을 반환하며, /user/{userId}/reservations 로 설계할 것 같습니다.

추가로 비회원 사용자가 회원인 일행의 토큰을 받아 조회하는 것보다는 공유하는 방법이 더 적절할 것 같습니다.

public ResponseEntity<List<MyReservationResponse>> readAllByMember(LoginMember loginMember) {
List<MyReservationResponse> response = reservationService.readAllByMember(loginMember.email());
return ResponseEntity.ok(response);
}

@PostMapping("/reservations")
public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, LoginMember loginMember) {
reservationRequest.checkName(loginMember.name());
ReservationResponse reservation = reservationService.save(reservationRequest);

return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation);
ReservationResponse reservation = reservationService.save(reservationRequest, loginMember.email());
return ResponseEntity.created(URI.create("/reservations/" + reservation.id())).body(reservation);
}

@DeleteMapping("/reservations/{id}")
Expand Down
15 changes: 13 additions & 2 deletions src/main/java/roomescape/reservation/ReservationRepository.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
package roomescape.reservation;

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import java.util.Optional;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.repository.CrudRepository;
import roomescape.member.Member;
import roomescape.theme.Theme;
import roomescape.time.Time;

public interface ReservationRepository extends CrudRepository<Reservation, Long> {
@Query("SELECT r FROM Reservation r JOIN FETCH r.theme JOIN FETCH r.time")

@EntityGraph(attributePaths = {"member"})
Optional<Reservation> findWithMemberByDateAndTimeAndTheme(String date, Time time, Theme theme);

@EntityGraph(attributePaths = {"time", "theme"})
List<Reservation> findAll();

@EntityGraph(attributePaths = {"time", "theme"})
List<Reservation> findByMember(Member member);

List<Reservation> findByDateAndThemeId(String date, Long themeId);
}
42 changes: 8 additions & 34 deletions src/main/java/roomescape/reservation/ReservationRequest.java
Original file line number Diff line number Diff line change
@@ -1,43 +1,17 @@
package roomescape.reservation;

public class ReservationRequest {
private String name;
private String date;
private Long theme;
private Long time;

public ReservationRequest(String name, String date, Long theme, Long time) {
public record ReservationRequest(
String name,
String date,
Long theme,
Long time
) {
public ReservationRequest {
validateDate(date);
validateTheme(theme);
validateTime(time);
this.name = name;
this.date = date;
this.theme = theme;
this.time = time;
}

public String getName() {
return name;
}

public String getDate() {
return date;
}

public Long getTheme() {
return theme;
}

public Long getTime() {
return time;
}

public void checkName(String name) {
if (this.name == null) {
this.name = name;
}
}


private void validateDate(String date) {
if (date == null) {
throw new IllegalArgumentException("Invalid date");
Expand Down
48 changes: 15 additions & 33 deletions src/main/java/roomescape/reservation/ReservationResponse.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,19 @@
package roomescape.reservation;

public class ReservationResponse {
private Long id;
private String name;
private String theme;
private String date;
private String time;

public ReservationResponse(Long id, String name, String theme, String date, String time) {
this.id = id;
this.name = name;
this.theme = theme;
this.date = date;
this.time = time;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getTheme() {
return theme;
}

public String getDate() {
return date;
}

public String getTime() {
return time;
public record ReservationResponse(
Long id,
String name,
String theme,
String date,
String time
) {
public static ReservationResponse from(Reservation reservation) {
return new ReservationResponse(
reservation.getId(),
reservation.getName(),
reservation.getTheme().getName(),
reservation.getDate(),
reservation.getTime().getValue()
);
}
}
41 changes: 30 additions & 11 deletions src/main/java/roomescape/reservation/ReservationService.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,56 @@
package roomescape.reservation;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.springframework.stereotype.Service;
import roomescape.member.Member;
import roomescape.member.MemberRepository;
import roomescape.theme.Theme;
import roomescape.theme.ThemeRepository;
import roomescape.time.Time;
import roomescape.time.TimeRepository;
import roomescape.waiting.WaitingRepository;

@Service
public class ReservationService {
private final ReservationRepository reservationRepository;
private final TimeRepository timeRepository;
private final ThemeRepository themeRepository;
private final MemberRepository memberRepository;
private final WaitingRepository waitingRepository;

public ReservationService(ReservationRepository reservationRepository, TimeRepository timeRepository,
ThemeRepository themeRepository) {
ThemeRepository themeRepository, MemberRepository memberRepository,
WaitingRepository waitingRepository) {
this.reservationRepository = reservationRepository;
this.timeRepository = timeRepository;
this.themeRepository = themeRepository;
this.memberRepository = memberRepository;
this.waitingRepository = waitingRepository;
}

public ReservationResponse save(ReservationRequest reservationRequest) {
Time time = timeRepository.findByIdOrThrow(reservationRequest.getTime());
Theme theme = themeRepository.findByIdOrThrow(reservationRequest.getTime());
Reservation reservation = new Reservation(reservationRequest.getName(), reservationRequest.getDate(), time,
theme);
public List<MyReservationResponse> readAllByMember(String email) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 코드도 아래와 비슷하게 정답은 아니지만

    public List<MyReservationResponse> readAllByMember(final String email) {
        final Member member = memberRepository.findByEmailOrThrow(email);
        final var reservations = getMemberReservation(member);
        final var waitingWithRanks = getMemberWaiting(member);


        return List.copyOf(Stream.concat(
                        reservations.stream().map(MyReservationResponse::from),
                        waitingWithRanks.stream().map(MyReservationResponse::from))
                .toList());
    }

이와같으면 좀 더 깔끔 해질거 같네요.
getXXX 와 같은 메소드는 재사용이 용이 + DTO 변환 로직과 분리, 중복 코드 제거

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지난번 리뷰에서 알려주신 레이어 분리 방식이 너무 마음에 들었습니다. 공유해주신 지속 성장 가능한 소프트웨어를 만들어가는 방법 글을 읽으며, 레이어를 분리하는 것만으로 해결하려다 보니 메서드 분리를 잊고 있었네요. 수정해보겠습니다. 👍

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 해당 아티클을 읽으며 영향을 받았는데
다양하게 코드를 작성하며 관점을 가져나가시면 될 거 같아요.

  • 명확하게 분리해야 할 필요 & 확장될 가능성이 있는가?
  • 스프링 기술적으로 ( private 메소드는 트랜잭션 불가 ) 또는 기술적으로 ( 클래스 재사용 등 ) 분리되야 하는가?

무조건 레이어 기반 클래스들을 계속 만들어 분리하다보니 1:1 과 같은 형태가 될 수도 있더라구요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

단순히 메서드 호출을 위임하는 형태의 분리보다는, 현재 요구사항을 명확하게 만족하는 코드가 더 중요하다는 관점을 갖게 되었습니다. 하지만 일관성에 대한 고민은 여전히 남아 있습니다.

특정 도메인에서는 분리와 확장의 필요성이 명확해 4단계 레이어 구조를 적용했지만, 다른 도메인에서는 굳이 필요하지 않아 3단계로 유지했을 때, 설계의 균형이 맞지 않는 느낌이 들었습니다. 해당 아티클에도 레이어들의 규칙이 명시되어 있는데, 이는 어떻게 균형을 맞추는게 좋을까요?

Member member = memberRepository.findByEmailOrThrow(email);
List<MyReservationResponse> reservations = reservationRepository.findByMember(member).stream()
.map(MyReservationResponse::from).toList();
List<MyReservationResponse> waitings = waitingRepository.findAllWithRankByMemberId(member.getId()).stream()
.map(MyReservationResponse::from).toList();
return List.copyOf(Stream.concat(reservations.stream(), waitings.stream()).toList());
}

public ReservationResponse save(ReservationRequest reservationRequest, String email) {
Member member = memberRepository.findByEmailOrThrow(email);
Time time = timeRepository.findByIdOrThrow(reservationRequest.time());
Theme theme = themeRepository.findByIdOrThrow(reservationRequest.theme());

Reservation reservation = new Reservation(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분에서 Option.ofNullable 부분을 private method 로 빼도 좋을거 같네요.
비즈니스 로직의 일환이라 어쩔수 없지만, 새로운 팀원이 들어오고 이 로직을 보면 혼동을 느낄거 같네요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조건식을 메서드로 추출하면 네이밍을 통해 조건의 의미를 쉽게 파악할 수 있는 것처럼, 해당 동작도 private method 로 분리하는 것이 동작의 의미를 명확하게 전달할 수 있을 것 같습니다. 수정해보겠습니다 👍

Optional.ofNullable(reservationRequest.name()).orElse(member.getName()), reservationRequest.date(),
time, theme, member);

Reservation savedReservation = reservationRepository.save(reservation);

return new ReservationResponse(savedReservation.getId(), savedReservation.getName(),
savedReservation.getTheme().getName(), savedReservation.getDate(),
savedReservation.getTime().getValue());
return ReservationResponse.from(savedReservation);
}

public void deleteById(Long id) {
Expand All @@ -39,8 +59,7 @@ public void deleteById(Long id) {

public List<ReservationResponse> findAll() {
return reservationRepository.findAll().stream()
.map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(),
it.getTime().getValue()))
.map(ReservationResponse::from)
.toList();
}
}
57 changes: 57 additions & 0 deletions src/main/java/roomescape/waiting/Waiting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package roomescape.waiting;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import roomescape.member.Member;
import roomescape.theme.Theme;
import roomescape.time.Time;

@Entity
public class Waiting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String date;
@ManyToOne(fetch = FetchType.LAZY)
private Time time;
@ManyToOne(fetch = FetchType.LAZY)
private Theme theme;
@ManyToOne(fetch = FetchType.LAZY)
private Member member;

public Waiting() {
}

public Waiting(String name, String date, Time time, Theme theme, Member member) {
this.name = name;
this.date = date;
this.time = time;
this.theme = theme;
this.member = member;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getDate() {
return date;
}

public Time getTime() {
return time;
}

public Theme getTheme() {
return theme;
}
}
Loading