diff --git a/README.md b/README.md index 3b72a1049..e79425661 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,9 @@ ## πŸ§κ³ λ―Όμ‚¬ν•­ -😈 "/"둜 μš”μ²­μ„ λ³΄λƒˆλŠ”λ° 아무것도 μΈμ‹ν•˜μ§€ λͺ»ν•˜λŠ” 문제 -> πŸ’‘thymeleafμ˜μ‘΄μ„± μΆ”κ°€ ν›„ ν•΄κ²° -> - +😈 "/"둜 μš”μ²­μ„ λ³΄λƒˆλŠ”λ° 아무것도 μΈμ‹ν•˜μ§€ λͺ»ν•˜λŠ” 문제 +> πŸ’‘thymeleafμ˜μ‘΄μ„± μΆ”κ°€ ν›„ ν•΄κ²° +> > ν…œν”Œλ¦Ώ μ—”μ§„ 없이 λ Œλ”λ§ ν•˜λŠ” 방법 : https://bottom-to-top.tistory.com/38 # πŸš€2단계 - μ˜ˆμ•½ 쑰회 @@ -24,13 +23,14 @@ - [x] /reservation λ Œλ”λ§ - [x] μ˜ˆμ•½ λͺ©λ‘ 쑰회 API κ΅¬ν˜„ + --- ## πŸ§κ³ λ―Όμ‚¬ν•­ 😈 builder νŒ¨ν„΄μ„ μΈμ‹ν•˜μ§€ λͺ»ν•˜λŠ” μƒνƒœ > πŸ’‘lombok μ˜μ‘΄μ„± μˆ˜μ • - +> > μ°Έκ³  : https://ururuwave.tistory.com/66 # πŸš€3단계 - μ˜ˆμ•½ μΆ”κ°€/μ·¨μ†Œ @@ -42,6 +42,8 @@ - [x] μ˜ˆμ•½ μΆ”κ°€ API κ΅¬ν˜„ - [x] μ˜ˆμ•½ μ‚­μ œ API κ΅¬ν˜„ +--- + # πŸš€4단계 - μ˜ˆμ™Έ 처리 --- @@ -52,6 +54,7 @@ - [x] μ˜ˆμ•½ μ‚­μ œ μ‹œ μ‹λ³„μžλ‘œ μ €μž₯된 μ˜ˆμ•½μ„ 찾을 수 μ—†λŠ” 경우 μ˜ˆμ™Έ 처리 ## πŸ§κ³ λ―Όμ‚¬ν•­ + πŸ˜ˆμ‚­μ œν•  μ˜ˆμ•½μ΄ μ—†λŠ” κ²½μš°λŠ” NOT_FOUNDλ₯Ό λ°˜ν™˜ν•˜λŠ” 게 μ•Œλ§žλ‹€κ³  μƒκ°ν–ˆλ‹€. ν•˜μ§€λ§Œ μš”μ²­μ„ 보낼 λ•Œ μ—†λŠ” 번호둜 보낸닀면 이 μƒν™©μ—μ„œλŠ” BAD_REQUESTκ°€ λ§žλ‹€κ³ λ„ μƒκ°ν–ˆλ‹€. > 이에 λŒ€ν•œ 고민이 더 ν•„μš”ν•  것 κ°™λ‹€. @@ -101,3 +104,32 @@ 😈 update vs batchUpdate 😈 keyHolder? PreparedStatement? NotSerializableException? + +# πŸš€8단계 - μ‹œκ°„ 관리 κΈ°λŠ₯ + +--- + +## πŸ§κ³ λ―Όμ‚¬ν•­ + +😈 Service에 데이터λ₯Ό μ €μž₯ν•˜κ³  λΆˆλŸ¬μ˜€λŠ” DB관련이 κΈ°λŠ₯이 μžˆλŠ” 것이 어색함 +> DAO객체λ₯Ό λ§Œλ“€μ–΄ μ΄κ³³μ—μ„œ μ²˜λ¦¬ν•˜λ„λ‘ μˆ˜μ • +> Reservation도 이처럼 μˆ˜μ •ν•  μ˜ˆμ • + + +# πŸš€9단계 - κΈ°μ‘΄ μ½”λ“œ μˆ˜μ • + +--- + +## πŸ”§κ΅¬ν˜„μ‚¬ν•­ + +- [ ] μŠ€ν‚€λ§ˆ μˆ˜μ • +- [ ] μ˜ˆμ•½ νŽ˜μ΄μ§€ 파일 μˆ˜μ • +- [ ] μ˜ˆμ•½ 클래슀 μˆ˜μ • +- [ ] μ˜ˆμ•½ 쿼리 μˆ˜μ • + +--- + +## πŸ§κ³ λ―Όμ‚¬ν•­ + +😈 κΈ°μ‘΄ ν˜•μ‹μœΌλ‘œ λ“€μ–΄μ˜¬ λ•Œ 였λ₯˜λ°œμƒ +> 컨트둀러 λ‹¨μ—μ„œ 처리 diff --git a/src/main/java/roomescape/api/exception/ReservationException.java b/src/main/java/roomescape/api/exception/ReservationException.java index 4ca848d95..73f2db659 100644 --- a/src/main/java/roomescape/api/exception/ReservationException.java +++ b/src/main/java/roomescape/api/exception/ReservationException.java @@ -10,7 +10,7 @@ public class ReservationException extends RuntimeException { private final String message; private final HttpStatus httpStatus; - protected ReservationException(final ErrorMessage errorMessage) { + public ReservationException(final ErrorMessage errorMessage) { super(errorMessage.getMessage()); this.message = errorMessage.getMessage(); this.httpStatus = errorMessage.getHttpStatus(); diff --git a/src/main/java/roomescape/api/reservations/controller/ReservationsController.java b/src/main/java/roomescape/api/reservations/controller/ReservationController.java similarity index 57% rename from src/main/java/roomescape/api/reservations/controller/ReservationsController.java rename to src/main/java/roomescape/api/reservations/controller/ReservationController.java index de7178fc7..7aba9bcc9 100644 --- a/src/main/java/roomescape/api/reservations/controller/ReservationsController.java +++ b/src/main/java/roomescape/api/reservations/controller/ReservationController.java @@ -6,45 +6,46 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import roomescape.api.reservations.dto.request.ReservationRegisterRequest; +import roomescape.api.reservations.dto.response.ReservationResponse; import roomescape.api.reservations.service.ReservationsService; -import roomescape.db.entity.ReservationsEntity; -import java.net.URI; import java.util.List; @RestController @RequestMapping("/reservations") @RequiredArgsConstructor -public class ReservationsController { +public class ReservationController { private final ReservationsService reservationsService; @GetMapping - public ResponseEntity> getReservations() { - return ResponseEntity.status(HttpStatus.OK).body(reservationsService.getReservations()); + public ResponseEntity> getReservations() { + return ResponseEntity.status(HttpStatus.OK) + .body(reservationsService.getReservations()); } @PostMapping - public ResponseEntity createReservation( + public ResponseEntity createReservation( @RequestBody @Valid final ReservationRegisterRequest reservationRegisterRequest ) { - final long reservationsId = reservationsService.createReservations( + final ReservationResponse reservationResponse = reservationsService.createReservations( reservationRegisterRequest.name(), reservationRegisterRequest.date(), - reservationRegisterRequest.time() + reservationRegisterRequest.timeId() ); - return ResponseEntity.created(URI.create("/reservations/" + reservationsId)).build(); + return ResponseEntity.status(HttpStatus.CREATED) + .header("Location", "/reservations/" + reservationResponse.id()) + .body(reservationResponse); } @DeleteMapping("/{reservationId}") public ResponseEntity deleteReservation( @PathVariable(name = "reservationId") final long reservationId ) { - reservationsService.deleteReservations(reservationId); - - return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null); + return ResponseEntity.status(HttpStatus.NO_CONTENT) + .body(null); } } diff --git a/src/main/java/roomescape/api/reservations/dto/request/ReservationRegisterRequest.java b/src/main/java/roomescape/api/reservations/dto/request/ReservationRegisterRequest.java index e02c0b450..3042eff12 100644 --- a/src/main/java/roomescape/api/reservations/dto/request/ReservationRegisterRequest.java +++ b/src/main/java/roomescape/api/reservations/dto/request/ReservationRegisterRequest.java @@ -1,13 +1,14 @@ package roomescape.api.reservations.dto.request; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; public record ReservationRegisterRequest( @NotBlank(message = "λ‚ μ§œκ°€ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") String date, @NotBlank(message = "이름이 λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") String name, - @NotBlank(message = "μ‹œκ°„μ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") - String time + @NotNull(message = "μ‹œκ°„μ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") + Long timeId ) { } diff --git a/src/main/java/roomescape/api/reservations/dto/response/ReservationRegisterResponse.java b/src/main/java/roomescape/api/reservations/dto/response/ReservationRegisterResponse.java deleted file mode 100644 index 35e8460df..000000000 --- a/src/main/java/roomescape/api/reservations/dto/response/ReservationRegisterResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package roomescape.api.reservations.dto.response; - -public record ReservationRegisterResponse( - long id, - String name, - String date, - String time -) { - - public static ReservationRegisterResponse of(final long id, final String name, final String date, final String time) { - return new ReservationRegisterResponse(id, name, date, time); - } -} diff --git a/src/main/java/roomescape/api/reservations/dto/response/ReservationResponse.java b/src/main/java/roomescape/api/reservations/dto/response/ReservationResponse.java new file mode 100644 index 000000000..bb2e10f3e --- /dev/null +++ b/src/main/java/roomescape/api/reservations/dto/response/ReservationResponse.java @@ -0,0 +1,13 @@ +package roomescape.api.reservations.dto.response; + +public record ReservationResponse( + long id, + String name, + String date, + String time +) { + + public static ReservationResponse of(final long id, final String name, final String date, final String time) { + return new ReservationResponse(id, name, date, time); + } +} diff --git a/src/main/java/roomescape/api/reservations/service/ReservationsService.java b/src/main/java/roomescape/api/reservations/service/ReservationsService.java index d988697ae..dfe4d5b81 100644 --- a/src/main/java/roomescape/api/reservations/service/ReservationsService.java +++ b/src/main/java/roomescape/api/reservations/service/ReservationsService.java @@ -1,56 +1,44 @@ package roomescape.api.reservations.service; import lombok.RequiredArgsConstructor; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import roomescape.db.entity.ReservationsEntity; +import roomescape.api.reservations.dto.response.ReservationResponse; +import roomescape.db.reservation.dao.ReservationDao; +import roomescape.db.reservation.entity.ReservationEntity; -import java.sql.PreparedStatement; -import java.sql.Statement; import java.util.List; @Service @RequiredArgsConstructor public class ReservationsService { - private final JdbcTemplate jdbcTemplate; + private final ReservationDao reservationDao; @Transactional(readOnly = true) - public List getReservations() { - final String sql = "SELECT id, name, date, time FROM reservation"; - - return jdbcTemplate.query( - sql, (resultSet, rowNum) -> ReservationsEntity.builder() - .id(resultSet.getLong("id")) - .name(resultSet.getString("name")) - .date(resultSet.getString("date")) - .time(resultSet.getString("time")) - .build()); + public List getReservations() { + return reservationDao.getReservations().stream() + .map(reservationsEntity -> ReservationResponse.of( + reservationsEntity.getId(), + reservationsEntity.getName(), + reservationsEntity.getDate(), + reservationsEntity.getTime().getTime())) + .toList(); } @Transactional - public long createReservations(final String name, final String date, final String time) { - final String sql = "INSERT INTO reservation(name, date, time) VALUES (?, ?, ?)"; - final KeyHolder keyHolder = new GeneratedKeyHolder(); - - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - ps.setString(1, name); - ps.setString(2, date); - ps.setString(3, time); - return ps; - }, keyHolder); - - return keyHolder.getKey().longValue(); + public ReservationResponse createReservations(final String name, final String date, final Long timeId) { + final ReservationEntity reservationEntity = reservationDao.createReservations(name, date, timeId); + return ReservationResponse.of( + reservationEntity.getId(), + reservationEntity.getName(), + reservationEntity.getDate(), + reservationEntity.getTime().getTime() + ); } @Transactional public void deleteReservations(final long reservationId) { - final String sql = "DELETE FROM reservation WHERE id = ?"; - - jdbcTemplate.update(sql, reservationId); + reservationDao.deleteReservations(reservationId); } } diff --git a/src/main/java/roomescape/api/times/controller/TimeController.java b/src/main/java/roomescape/api/times/controller/TimeController.java new file mode 100644 index 000000000..bbdaf18ab --- /dev/null +++ b/src/main/java/roomescape/api/times/controller/TimeController.java @@ -0,0 +1,46 @@ +package roomescape.api.times.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import roomescape.api.times.dto.request.TimeRegisterRequest; +import roomescape.api.times.dto.response.TimeResponse; +import roomescape.api.times.service.TimeService; + +import java.util.List; + +@RestController +@RequestMapping("/times") +@RequiredArgsConstructor +public class TimeController { + + private final TimeService timeService; + + @GetMapping + public ResponseEntity> getTimes() { + return ResponseEntity.status(HttpStatus.OK) + .body(timeService.getTimes()); + } + + @PostMapping + public ResponseEntity createTime( + @RequestBody final TimeRegisterRequest timeRegisterRequest + ) { + final TimeResponse timeResponse = timeService.createTime( + timeRegisterRequest.time() + ); + return ResponseEntity.status(HttpStatus.CREATED) + .header("Location", "/times/" + timeResponse.id()) + .body(timeResponse); + } + + @DeleteMapping("/{timeId}") + public ResponseEntity deleteTime( + @PathVariable(name = "timeId") final long timeId + ) { + timeService.deleteTime(timeId); + return ResponseEntity.status(HttpStatus.NO_CONTENT) + .body(null); + } +} diff --git a/src/main/java/roomescape/api/times/dto/request/TimeRegisterRequest.java b/src/main/java/roomescape/api/times/dto/request/TimeRegisterRequest.java new file mode 100644 index 000000000..880e286cb --- /dev/null +++ b/src/main/java/roomescape/api/times/dto/request/TimeRegisterRequest.java @@ -0,0 +1,6 @@ +package roomescape.api.times.dto.request; + +public record TimeRegisterRequest( + String time +) { +} diff --git a/src/main/java/roomescape/api/times/dto/response/TimeResponse.java b/src/main/java/roomescape/api/times/dto/response/TimeResponse.java new file mode 100644 index 000000000..172a838e3 --- /dev/null +++ b/src/main/java/roomescape/api/times/dto/response/TimeResponse.java @@ -0,0 +1,11 @@ +package roomescape.api.times.dto.response; + +public record TimeResponse( + long id, + String time +) { + + public static TimeResponse of(final long id, final String time) { + return new TimeResponse(id, time); + } +} diff --git a/src/main/java/roomescape/api/times/service/TimeService.java b/src/main/java/roomescape/api/times/service/TimeService.java new file mode 100644 index 000000000..b7ed59ab6 --- /dev/null +++ b/src/main/java/roomescape/api/times/service/TimeService.java @@ -0,0 +1,35 @@ +package roomescape.api.times.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import roomescape.api.times.dto.response.TimeResponse; +import roomescape.db.time.dao.TimeDao; +import roomescape.db.time.entity.TimeEntity; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class TimeService { + + private final TimeDao timeDao; + + @Transactional(readOnly = true) + public List getTimes() { + return timeDao.getTimes().stream() + .map(timeEntity -> TimeResponse.of(timeEntity.getId(), timeEntity.getTime())) + .toList(); + } + + @Transactional + public TimeResponse createTime(final String time) { + final TimeEntity timeEntity = timeDao.createTime(time); + return TimeResponse.of(timeEntity.getId(), timeEntity.getTime()); + } + + @Transactional + public void deleteTime(final long timeId) { + timeDao.deleteTime(timeId); + } +} diff --git a/src/main/java/roomescape/api/view/controller/RoomescapeViewController.java b/src/main/java/roomescape/api/view/controller/RoomescapeViewController.java index b5b1fdf15..a96dff740 100644 --- a/src/main/java/roomescape/api/view/controller/RoomescapeViewController.java +++ b/src/main/java/roomescape/api/view/controller/RoomescapeViewController.java @@ -13,7 +13,12 @@ public String renderingHomePage() { @GetMapping("/reservation") public String renderingReservationPage() { - return "reservation"; + return "new-reservation"; + } + + @GetMapping("/time") + public String renderingTimePage() { + return "time"; } } diff --git a/src/main/java/roomescape/controller/reservations/ReservationsController.java b/src/main/java/roomescape/controller/reservations/ReservationsController.java deleted file mode 100644 index 39f5ed2de..000000000 --- a/src/main/java/roomescape/controller/reservations/ReservationsController.java +++ /dev/null @@ -1,70 +0,0 @@ -package roomescape.controller.reservations; - -import jakarta.validation.Valid; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import roomescape.dto.request.ReservationRegisterRequest; -import roomescape.dto.response.ReservationRegisterResponse; -import roomescape.entity.ReservationEntity; -import roomescape.enums.ErrorMessage; -import roomescape.exception.NotFoundException; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; - -@RestController -@RequestMapping("/reservations") -public class ReservationsController { - - private List reservationEntities = new ArrayList<>(); - private AtomicLong index = new AtomicLong(); - - @GetMapping - public ResponseEntity> getReservations() { - return ResponseEntity.status(HttpStatus.OK).body(reservationEntities); - } - - @PostMapping - public ResponseEntity createReservation( - @RequestBody @Valid final ReservationRegisterRequest reservationRegisterRequest - ) { - final ReservationEntity reservationEntity = ReservationEntity.builder() - .id(index.incrementAndGet()) - .name(reservationRegisterRequest.name()) - .date(reservationRegisterRequest.date()) - .time(reservationRegisterRequest.time()) - .build(); - - reservationEntities.add(reservationEntity); - - return ResponseEntity.status(HttpStatus.CREATED) - .header("Location", URI.create("/reservations/" + reservationEntity.getId()).toString()) - .body(ReservationRegisterResponse.of( - reservationEntity.getId(), - reservationEntity.getName(), - reservationEntity.getDate(), - reservationEntity.getTime() - )); - } - - @DeleteMapping("/{reservationId}") - public ResponseEntity deleteReservation( - @PathVariable(name = "reservationId") final long reservationId - ) { - final ReservationEntity findReservationEntity = reservationEntities.stream() - .filter(reservationEntity -> reservationEntity.getId() == reservationId) - .findFirst() - .orElse(null); - - if (findReservationEntity == null) { - throw new NotFoundException(ErrorMessage.NOT_FOUND_RESERVATION); - } - - reservationEntities.remove(findReservationEntity); - - return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null); - } -} diff --git a/src/main/java/roomescape/controller/view/RoomescapeController.java b/src/main/java/roomescape/controller/view/RoomescapeController.java deleted file mode 100644 index 861a48d03..000000000 --- a/src/main/java/roomescape/controller/view/RoomescapeController.java +++ /dev/null @@ -1,19 +0,0 @@ -package roomescape.controller.view; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -public class RoomescapeController { - - @GetMapping("/") - public String renderingHomePage() { - return "home"; - } - - @GetMapping("/reservation") - public String renderingReservationPage() { - return "reservation"; - } - -} diff --git a/src/main/java/roomescape/db/reservation/dao/ReservationDao.java b/src/main/java/roomescape/db/reservation/dao/ReservationDao.java new file mode 100644 index 000000000..edb2ff112 --- /dev/null +++ b/src/main/java/roomescape/db/reservation/dao/ReservationDao.java @@ -0,0 +1,74 @@ +package roomescape.db.reservation.dao; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Component; +import roomescape.api.exception.NotFoundException; +import roomescape.db.reservation.entity.ReservationEntity; +import roomescape.db.time.entity.TimeEntity; +import roomescape.enums.ErrorMessage; + +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class ReservationDao { + + private final JdbcTemplate jdbcTemplate; + private static final int DATABASE_ERROR = 0; + + public List getReservations() { + final String sql = "SELECT r.id as reservation_id, r.name, r.date, t.id as time_id, t.time as time_value FROM reservation as r inner join time as t on r.time_id = t.id"; + + return jdbcTemplate.query( + sql, (resultSet, rowNum) -> ReservationEntity.builder() + .id(resultSet.getLong("id")) + .name(resultSet.getString("name")) + .date(resultSet.getString("date")) + .time(new TimeEntity(resultSet.getLong("time_id"), resultSet.getString("time_value"))) + .build()); + } + + public ReservationEntity createReservations(final String name, final String date, final Long timeId) { + final String sql = "INSERT INTO reservation(name, date, time_id) VALUES (?, ?, ?)"; + final KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + ps.setString(1, name); + ps.setString(2, date); + ps.setLong(3, timeId); + return ps; + }, keyHolder); + + final String findTimeSql = "INSERT INTO time(time) VALUES (?)"; + final String findSql = "SELECT id, name, date, time_id FROM reservation WHERE id = (?)"; + return jdbcTemplate.queryForObject( + findSql, + (resultSet, rowNum) -> { + final long findTimeId = resultSet.getLong("time_id"); + final String findTime = jdbcTemplate.queryForObject(findTimeSql, String.class, findTimeId); + + return ReservationEntity.builder() + .id(resultSet.getLong("id")) + .name(resultSet.getString("name")) + .date(resultSet.getString("date")) + .time(new TimeEntity(findTimeId, findTime)) // time_id λŒ€μ‹  μ‹€μ œ μ‹œκ°„ λ°˜ν™˜ + .build(); + }, + keyHolder.getKey().longValue() + ); + } + + public void deleteReservations(final long reservationId) { + final String sql = "DELETE FROM reservation WHERE id = ?"; + + if (jdbcTemplate.update(sql, reservationId) == DATABASE_ERROR) { + throw new NotFoundException(ErrorMessage.NOT_FOUND_RESERVATION); + } + } +} diff --git a/src/main/java/roomescape/db/reservation/entity/ReservationEntity.java b/src/main/java/roomescape/db/reservation/entity/ReservationEntity.java new file mode 100644 index 000000000..45eb174df --- /dev/null +++ b/src/main/java/roomescape/db/reservation/entity/ReservationEntity.java @@ -0,0 +1,21 @@ +package roomescape.db.reservation.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import roomescape.db.time.entity.TimeEntity; + +@Getter +@Builder +@AllArgsConstructor +public class ReservationEntity { + + private final long id; + + private final String name; + + private final String date; + + private final TimeEntity time; + +} diff --git a/src/main/java/roomescape/db/time/dao/TimeDao.java b/src/main/java/roomescape/db/time/dao/TimeDao.java new file mode 100644 index 000000000..512891a8d --- /dev/null +++ b/src/main/java/roomescape/db/time/dao/TimeDao.java @@ -0,0 +1,66 @@ +package roomescape.db.time.dao; + +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Component; +import roomescape.api.exception.NotFoundException; +import roomescape.db.time.entity.TimeEntity; +import roomescape.enums.ErrorMessage; + +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class TimeDao { + + private final JdbcTemplate jdbcTemplate; + private static final int DATABASE_ERROR = 0; + + + public List getTimes() { + + final String sql = "SELECT id, time FROM time"; + + return jdbcTemplate.query( + sql, (resultSet, rowNum) -> TimeEntity.builder() + .id(resultSet.getLong("id")) + .time(resultSet.getString("time")) + .build() + ); + } + + public TimeEntity createTime(final String time) { + final String sql = "INSERT INTO time(time) VALUES (?)"; + final KeyHolder keyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + ps.setString(1, time); + return ps; + }, keyHolder); + + final String findSql = "SELECT id, time FROM time WHERE id = (?)"; + return jdbcTemplate.queryForObject( + findSql, + (resultSet, rowNum) -> { + return TimeEntity.builder() + .id(resultSet.getLong("id")) + .time(resultSet.getString("time")) // time_id λŒ€μ‹  μ‹€μ œ μ‹œκ°„ λ°˜ν™˜ + .build(); + }, + keyHolder.getKey().longValue() + ); + } + + public void deleteTime(final long timeId) { + final String sql = "DELETE FROM time WHERE id = ?"; + + if (jdbcTemplate.update(sql, timeId) == DATABASE_ERROR) { + throw new NotFoundException(ErrorMessage.NOT_FOUND_RESERVATION); + } + } +} diff --git a/src/main/java/roomescape/db/entity/ReservationsEntity.java b/src/main/java/roomescape/db/time/entity/TimeEntity.java similarity index 58% rename from src/main/java/roomescape/db/entity/ReservationsEntity.java rename to src/main/java/roomescape/db/time/entity/TimeEntity.java index 35f26ffb1..6dd16fef7 100644 --- a/src/main/java/roomescape/db/entity/ReservationsEntity.java +++ b/src/main/java/roomescape/db/time/entity/TimeEntity.java @@ -1,4 +1,4 @@ -package roomescape.db.entity; +package roomescape.db.time.entity; import lombok.AllArgsConstructor; import lombok.Builder; @@ -7,14 +7,9 @@ @Getter @Builder @AllArgsConstructor -public class ReservationsEntity { +public class TimeEntity { private final long id; - private final String name; - - private final String date; - private final String time; - } diff --git a/src/main/java/roomescape/dto/request/ReservationRegisterRequest.java b/src/main/java/roomescape/dto/request/ReservationRegisterRequest.java deleted file mode 100644 index e38be675a..000000000 --- a/src/main/java/roomescape/dto/request/ReservationRegisterRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package roomescape.dto.request; - -import jakarta.validation.constraints.NotBlank; - -public record ReservationRegisterRequest( - @NotBlank(message = "λ‚ μ§œκ°€ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") - String date, - @NotBlank(message = "이름이 λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") - String name, - @NotBlank(message = "μ‹œκ°„μ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.") - String time -) { -} diff --git a/src/main/java/roomescape/dto/response/ReservationRegisterResponse.java b/src/main/java/roomescape/dto/response/ReservationRegisterResponse.java deleted file mode 100644 index a1e907dc4..000000000 --- a/src/main/java/roomescape/dto/response/ReservationRegisterResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package roomescape.dto.response; - -public record ReservationRegisterResponse( - long id, - String name, - String date, - String time -) { - - public static ReservationRegisterResponse of(final long id, final String name, final String date, final String time) { - return new ReservationRegisterResponse(id, name, date, time); - } -} diff --git a/src/main/java/roomescape/entity/ReservationEntity.java b/src/main/java/roomescape/entity/ReservationEntity.java deleted file mode 100644 index 3c8f68a33..000000000 --- a/src/main/java/roomescape/entity/ReservationEntity.java +++ /dev/null @@ -1,24 +0,0 @@ -package roomescape.entity; - -import lombok.Builder; -import lombok.Getter; - -@Getter -public class ReservationEntity { - - private final long id; - - private final String name; - - private final String date; - - private final String time; - - @Builder - public ReservationEntity(long id, String name, String date, String time) { - this.id = id; - this.name = name; - this.date = date; - this.time = time; - } -} diff --git a/src/main/java/roomescape/enums/ErrorMessage.java b/src/main/java/roomescape/enums/ErrorMessage.java index 1f3ea3ea4..bb4cd3b33 100644 --- a/src/main/java/roomescape/enums/ErrorMessage.java +++ b/src/main/java/roomescape/enums/ErrorMessage.java @@ -11,7 +11,8 @@ public enum ErrorMessage { // 400 : BAD_REQUEST EMPTY_VALUE_REQUEST("λΉ„μ–΄μžˆλŠ” 값이 μ‘΄μž¬ν•©λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST), - NOT_FOUND_RESERVATION("ν•΄λ‹Ήν•˜λŠ” μ˜ˆμ•½μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST); + NOT_FOUND_RESERVATION("ν•΄λ‹Ήν•˜λŠ” μ˜ˆμ•½μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST), + TRANSACTION_ERROR("데이터 베이슀 λ°˜μ˜μ— 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST); // 404 : NOT_FOUND private final String message; diff --git a/src/main/java/roomescape/exception/BadRequestException.java b/src/main/java/roomescape/exception/BadRequestException.java deleted file mode 100644 index c145bf38e..000000000 --- a/src/main/java/roomescape/exception/BadRequestException.java +++ /dev/null @@ -1,10 +0,0 @@ -package roomescape.exception; - -import roomescape.enums.ErrorMessage; - -public class BadRequestException extends ReservationException { - - public BadRequestException(final ErrorMessage errorMessage) { - super(errorMessage); - } -} diff --git a/src/main/java/roomescape/exception/GlobalExceptionHandler.java b/src/main/java/roomescape/exception/GlobalExceptionHandler.java deleted file mode 100644 index 0008dda79..000000000 --- a/src/main/java/roomescape/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -package roomescape.exception; - -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import java.util.List; -import java.util.stream.Collectors; - -@RestControllerAdvice -public class GlobalExceptionHandler { - - // custom - @ExceptionHandler({NotFoundException.class, BadRequestException.class}) - public ResponseEntity handleCustomException(final ReservationException reservationException) { - return ResponseEntity.status(reservationException.getHttpStatus()).body(reservationException.getMessage()); - } - - // validation - @ExceptionHandler({MethodArgumentNotValidException.class}) - public ResponseEntity> handleValidateException(final MethodArgumentNotValidException methodArgumentNotValidException) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(methodArgumentNotValidException.getBindingResult() - .getFieldErrors().stream() - .map(DefaultMessageSourceResolvable::getDefaultMessage) - .collect(Collectors.toList())); - } -} diff --git a/src/main/java/roomescape/exception/NotFoundException.java b/src/main/java/roomescape/exception/NotFoundException.java deleted file mode 100644 index 5863da1eb..000000000 --- a/src/main/java/roomescape/exception/NotFoundException.java +++ /dev/null @@ -1,10 +0,0 @@ -package roomescape.exception; - -import roomescape.enums.ErrorMessage; - -public class NotFoundException extends ReservationException { - - public NotFoundException(final ErrorMessage errorMessage) { - super(errorMessage); - } -} diff --git a/src/main/java/roomescape/exception/ReservationException.java b/src/main/java/roomescape/exception/ReservationException.java deleted file mode 100644 index 38173f64a..000000000 --- a/src/main/java/roomescape/exception/ReservationException.java +++ /dev/null @@ -1,18 +0,0 @@ -package roomescape.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; -import roomescape.enums.ErrorMessage; - -@Getter -public class ReservationException extends RuntimeException { - - private final String message; - private final HttpStatus httpStatus; - - public ReservationException(final ErrorMessage errorMessage) { - super(errorMessage.getMessage()); - this.message = errorMessage.getMessage(); - this.httpStatus = errorMessage.getHttpStatus(); - } -} diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index b3f9173d1..dbdb51909 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -1,8 +1,16 @@ -CREATE TABLE reservation +CREATE TABLE time ( id BIGINT NOT NULL AUTO_INCREMENT, - name VARCHAR(255) NOT NULL, - date VARCHAR(255) NOT NULL, time VARCHAR(255) NOT NULL, PRIMARY KEY (id) ); + +CREATE TABLE reservation +( + id BIGINT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + date VARCHAR(255) NOT NULL, + time_id BIGINT, + PRIMARY KEY (id), + FOREIGN KEY (time_id) REFERENCES time (id) +); diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 3416ce578..c1c0a3a7d 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -7,8 +7,10 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.DirtiesContext; -import roomescape.db.entity.ReservationsEntity; +import roomescape.api.reservations.controller.ReservationController; +import roomescape.db.reservation.entity.ReservationEntity; +import java.lang.reflect.Field; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; @@ -18,11 +20,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; -import java.util.HashMap; -import java.util.Map; - -import static org.hamcrest.Matchers.is; - @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public class MissionStepTest { @@ -127,11 +124,11 @@ public class MissionStepTest { void μœ‘λ‹¨κ³„() { jdbcTemplate.update("INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)", "브라운", "2023-08-05", "15:40"); - List reservations = RestAssured.given().log().all() + List reservations = RestAssured.given().log().all() .when().get("/reservations") .then().log().all() .statusCode(200).extract() - .jsonPath().getList(".", ReservationsEntity.class); + .jsonPath().getList(".", ReservationEntity.class); Integer count = jdbcTemplate.queryForObject("SELECT count(1) from reservation", Integer.class); @@ -165,5 +162,60 @@ public class MissionStepTest { assertThat(countAfterDelete).isEqualTo(0); } + @Test + void νŒ”λ‹¨κ³„() { + Map params = new HashMap<>(); + params.put("time", "10:00"); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(params) + .when().post("/times") + .then().log().all() + .statusCode(201) + .header("Location", "/times/1"); + + RestAssured.given().log().all() + .when().get("/times") + .then().log().all() + .statusCode(200) + .body("size()", is(1)); + RestAssured.given().log().all() + .when().delete("/times/1") + .then().log().all() + .statusCode(204); + } + + @Test + void ꡬ단계() { + Map reservation = new HashMap<>(); + reservation.put("name", "브라운"); + reservation.put("date", "2023-08-05"); + reservation.put("time", "10:00"); + + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(reservation) + .when().post("/reservations") + .then().log().all() + .statusCode(400); + } + + @Autowired + private ReservationController reservationController; + + @Test + void 십단계() { + boolean isJdbcTemplateInjected = false; + + for (Field field : reservationController.getClass().getDeclaredFields()) { + if (field.getType().equals(JdbcTemplate.class)) { + isJdbcTemplateInjected = true; + break; + } + } + + assertThat(isJdbcTemplateInjected).isFalse(); + } }