diff --git a/build.gradle b/build.gradle index c0ed8e9b3b..a3bbc50520 100644 --- a/build.gradle +++ b/build.gradle @@ -19,10 +19,16 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-validation' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/roomescape/holiday/controller/dto/HolidayResponse.java b/src/main/java/roomescape/holiday/controller/dto/HolidayResponse.java index fd38dcaaf9..733056672f 100644 --- a/src/main/java/roomescape/holiday/controller/dto/HolidayResponse.java +++ b/src/main/java/roomescape/holiday/controller/dto/HolidayResponse.java @@ -8,8 +8,8 @@ public record HolidayResponse(Long id, LocalDate date) { public static HolidayResponse from(Holiday holiday) { return new HolidayResponse( - holiday.id(), - holiday.date() + holiday.getId(), + holiday.getDate() ); } } diff --git a/src/main/java/roomescape/holiday/domain/Holiday.java b/src/main/java/roomescape/holiday/domain/Holiday.java index fbefea99f1..97c6d687ce 100644 --- a/src/main/java/roomescape/holiday/domain/Holiday.java +++ b/src/main/java/roomescape/holiday/domain/Holiday.java @@ -1,13 +1,29 @@ package roomescape.holiday.domain; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + import java.time.LocalDate; -public record Holiday(Long id, LocalDate date) { - public Holiday(LocalDate date) { - this(null, date); - } +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Holiday { - public Holiday withId(Long id) { - return new Holiday(id, this.date); + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column + private LocalDate date; + + public Holiday(LocalDate date) { + this.date = date; } } diff --git a/src/main/java/roomescape/holiday/repository/HolidayRepository.java b/src/main/java/roomescape/holiday/repository/HolidayRepository.java index 1f9b437100..794523aa29 100644 --- a/src/main/java/roomescape/holiday/repository/HolidayRepository.java +++ b/src/main/java/roomescape/holiday/repository/HolidayRepository.java @@ -1,15 +1,11 @@ package roomescape.holiday.repository; -import java.time.LocalDate; +import org.springframework.data.jpa.repository.JpaRepository; import roomescape.holiday.domain.Holiday; -import java.util.List; +import java.time.LocalDate; -public interface HolidayRepository { - Holiday save(Holiday holiday); - List findAll(); +public interface HolidayRepository extends JpaRepository { boolean existsByDate(LocalDate date); - - boolean deleteById(Long id); } diff --git a/src/main/java/roomescape/holiday/repository/JdbcHolidayRepository.java b/src/main/java/roomescape/holiday/repository/JdbcHolidayRepository.java deleted file mode 100644 index 947daa0a57..0000000000 --- a/src/main/java/roomescape/holiday/repository/JdbcHolidayRepository.java +++ /dev/null @@ -1,73 +0,0 @@ -package roomescape.holiday.repository; - -import java.sql.Date; -import java.time.LocalDate; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.simple.SimpleJdbcInsert; -import org.springframework.stereotype.Repository; -import roomescape.holiday.domain.Holiday; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -@Repository -public class JdbcHolidayRepository implements HolidayRepository{ - - private final JdbcTemplate jdbcTemplate; - private final SimpleJdbcInsert themeInsert; - - public JdbcHolidayRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - this.themeInsert = new SimpleJdbcInsert(jdbcTemplate) - .withTableName("holiday") - .usingGeneratedKeyColumns("id"); - } - - @Override - public Holiday save(Holiday holiday) { - Number id = themeInsert.executeAndReturnKey(new MapSqlParameterSource() - .addValue("date", holiday.date())); - return holiday.withId(id.longValue()); - } - - @Override - public List findAll() { - return jdbcTemplate.query( - """ - SELECT h.id, h.date - FROM holiday h - """, - new HolidayRowMapper() - ); - } - - @Override - public boolean existsByDate(LocalDate date) { - Integer exists = jdbcTemplate.queryForObject( - "SELECT EXISTS(SELECT 1 FROM holiday WHERE date = ?)", - Integer.class, - Date.valueOf(date) - ); - return exists != null && exists == 1; - } - - @Override - public boolean deleteById(Long id) { - int affectedRows = jdbcTemplate.update("DELETE FROM holiday WHERE id = ?", id); - return affectedRows > 0; - } - - private static class HolidayRowMapper implements RowMapper { - - @Override - public Holiday mapRow(ResultSet rs, int rowNum) throws SQLException { - Holiday holiday = new Holiday( - rs.getDate("date").toLocalDate() - ); - return holiday.withId(rs.getLong("id")); - } - } -} diff --git a/src/main/java/roomescape/holiday/service/HolidayServiceImpl.java b/src/main/java/roomescape/holiday/service/HolidayServiceImpl.java index b7d72f15e4..66ca664c84 100644 --- a/src/main/java/roomescape/holiday/service/HolidayServiceImpl.java +++ b/src/main/java/roomescape/holiday/service/HolidayServiceImpl.java @@ -34,10 +34,10 @@ public Holiday create(HolidaySaveServiceRequest holiday) { @Override @Transactional public void delete(Long id) { - boolean deleted = holidayRepository.deleteById(id); - if (!deleted) { + if (!holidayRepository.existsById(id)) { throw new HolidayNotFoundException(id); } + holidayRepository.deleteById(id); } @Override diff --git a/src/main/java/roomescape/reservation/controller/AdminReservationController.java b/src/main/java/roomescape/reservation/controller/AdminReservationController.java index 444ed246ef..927d7a44f7 100644 --- a/src/main/java/roomescape/reservation/controller/AdminReservationController.java +++ b/src/main/java/roomescape/reservation/controller/AdminReservationController.java @@ -34,6 +34,11 @@ public ResponseEntity> getAll() { return ResponseEntity.ok(body); } + @GetMapping("/waiting") + public ResponseEntity> getWaitings() { + return ResponseEntity.ok(reservationService.getWaitings()); + } + @PostMapping public ResponseEntity create( @RequestBody @Valid ReservationSaveRequest reservationRequest) { diff --git a/src/main/java/roomescape/reservation/controller/ReservationController.java b/src/main/java/roomescape/reservation/controller/ReservationController.java index 95b44a9fe3..5a7698e920 100644 --- a/src/main/java/roomescape/reservation/controller/ReservationController.java +++ b/src/main/java/roomescape/reservation/controller/ReservationController.java @@ -5,12 +5,13 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import roomescape.reservation.controller.dto.*; +import roomescape.reservation.repository.dto.ReservationWithRank; import roomescape.reservation.service.ReservationService; import java.util.List; @RestController -@RequestMapping("/reservations") +@RequestMapping public class ReservationController { private final ReservationService reservationService; @@ -19,19 +20,19 @@ public ReservationController(ReservationService reservationService) { this.reservationService = reservationService; } - @GetMapping + @GetMapping("/reservations") public ResponseEntity> getAllByName(@RequestParam String name) { List body = reservationService.getAllByName(name); return ResponseEntity.ok(body); } - @DeleteMapping("/{id}") + @DeleteMapping("/reservations/{id}") public ResponseEntity cancel(@PathVariable Long id, @RequestParam String name) { reservationService.cancelForUser(id, name); return ResponseEntity.noContent().build(); } - @PutMapping("/{id}") + @PutMapping("/reservations/{id}") public ResponseEntity update( @PathVariable Long id, @RequestBody @Valid UserReservationUpdateRequest request, @@ -41,11 +42,36 @@ public ResponseEntity update( return ResponseEntity.ok(body); } - @PostMapping + @PostMapping("/reservations") public ResponseEntity create( @RequestBody @Valid ReservationSaveRequest reservationRequest) { ReservationSaveResponse body = ReservationSaveResponse.from( reservationService.create(reservationRequest.toServiceDto())); return ResponseEntity.status(HttpStatus.CREATED).body(body); } + + @GetMapping("/reservation-mine") + public ResponseEntity> findMine(@RequestParam String name) { + List response = reservationService.findMine(name); + return ResponseEntity.ok(response); + } + + @GetMapping("/reservation-mine-rank") + public ResponseEntity> findMineWithRank(@RequestParam String name) { + List response = reservationService.findMineWithRank(name); + return ResponseEntity.ok(response); + } + + @PostMapping("/waitings") + public ResponseEntity createWaiting(@RequestBody @Valid ReservationSaveRequest request) { + ReservationSaveResponse response = ReservationSaveResponse.from( + reservationService.requestWaiting(request.toServiceDto())); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @DeleteMapping("/waitings/{id}") + public ResponseEntity cancelWaiting(@PathVariable Long id, @RequestParam String name) { + reservationService.cancelWaiting(id, name); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/roomescape/reservation/domain/Reservation.java b/src/main/java/roomescape/reservation/domain/Reservation.java index 88b13931bd..73acab6fe4 100644 --- a/src/main/java/roomescape/reservation/domain/Reservation.java +++ b/src/main/java/roomescape/reservation/domain/Reservation.java @@ -1,79 +1,41 @@ package roomescape.reservation.domain; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; import roomescape.reservation.exception.ForbiddenRequestException; import roomescape.theme.domain.Theme; import roomescape.time.domain.ReservationTime; import java.time.LocalDateTime; +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class Reservation { - private final Long id; - private final String name; - private final ReservationTime time; - private final Theme theme; - private final Status status; - private final LocalDateTime createdAt; - public Reservation(String name, ReservationTime time, Theme theme, Status status, LocalDateTime createdAt) { - this(null, name, time, theme, status, createdAt); - } - - private Reservation(Long id, String name, ReservationTime time, Theme theme, Status status, - LocalDateTime createdAt) { - this.id = id; - this.name = name; - this.time = time; - this.theme = theme; - this.status = status; - this.createdAt = createdAt; - } - - public Reservation withId(Long id) { - return new Reservation(id, this.name, this.time, this.theme, this.status, this.createdAt); - } - - public Reservation withTime(ReservationTime time) { - return new Reservation(this.id, this.name, time, this.theme, this.status, this.createdAt); - } - - public Reservation withStatus(Status status) { - return new Reservation(this.id, this.name, this.time, this.theme, status, this.createdAt); - } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public Reservation promote() { - if (this.status != Status.WAITING) { - throw new IllegalStateException("WAITING 상태만 예약으로 가능합니다."); - } - return new Reservation(id, name, time, theme, Status.RESERVED, createdAt); - } + @Column + private String name; - public Reservation withCreatedAt(LocalDateTime createdAt) { - return new Reservation(this.id, this.name, this.time, this.theme, this.status, createdAt); - } + @ManyToOne + @JoinColumn(name = "time_id") + private ReservationTime time; - public Long getId() { - return id; - } + @ManyToOne + @JoinColumn(name = "theme_id") + private Theme theme; - public String getName() { - return name; - } + @Enumerated(EnumType.STRING) + private Status status; - public ReservationTime getTime() { - return time; - } - - public Theme getTheme() { - return theme; - } - - public Status getStatus() { - return status; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } + @Column + private LocalDateTime createdAt; public boolean isReserved() { return this.status.equals(Status.RESERVED); @@ -90,6 +52,14 @@ public void validateOwnedBy(String name) { } } + public Reservation(String name, ReservationTime time, Theme theme, Status status, LocalDateTime createdAt) { + this.name = name; + this.time = time; + this.theme = theme; + this.status = status; + this.createdAt = createdAt; + } + public void validateExpired(LocalDateTime dateTime) { time.validateExpired(dateTime); } @@ -101,4 +71,19 @@ public Long getThemeId() { public Long getTimeId() { return time.getId(); } + + public void promote() { + if (this.status != Status.WAITING) { + throw new IllegalStateException("WAITING 상태만 예약으로 가능합니다."); + } + status = Status.RESERVED; + } + + public void update(ReservationTime newTime) { + time = newTime; + } + + public void updateName(String name) { + this.name = name; + } } diff --git a/src/main/java/roomescape/reservation/repository/JdbcReservationRepository.java b/src/main/java/roomescape/reservation/repository/JdbcReservationRepository.java deleted file mode 100644 index e10c1ecfcf..0000000000 --- a/src/main/java/roomescape/reservation/repository/JdbcReservationRepository.java +++ /dev/null @@ -1,199 +0,0 @@ -package roomescape.reservation.repository; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.simple.SimpleJdbcInsert; -import org.springframework.stereotype.Repository; -import roomescape.reservation.domain.Reservation; -import roomescape.reservation.domain.Status; -import roomescape.theme.domain.Theme; -import roomescape.time.domain.ReservationTime; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -@Repository -public class JdbcReservationRepository implements ReservationRepository { - private final JdbcTemplate jdbcTemplate; - private final SimpleJdbcInsert reservationInsert; - - public JdbcReservationRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - this.reservationInsert = new SimpleJdbcInsert(jdbcTemplate) - .withTableName("reservation") - .usingGeneratedKeyColumns("id"); - } - - private static final String BASE_SELECT = """ - SELECT r.id, - r.name, - r.time_id, - r.theme_id, - r.status, - r.created_at, - rt.start_time, - rt.end_time, - t.name AS theme_name, - t.description AS theme_description, - t.image_url AS theme_image_url - FROM reservation r - LEFT JOIN reservation_time rt ON r.time_id = rt.id - LEFT JOIN theme t ON r.theme_id = t.id - """; - - @Override - public List findAll() { - return jdbcTemplate.query(BASE_SELECT, new ReservationRowMapper()); - } - - @Override - public Optional findById(Long id) { - List results = jdbcTemplate.query( - BASE_SELECT + "WHERE r.id = ?", - new ReservationRowMapper(), - id - ); - return results.stream().findFirst(); - } - - @Override - public Reservation update(Reservation reservation) { - jdbcTemplate.update( - "UPDATE reservation " + - "SET name = ?, time_id = ?, theme_id =?, status = ?, created_at = ? " + - "WHERE id = ?", - reservation.getName(), - reservation.getTime().getId(), - reservation.getTheme().getId(), - reservation.getStatus().name(), - reservation.getCreatedAt(), - reservation.getId() - ); - return reservation; - } - - @Override - public Reservation save(Reservation reservation) { - Number id = reservationInsert.executeAndReturnKey(new MapSqlParameterSource() - .addValue("name", reservation.getName()) - .addValue("time_id", reservation.getTime().getId()) - .addValue("theme_id", reservation.getTheme().getId()) - .addValue("status", reservation.getStatus().name()) - .addValue("created_at", reservation.getCreatedAt())); - return reservation.withId(id.longValue()); - } - - @Override - public boolean isDuplicated(Long themeId, ReservationTime time) { - Integer exists = jdbcTemplate.queryForObject( - "SELECT EXISTS(SELECT 1 FROM reservation WHERE theme_id = ? AND time_id = ?)", - Integer.class, - themeId, - time.getId() - ); - return exists != null && exists == 1; - } - - @Override - public List findTimeIdsByThemeIdAndDate(Long themeId, LocalDate date) { - return jdbcTemplate.query( - """ - SELECT r.time_id FROM reservation r - JOIN reservation_time rt ON r.time_id = rt.id - WHERE r.theme_id = ? AND rt.start_time >= ? AND rt.start_time < ? - """, - (rs, rowNum) -> rs.getLong("time_id"), - themeId, - date.atStartOfDay(), - date.plusDays(1).atStartOfDay() - ); - } - - @Override - public boolean deleteById(Long id) { - int affectedRows = jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); - return affectedRows > 0; - } - - @Override - public boolean existsByTimeId(Long timeId) { - Integer exists = jdbcTemplate.queryForObject( - "SELECT EXISTS(SELECT 1 FROM reservation WHERE time_id = ?)", - Integer.class, - timeId - ); - return exists != null && exists == 1; - } - - private static class ReservationRowMapper implements RowMapper { - @Override - public Reservation mapRow(ResultSet rs, int rowNum) throws SQLException { - ReservationTime time = null; - Long timeId = rs.getLong("time_id"); - if (!rs.wasNull()) { - time = new ReservationTime( - timeId, - rs.getObject("start_time", LocalDateTime.class), - rs.getObject("end_time", LocalDateTime.class) - ); - } - - Theme theme = null; - String themeName = rs.getString("theme_name"); - if (themeName != null) { - theme = new Theme( - themeName, - rs.getString("theme_description"), - rs.getString("theme_image_url") - ).withId(rs.getLong("theme_id")); - } - - Status status = Status.valueOf(rs.getString("status")); - LocalDateTime createdAt = rs.getObject("created_at", LocalDateTime.class); - - return new Reservation( - rs.getString("name"), - time, - theme, - status, - createdAt - ).withId(rs.getLong("id")); - } - } - - @Override - public List findByName(String name) { - return jdbcTemplate.query( - BASE_SELECT + "WHERE r.name = ?", - new ReservationRowMapper(), - name - ); - } - - @Override - public boolean isDuplicatedWithName(String name, Long themeId, ReservationTime time) { - Integer exists = jdbcTemplate.queryForObject( - "SELECT EXISTS(SELECT 1 FROM reservation WHERE name = ? AND theme_id = ? AND time_id = ?)", - Integer.class, - name, - themeId, - time.getId() - ); - return exists != null && exists == 1; - } - - @Override - public List findAllWaitingBy(Long timeId, Long themeId) { - return jdbcTemplate.query( - BASE_SELECT + "WHERE r.time_id = ? AND r.theme_id = ? AND r.status = 'WAITING'", - new ReservationRowMapper(), - timeId, themeId - ); - } - -} diff --git a/src/main/java/roomescape/reservation/repository/ReservationRepository.java b/src/main/java/roomescape/reservation/repository/ReservationRepository.java index 244b3d82ca..97c4a3a559 100644 --- a/src/main/java/roomescape/reservation/repository/ReservationRepository.java +++ b/src/main/java/roomescape/reservation/repository/ReservationRepository.java @@ -1,33 +1,50 @@ package roomescape.reservation.repository; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import roomescape.reservation.domain.Reservation; +import roomescape.reservation.domain.Status; +import roomescape.reservation.repository.dto.ReservationWithRank; import roomescape.time.domain.ReservationTime; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; -import java.util.Optional; -public interface ReservationRepository { - List findAll(); +public interface ReservationRepository extends JpaRepository { - Optional findById(Long id); + boolean existsByNameAndTheme_IdAndTime(String name, Long themeId, ReservationTime time); - List findByName(String name); - - List findAllWaitingBy(Long timeId, Long themeId); - - List findTimeIdsByThemeIdAndDate(Long themeId, LocalDate date); + boolean existsByTheme_IdAndTime(Long themeId, ReservationTime time); - Reservation save(Reservation reservation); + List findAllByTime_IdAndTheme_Id(Long timeId, Long themeId); - Reservation update(Reservation reservation); + List findAllByTime_IdAndTheme_IdAndStatus(Long timeId, Long themeId, Status status); - boolean deleteById(Long id); + @EntityGraph(attributePaths = {"theme", "time"}) + List findAllByStatus(Status status); - boolean existsByTimeId(Long timeId); - - boolean isDuplicated(Long themeId, ReservationTime time); - - boolean isDuplicatedWithName(String name, Long themeId, ReservationTime time); + @EntityGraph(attributePaths = {"theme","time"}) + List findByName(String name); + @Query("SELECT r.time.id FROM Reservation r " + + "WHERE r.theme.id = :themeId " + + "AND r.time.startAt >= :start AND r.time.startAt < :end") + List findAvailableTimeIds(Long themeId, LocalDateTime start, LocalDateTime end); + + @Query(""" + SELECT new roomescape.reservation.repository.dto.ReservationWithRank( + r, + (SELECT COUNT(w) FROM Reservation w + WHERE w.theme = r.theme + AND w.time = r.time + AND w.status = roomescape.reservation.domain.Status.WAITING + AND w.createdAt < r.createdAt)) + FROM Reservation r + JOIN FETCH r.theme + JOIN FETCH r.time + WHERE r.name = :name + ORDER BY r.id + """) + List findMineWithRank(String name); } diff --git a/src/main/java/roomescape/reservation/repository/dto/ReservationWithRank.java b/src/main/java/roomescape/reservation/repository/dto/ReservationWithRank.java new file mode 100644 index 0000000000..9a7bbf8db4 --- /dev/null +++ b/src/main/java/roomescape/reservation/repository/dto/ReservationWithRank.java @@ -0,0 +1,9 @@ +package roomescape.reservation.repository.dto; + +import roomescape.reservation.domain.Reservation; + +public record ReservationWithRank( + Reservation reservation, + long rank +) { +} diff --git a/src/main/java/roomescape/reservation/service/ReservationService.java b/src/main/java/roomescape/reservation/service/ReservationService.java index eb029fa431..027aab354e 100644 --- a/src/main/java/roomescape/reservation/service/ReservationService.java +++ b/src/main/java/roomescape/reservation/service/ReservationService.java @@ -1,7 +1,9 @@ package roomescape.reservation.service; +import roomescape.reservation.controller.dto.ReservationResponse; import roomescape.reservation.controller.dto.ReservationWithWaitingOrderResponse; import roomescape.reservation.domain.Reservation; +import roomescape.reservation.repository.dto.ReservationWithRank; import roomescape.reservation.service.dto.ReservationSaveServiceRequest; import java.util.List; @@ -13,4 +15,13 @@ public interface ReservationService { void cancelForUser(Long id, String name); Reservation update(Long id, Long timeId, String name); List getAllByName(String name); + + List findMine(String name); + List findMineWithRank(String name); + + List getWaitings(); + + Reservation requestWaiting(ReservationSaveServiceRequest request); + + void cancelWaiting(Long id, String name); } diff --git a/src/main/java/roomescape/reservation/service/ReservationServiceImpl.java b/src/main/java/roomescape/reservation/service/ReservationServiceImpl.java index 4771750f2d..ffea22ec7f 100644 --- a/src/main/java/roomescape/reservation/service/ReservationServiceImpl.java +++ b/src/main/java/roomescape/reservation/service/ReservationServiceImpl.java @@ -1,8 +1,10 @@ package roomescape.reservation.service; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import roomescape.holiday.service.HolidayService; +import roomescape.reservation.controller.dto.ReservationResponse; import roomescape.reservation.controller.dto.ReservationWithWaitingOrderResponse; import roomescape.reservation.domain.MyReservation; import roomescape.reservation.domain.Reservation; @@ -11,6 +13,7 @@ import roomescape.reservation.exception.DuplicateReservationException; import roomescape.reservation.exception.ReservationNotFoundException; import roomescape.reservation.repository.ReservationRepository; +import roomescape.reservation.repository.dto.ReservationWithRank; import roomescape.reservation.service.dto.ReservationSaveServiceRequest; import roomescape.theme.domain.Theme; import roomescape.theme.exception.ThemeNotFoundException; @@ -21,6 +24,7 @@ import java.time.LocalDateTime; import java.util.List; +@Slf4j @Service @Transactional(readOnly = true) public class ReservationServiceImpl implements ReservationService { @@ -61,14 +65,14 @@ public Reservation create(ReservationSaveServiceRequest request) { Theme theme = themeRepository.findById(request.themeId()) .orElseThrow(() -> new ThemeNotFoundException(request.themeId())); - if (holidayService.isHoliday(time.getDate())) { + if (holidayService.isHoliday(time.getStartAt().toLocalDate())) { throw new IllegalArgumentException("휴일은 예약이 불가합니다."); } - if (reservationRepository.isDuplicatedWithName(request.name(), request.themeId(), time)) { + if (reservationRepository.existsByNameAndTheme_IdAndTime(request.name(), request.themeId(), time)) { throw new DuplicateReservationException(); } - if (reservationRepository.isDuplicated(request.themeId(), time)) { + if (reservationRepository.existsByTheme_IdAndTime(request.themeId(), time)) { return reservationRepository.save(new Reservation(request.name(), time, theme, Status.WAITING, now)); } return reservationRepository.save(new Reservation(request.name(), time, theme, Status.RESERVED, now)); @@ -87,27 +91,20 @@ public void cancel(Long id) { reservation.validateExpired(LocalDateTime.now()); if (reservation.isReserved()) { ReservationWaitings waitings = new ReservationWaitings( - reservationRepository.findAllWaitingBy( + reservationRepository.findAllByTime_IdAndTheme_IdAndStatus( reservation.getTimeId(), - reservation.getThemeId())); - waitings.earliest() - .map(Reservation::promote) - .ifPresent(reservationRepository::update); + reservation.getThemeId(), + Status.WAITING)); + waitings.earliest().ifPresent(Reservation::promote); } reservationRepository.deleteById(id); } @Override public List getAllByName(String name) { - List myReservations = reservationRepository.findByName(name); - - return myReservations.stream() - .map(reservation -> MyReservation.of( - reservation, - new ReservationWaitings( - reservationRepository.findAllWaitingBy( - reservation.getTimeId(), - reservation.getThemeId())))) + List mine = reservationRepository.findByName(name); + return mine.stream() + .map(reservation -> new MyReservation(reservation, 0)) .map(ReservationWithWaitingOrderResponse::from) .toList(); } @@ -125,12 +122,11 @@ public void cancelForUser(Long id, String name) { reservation.validateChangeableBy(name, LocalDateTime.now()); if (reservation.isReserved()) { ReservationWaitings waitings = new ReservationWaitings( - reservationRepository.findAllWaitingBy( + reservationRepository.findAllByTime_IdAndTheme_IdAndStatus( reservation.getTimeId(), - reservation.getThemeId())); - waitings.earliest() - .map(Reservation::promote) - .ifPresent(reservationRepository::update); + reservation.getThemeId(), + Status.WAITING)); + waitings.earliest().ifPresent(Reservation::promote); } reservationRepository.deleteById(id); } @@ -145,34 +141,76 @@ public Reservation update(Long id, Long timeId, String name) { ReservationTime newTime = timeService.findById(timeId); newTime.validateExpired(now); - if (holidayService.isHoliday(newTime.getDate())) { + if (holidayService.isHoliday(newTime.getStartAt().toLocalDate())) { throw new IllegalArgumentException("휴일은 예약이 불가합니다."); } - if (reservationRepository.isDuplicatedWithName(name, reservation.getThemeId(), newTime)) { + if (reservationRepository.existsByNameAndTheme_IdAndTime(name, reservation.getThemeId(), newTime)) { throw new DuplicateReservationException(); } if (reservation.isReserved()) { ReservationWaitings waitings = new ReservationWaitings( - reservationRepository.findAllWaitingBy( + reservationRepository.findAllByTime_IdAndTheme_IdAndStatus( reservation.getTimeId(), - reservation.getThemeId())); - waitings.earliest() - .map(Reservation::promote) - .ifPresent(reservationRepository::update); + reservation.getThemeId(), + Status.WAITING)); + waitings.earliest().ifPresent(Reservation::promote); } - if (reservationRepository.isDuplicated(reservation.getThemeId(), newTime)) { - return reservationRepository.update(reservation - .withTime(newTime) - .withStatus(Status.WAITING) - .withCreatedAt(now)); + reservation.update(newTime); + return reservation; + } + + @Override + public List findMineWithRank(String name) { + return reservationRepository.findMineWithRank(name); + } + + @Override + public List findMine(String name) { + reservationRepository.findByName(name); + + return List.of(); + } + + @Override + public List getWaitings() { + return reservationRepository.findAllByStatus(Status.WAITING).stream() + .map(ReservationResponse::from) + .toList(); + } + + @Override + @Transactional + public Reservation requestWaiting(ReservationSaveServiceRequest request) { + LocalDateTime now = LocalDateTime.now(); + + ReservationTime time = timeService.findById(request.timeId()); + time.validateExpired(now); + + Theme theme = themeRepository.findById(request.themeId()) + .orElseThrow(() -> new ThemeNotFoundException(request.themeId())); + + if (holidayService.isHoliday(time.getStartAt().toLocalDate())) { + throw new IllegalArgumentException("휴일은 예약이 불가합니다."); } + if (reservationRepository.existsByNameAndTheme_IdAndTime(request.name(), request.themeId(), time)) { + throw new DuplicateReservationException(); + } + return reservationRepository.save( + new Reservation(request.name(), time, theme, Status.WAITING, now)); + } - return reservationRepository.update(reservation - .withTime(newTime) - .withStatus(Status.RESERVED) - .withCreatedAt(now)); + @Override + @Transactional + public void cancelWaiting(Long id, String name) { + Reservation waiting = reservationRepository.findById(id) + .orElseThrow(() -> new ReservationNotFoundException(id)); + waiting.validateOwnedBy(name); + if (waiting.isReserved()) { + throw new IllegalStateException("대기 상태만 취소할 수 있습니다."); + } + reservationRepository.deleteById(id); } } diff --git a/src/main/java/roomescape/theme/domain/Theme.java b/src/main/java/roomescape/theme/domain/Theme.java index 614b6a16fb..a3a1ed7dc8 100644 --- a/src/main/java/roomescape/theme/domain/Theme.java +++ b/src/main/java/roomescape/theme/domain/Theme.java @@ -1,39 +1,33 @@ package roomescape.theme.domain; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class Theme { - private final Long id; - private final String name; - private final String description; - private final String imageUrl; - public Theme(String name, String description, String imageUrl) { - this(null, name, description, imageUrl); - } - - private Theme(Long id, String name, String description, String imageUrl) { - this.id = id; - this.name = name; - this.description = description; - this.imageUrl = imageUrl; - } - - public Theme withId(Long id) { - return new Theme(id, this.name, this.description, this.imageUrl); - } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public Long getId() { - return id; - } + @Column + private String name; - public String getName() { - return name; - } + @Column + private String description; - public String getDescription() { - return description; - } + @Column + private String imageUrl; - public String getImageUrl() { - return imageUrl; + public Theme(String name, String description, String imageUrl) { + this.name = name; + this.description = description; + this.imageUrl = imageUrl; } } diff --git a/src/main/java/roomescape/theme/repository/JdbcThemeRepository.java b/src/main/java/roomescape/theme/repository/JdbcThemeRepository.java deleted file mode 100644 index 7de88541d0..0000000000 --- a/src/main/java/roomescape/theme/repository/JdbcThemeRepository.java +++ /dev/null @@ -1,111 +0,0 @@ -package roomescape.theme.repository; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.simple.SimpleJdbcInsert; -import org.springframework.stereotype.Repository; -import roomescape.theme.domain.Theme; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - -@Repository -public class JdbcThemeRepository implements ThemeRepository { - - private final JdbcTemplate jdbcTemplate; - private final SimpleJdbcInsert themeInsert; - - public JdbcThemeRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - this.themeInsert = new SimpleJdbcInsert(jdbcTemplate) - .withTableName("theme") - .usingGeneratedKeyColumns("id"); - } - - @Override - public List findAll() { - return jdbcTemplate.query( - """ - SELECT t.id, t.name, t.description, t.image_url - FROM theme t - """, - new JdbcThemeRepository.ThemeRowMapper() - ); - } - - @Override - public Optional findById(Long id) { - List themes = jdbcTemplate.query( - """ - SELECT t.id, t.name, t.description, t.image_url - FROM theme t - WHERE t.id = ? - """, - new JdbcThemeRepository.ThemeRowMapper(), - id - ); - return themes.stream().findFirst(); - } - - @Override - public Theme save(Theme theme) { - Number id = themeInsert.executeAndReturnKey(new MapSqlParameterSource() - .addValue("name", theme.getName()) - .addValue("description", theme.getDescription()) - .addValue("image_url", theme.getImageUrl())); - return theme.withId(id.longValue()); - } - - @Override - public boolean existsById(Long id) { - Integer exists = jdbcTemplate.queryForObject( - "SELECT EXISTS(SELECT 1 FROM theme WHERE id = ?)", - Integer.class, - id - ); - return exists != null && exists == 1; - } - - @Override - public boolean deleteById(Long id) { - int affectedRows = jdbcTemplate.update("DELETE FROM theme WHERE id = ?", id); - return affectedRows > 0; - } - - @Override - public List findBestThemesByDate(LocalDate startDate, LocalDate endDate, int limit) { - return jdbcTemplate.query( - """ - SELECT t.id, t.name, t.description, t.image_url - FROM theme t - JOIN reservation r ON r.theme_id = t.id - JOIN reservation_time rt ON r.time_id = rt.id - WHERE rt.start_time >= ? AND rt.start_time < ? - GROUP BY t.id, t.name, t.description, t.image_url - ORDER BY COUNT(r.id) DESC, t.id ASC - LIMIT ? - """, - new ThemeRowMapper(), - startDate.atStartOfDay(), - endDate.plusDays(1).atStartOfDay(), - limit - ); - } - - private static class ThemeRowMapper implements RowMapper { - - @Override - public Theme mapRow(ResultSet rs, int rowNum) throws SQLException { - Theme theme = new Theme( - rs.getString("name"), - rs.getString("description"), - rs.getString("image_url") - ); - return theme.withId(rs.getLong("id")); - } - } -} diff --git a/src/main/java/roomescape/theme/repository/ThemeRepository.java b/src/main/java/roomescape/theme/repository/ThemeRepository.java index 820d58db8c..e4a8901a5f 100644 --- a/src/main/java/roomescape/theme/repository/ThemeRepository.java +++ b/src/main/java/roomescape/theme/repository/ThemeRepository.java @@ -1,21 +1,22 @@ package roomescape.theme.repository; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import roomescape.theme.domain.Theme; import java.time.LocalDate; import java.util.List; -import java.util.Optional; -public interface ThemeRepository { - List findAll(); - - Optional findById(Long id); - - Theme save(Theme theme); - - boolean existsById(Long id); - - boolean deleteById(Long id); - - List findBestThemesByDate(LocalDate startDate, LocalDate endDate, int limit); +public interface ThemeRepository extends JpaRepository { + + @Query(value = """ + SELECT t.* FROM theme t + JOIN reservation r ON r.theme_id = t.id + JOIN reservation_time rt ON r.time_id = rt.id + WHERE rt.start_time >= :start AND rt.start_time < :end + GROUP BY t.id, t.name, t.description, t.image_url + ORDER BY COUNT(r.id) DESC, t.id ASC + LIMIT :limit + """, nativeQuery = true) + List findBestThemesByDate(LocalDate start, LocalDate end, int limit); } diff --git a/src/main/java/roomescape/theme/service/ThemeServiceImpl.java b/src/main/java/roomescape/theme/service/ThemeServiceImpl.java index 20d1bdffb4..1e1cee40f0 100644 --- a/src/main/java/roomescape/theme/service/ThemeServiceImpl.java +++ b/src/main/java/roomescape/theme/service/ThemeServiceImpl.java @@ -64,9 +64,10 @@ public Theme create(ThemeSaveServiceRequest theme) { @Override @Transactional public void deleteById(Long id) { - if(!themeRepository.deleteById(id)) { + if(!themeRepository.existsById(id)) { throw new ThemeNotFoundException(id); } + themeRepository.deleteById(id); } @Override @@ -79,7 +80,7 @@ public List getAvailableTimes(Long themeId, LocalDate date) { return List.of(); } - Set reservedTimeIds = new HashSet<>(reservationRepository.findTimeIdsByThemeIdAndDate(themeId, date)); + Set reservedTimeIds = new HashSet<>(reservationRepository.findAvailableTimeIds(themeId, date.atStartOfDay(), date.plusDays(1).atStartOfDay())); return timeService.findByDate(date) .stream() .filter(time -> !reservedTimeIds.contains(time.getId())) diff --git a/src/main/java/roomescape/time/controller/TimeController.java b/src/main/java/roomescape/time/controller/TimeController.java index 73f0da248b..dd4d84d2b7 100644 --- a/src/main/java/roomescape/time/controller/TimeController.java +++ b/src/main/java/roomescape/time/controller/TimeController.java @@ -1,25 +1,18 @@ package roomescape.time.controller; -import java.time.LocalDate; -import java.util.List; -import java.util.stream.Collectors; - +import jakarta.validation.Valid; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import jakarta.validation.Valid; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - +import org.springframework.web.bind.annotation.*; import roomescape.theme.service.ThemeService; import roomescape.time.controller.dto.TimeResponse; import roomescape.time.controller.dto.TimeSaveRequest; import roomescape.time.service.TimeService; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + @RestController public class TimeController { private final TimeService timeService; @@ -61,4 +54,10 @@ public ResponseEntity deleteById(@PathVariable Long id) { timeService.deleteById(id); return ResponseEntity.noContent().build(); } + + @GetMapping("/test") + public ResponseEntity test() { + timeService.explicitFlush(); + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/roomescape/time/domain/ReservationTime.java b/src/main/java/roomescape/time/domain/ReservationTime.java index 091a523dbc..4bcc91fe7f 100644 --- a/src/main/java/roomescape/time/domain/ReservationTime.java +++ b/src/main/java/roomescape/time/domain/ReservationTime.java @@ -1,54 +1,33 @@ package roomescape.time.domain; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; import roomescape.global.error.ErrorCode; import roomescape.reservation.exception.PastReservationException; -import java.time.LocalDate; import java.time.LocalDateTime; +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class ReservationTime { - private final Long id; - private final LocalDateTime startAt; - private final LocalDateTime endAt; - public ReservationTime(LocalDateTime startAt, LocalDateTime endAt) { - this(null, startAt, endAt); - } - - public ReservationTime(Long id, LocalDateTime startAt, LocalDateTime endAt) { - validateTimesPresent(startAt, endAt); - validateTimeOrder(startAt, endAt); - this.id = id; - this.startAt = startAt; - this.endAt = endAt; - } - - private void validateTimesPresent(LocalDateTime startAt, LocalDateTime endAt) { - if (startAt == null || endAt == null) { - throw new IllegalArgumentException("시작 시간과 종료 시간은 비어있을 수 없습니다."); - } - } - - private void validateTimeOrder(LocalDateTime startAt, LocalDateTime endAt) { - if (!endAt.isAfter(startAt)) { - throw new IllegalArgumentException("종료 시간은 시작 시간 이후여야 합니다."); - } - } - - public Long getId() { - return id; - } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - public LocalDateTime getStartAt() { - return startAt; - } + @Column + private LocalDateTime startAt; - public LocalDateTime getEndAt() { - return endAt; - } + @Column + private LocalDateTime endAt; - public LocalDate getDate() { - return startAt.toLocalDate(); + public ReservationTime(LocalDateTime startAt, LocalDateTime endAt) { + this.startAt = startAt; + this.endAt = endAt; } public void validateExpired(LocalDateTime dateTime) { @@ -56,4 +35,8 @@ public void validateExpired(LocalDateTime dateTime) { throw new PastReservationException(ErrorCode.RESERVATION_EXPIRED); } } + + public void updateStart(LocalDateTime startAt) { + this.startAt = startAt; + } } diff --git a/src/main/java/roomescape/time/repository/JdbcTimeRepository.java b/src/main/java/roomescape/time/repository/JdbcTimeRepository.java deleted file mode 100644 index 8593a08ccf..0000000000 --- a/src/main/java/roomescape/time/repository/JdbcTimeRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -package roomescape.time.repository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.jdbc.core.simple.SimpleJdbcInsert; -import org.springframework.stereotype.Repository; - -import roomescape.time.domain.ReservationTime; - -@Repository -public class JdbcTimeRepository implements TimeRepository { - private final JdbcTemplate jdbcTemplate; - private final SimpleJdbcInsert timeInsert; - - public JdbcTimeRepository(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - this.timeInsert = new SimpleJdbcInsert(jdbcTemplate) - .withTableName("reservation_time") - .usingGeneratedKeyColumns("id"); - } - - @Override - public List findAll() { - return jdbcTemplate.query( - "SELECT id, start_time, end_time FROM reservation_time ORDER BY id", - new ReservationTimeRowMapper() - ); - } - - @Override - public ReservationTime save(LocalDateTime startAt, LocalDateTime endAt) { - Number id = timeInsert.executeAndReturnKey( - new MapSqlParameterSource() - .addValue("start_time", startAt) - .addValue("end_time", endAt) - ); - return new ReservationTime(id.longValue(), startAt, endAt); - } - - @Override - public Optional findById(Long id) { - List results = jdbcTemplate.query( - "SELECT id, start_time, end_time FROM reservation_time WHERE id = ?", - new ReservationTimeRowMapper(), - id - ); - return results.stream().findFirst(); - } - - @Override - public List findByDate(LocalDate date) { - return jdbcTemplate.query( - "SELECT id, start_time, end_time FROM reservation_time WHERE start_time >= ? AND start_time < ? ORDER BY start_time", - new ReservationTimeRowMapper(), - date.atStartOfDay(), - date.plusDays(1).atStartOfDay() - ); - } - - @Override - public boolean deleteById(Long id) { - int affectedRows = jdbcTemplate.update( - "DELETE FROM reservation_time WHERE id = ?", - id - ); - return affectedRows > 0; - } - - private static class ReservationTimeRowMapper implements RowMapper { - @Override - public ReservationTime mapRow(ResultSet rs, int rowNum) throws SQLException { - return new ReservationTime( - rs.getLong("id"), - rs.getObject("start_time", LocalDateTime.class), - rs.getObject("end_time", LocalDateTime.class) - ); - } - } -} diff --git a/src/main/java/roomescape/time/repository/TimeRepository.java b/src/main/java/roomescape/time/repository/TimeRepository.java index 68b468e6ba..e35b6200ac 100644 --- a/src/main/java/roomescape/time/repository/TimeRepository.java +++ b/src/main/java/roomescape/time/repository/TimeRepository.java @@ -1,20 +1,12 @@ package roomescape.time.repository; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - +import org.springframework.data.jpa.repository.JpaRepository; import roomescape.time.domain.ReservationTime; -public interface TimeRepository { - List findAll(); - - ReservationTime save(LocalDateTime startAt, LocalDateTime endAt); - - Optional findById(Long id); +import java.time.LocalDateTime; +import java.util.List; - List findByDate(LocalDate date); +public interface TimeRepository extends JpaRepository { - boolean deleteById(Long id); + List findReservationTimeByStartAt(LocalDateTime startAt); } diff --git a/src/main/java/roomescape/time/service/TimeService.java b/src/main/java/roomescape/time/service/TimeService.java index 0ce37f38e1..7df0005fd8 100644 --- a/src/main/java/roomescape/time/service/TimeService.java +++ b/src/main/java/roomescape/time/service/TimeService.java @@ -1,11 +1,11 @@ package roomescape.time.service; +import roomescape.time.domain.ReservationTime; + import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; -import roomescape.time.domain.ReservationTime; - public interface TimeService { ReservationTime create(LocalDateTime startAt, LocalDateTime endAt); @@ -16,4 +16,9 @@ public interface TimeService { ReservationTime findById(Long id); void deleteById(Long id); + + void writeBehindInsert(); + void writeBehindUpdate(); + + void explicitFlush(); } diff --git a/src/main/java/roomescape/time/service/TimeServiceImpl.java b/src/main/java/roomescape/time/service/TimeServiceImpl.java index eb86ca6200..7f52f0799a 100644 --- a/src/main/java/roomescape/time/service/TimeServiceImpl.java +++ b/src/main/java/roomescape/time/service/TimeServiceImpl.java @@ -1,5 +1,8 @@ package roomescape.time.service; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import roomescape.reservation.repository.ReservationRepository; @@ -12,21 +15,22 @@ import java.time.LocalDateTime; import java.util.List; +@Slf4j @Service @Transactional(readOnly = true) +@RequiredArgsConstructor public class TimeServiceImpl implements TimeService { + private final TimeRepository timeRepository; private final ReservationRepository reservationRepository; + private final EntityManager em; - public TimeServiceImpl(TimeRepository timeRepository, ReservationRepository reservationRepository) { - this.timeRepository = timeRepository; - this.reservationRepository = reservationRepository; - } @Override @Transactional public ReservationTime create(LocalDateTime startAt, LocalDateTime endAt) { - return timeRepository.save(startAt, endAt); + ReservationTime reservationTime = new ReservationTime(startAt, endAt); + return timeRepository.save(reservationTime); } @Override @@ -36,7 +40,7 @@ public List findAll() { @Override public List findByDate(LocalDate date) { - return timeRepository.findByDate(date); + return timeRepository.findReservationTimeByStartAt(date.atStartOfDay()); } @Override @@ -48,12 +52,36 @@ public ReservationTime findById(Long id) { @Override @Transactional public void deleteById(Long id) { - if (reservationRepository.existsByTimeId(id)) { + if (reservationRepository.existsById(id)) { throw new ReservationTimeConflictException(id); } - boolean deleted = timeRepository.deleteById(id); - if (!deleted) { - throw new TimeNotFoundException(id); - } + timeRepository.deleteById(id); + } + + @Override + @Transactional + public void writeBehindInsert() { + log.info("========save 호출 전========"); + ReservationTime time = new ReservationTime(LocalDateTime.now(), LocalDateTime.now().plusHours(2)); + timeRepository.save(time); + log.info("========save 호출 후========"); + } + + @Override + @Transactional + public void writeBehindUpdate() { + ReservationTime t = timeRepository.findById(1L).get(); // SELECT + log.info(">>> 필드 수정 전"); + t.updateStart(LocalDateTime.now()); + log.info(">>> 필드 수정 후, commit 전"); + } + + @Transactional + public void explicitFlush() { + ReservationTime t = timeRepository.findById(1L).get(); // SELECT + log.info(">>> 필드 수정 전"); + t.updateStart(LocalDateTime.now()); + em.flush(); + log.info(">>> 필드 수정 후, commit 전"); } } diff --git a/src/main/java/roomescape/ui/controller/RootRedirectController.java b/src/main/java/roomescape/ui/controller/RootRedirectController.java index aa20be2995..39b4930e69 100644 --- a/src/main/java/roomescape/ui/controller/RootRedirectController.java +++ b/src/main/java/roomescape/ui/controller/RootRedirectController.java @@ -10,4 +10,4 @@ public class RootRedirectController { public String root() { return "redirect:/page/dashboard"; } -} \ No newline at end of file +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d6565db23f..bcc147595a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,3 +4,9 @@ spring.datasource.url=jdbc:h2:mem:DATABASE spring.datasource.username=sa theme.dayCount=7 theme.rankCount=10 +server.port=8081 + +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.defer-datasource-initialization=true diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 75ff549438..0394f5ae51 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,4 +1,4 @@ -INSERT INTO reservation_time (start_time, end_time) +INSERT INTO reservation_time (start_at, end_at) VALUES (DATEADD('DAY', -1, CURRENT_TIMESTAMP), DATEADD('DAY', -1, TIMESTAMPADD(HOUR, 1, CURRENT_TIMESTAMP))), (DATEADD('DAY', -2, CURRENT_TIMESTAMP), DATEADD('DAY', -2, TIMESTAMPADD(HOUR, 1, CURRENT_TIMESTAMP))), diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 3d1293ab2e..91df4b4758 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -7,8 +7,8 @@ DROP TABLE IF EXISTS holiday; CREATE TABLE reservation_time ( id BIGINT NOT NULL AUTO_INCREMENT, - start_time DATETIME NOT NULL, - end_time DATETIME NOT NULL, + start_at DATETIME NOT NULL, + end_at DATETIME NOT NULL, PRIMARY KEY (id) );