-
Notifications
You must be signed in to change notification settings - Fork 132
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
[Spring Core] 이가연 미션 제출합니다. #380
base: gy102912
Are you sure you want to change the base?
Changes from all commits
13b5d17
06f5ee5
cfdca16
28cd4ee
d04bd46
dff0100
932a14b
6905d66
dc601a3
9e49ac8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package roomescape.controller; | ||
|
||
import jakarta.validation.Valid; | ||
import java.net.URI; | ||
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.ResponseBody; | ||
import roomescape.entity.Reservation; | ||
import roomescape.service.ReservationService; | ||
|
||
@Controller | ||
public class ReservationController { | ||
|
||
private final ReservationService reservationService; | ||
|
||
@Autowired | ||
ReservationController(final ReservationService reservationService) { | ||
this.reservationService = reservationService; | ||
} | ||
|
||
|
||
@GetMapping("/") | ||
public String home() { | ||
return "home"; | ||
} | ||
|
||
@GetMapping("/reservation") | ||
public String reservation() { | ||
return "new-reservation"; | ||
} | ||
|
||
@GetMapping("/reservations") | ||
@ResponseBody | ||
public List<Reservation> getAllReservations() { | ||
return reservationService.searchAllReservations(); | ||
} | ||
|
||
@PostMapping("/reservations") | ||
@ResponseBody | ||
public ResponseEntity<Reservation> createReservation(@RequestBody @Valid Reservation reservation) { | ||
Reservation response = reservationService.makeReservation(reservation); | ||
URI location = URI.create("/reservations/" + response.getId()); | ||
|
||
return ResponseEntity.created(location).body(response); | ||
} | ||
|
||
@DeleteMapping("/reservations/{id}") | ||
public ResponseEntity<Void> deleteReservation(@PathVariable("id") Long id) { | ||
reservationService.cancelReservation(id); | ||
|
||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@ExceptionHandler({NoSuchElementException.class, MethodArgumentNotValidException.class}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 핸들링을 위해 ExceptionHandler를 추가하셨군요. 이 메서드를 controller에 추가하신 이유는 어떤건가요? |
||
public ResponseEntity<Void> handleException(Exception e) { | ||
return ResponseEntity.badRequest().build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package roomescape.controller; | ||
|
||
import jakarta.validation.Valid; | ||
import java.net.URI; | ||
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.ResponseBody; | ||
import roomescape.entity.Time; | ||
import roomescape.service.TimeService; | ||
|
||
@Controller | ||
public class TimeController { | ||
|
||
TimeService timeService; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근 제어자를 package-private로 두신 이유가 있으실까요?? |
||
|
||
@Autowired | ||
TimeController(TimeService timeService) { | ||
this.timeService = timeService; | ||
} | ||
|
||
@GetMapping("/times") | ||
@ResponseBody | ||
public ResponseEntity<List<Time>> getAllTimes() { | ||
List<Time> body = timeService.searchAllTimes(); | ||
return ResponseEntity.ok(body); | ||
} | ||
|
||
@PostMapping("/times") | ||
@ResponseBody | ||
public ResponseEntity<Time> createTime(@RequestBody @Valid Time request) { | ||
Time response = timeService.setTime(request); | ||
URI location = URI.create("/times/" + response.getId()); | ||
|
||
return ResponseEntity.created(location).body(response); | ||
} | ||
|
||
@DeleteMapping("/times/{timeId}") | ||
@ResponseBody | ||
public ResponseEntity<Void> deleteTime(@PathVariable Long timeId) { | ||
timeService.removeTime(timeId); | ||
|
||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@ExceptionHandler({NoSuchElementException.class, MethodArgumentNotValidException.class}) | ||
public ResponseEntity<Void> handleException(Exception e) { | ||
return ResponseEntity.badRequest().build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package roomescape.entity; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class Reservation { | ||
|
||
private Long id; | ||
private String name; | ||
private String date; | ||
private Time time; | ||
|
||
public static Reservation create(Long id, String name, String date, Time time) { | ||
return new Reservation(id, name, date, time); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package roomescape.entity; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@Getter | ||
public class Time { | ||
|
||
private Long id; | ||
|
||
private String time; | ||
|
||
public static Time create(Long id, String time) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 정적 팩터리 메서드가 일반적인 생성자와 동일하게 동작하는 것 같아요 별도로 정적 팩터리 메서드를 추가하신 이유가 있으신가요? |
||
return new Time(id, time); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package roomescape.repository; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
import roomescape.entity.Reservation; | ||
import roomescape.entity.Time; | ||
|
||
@Repository | ||
public class ReservationRepository { | ||
|
||
@Autowired | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 필드 주입으로 jdbcTemplate을 주입해주셨군요. 주입 방법엔 여러가지가 있는데, 필드 주입을 사용하시는 이유가 있으신가요? |
||
JdbcTemplate jdbcTemplate; | ||
|
||
private final RowMapper<Reservation> rowMapper = (resultSet, rowNum) -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rowMapper를 깔끔하게 분리해주신 게 좋네요! |
||
Reservation.create( | ||
resultSet.getLong("id"), | ||
resultSet.getString("name"), | ||
resultSet.getString("date"), | ||
Time.create( | ||
resultSet.getLong("time_id"), | ||
resultSet.getString("time_value")) | ||
); | ||
|
||
|
||
public List<Reservation> findAll() { | ||
String selectSql = """ | ||
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(selectSql, rowMapper); | ||
} | ||
|
||
public Reservation create(Reservation reservation) { | ||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
|
||
String insertSql = "INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)"; | ||
jdbcTemplate.update(connection -> { | ||
PreparedStatement ps = connection.prepareStatement(insertSql, new String[]{"id"}); | ||
ps.setString(1, reservation.getName()); | ||
ps.setString(2, reservation.getDate()); | ||
ps.setString(3, String.valueOf(reservation.getTime().getId())); | ||
return ps; | ||
}, keyHolder); | ||
|
||
Long generatedId = keyHolder.getKey().longValue(); | ||
return Reservation.create( | ||
generatedId, | ||
reservation.getName(), | ||
reservation.getDate(), | ||
reservation.getTime() | ||
); | ||
} | ||
|
||
public void deleteById(Long id) { | ||
int rowsAffected = jdbcTemplate.update("delete from reservation where id = ?", id); | ||
|
||
if (rowsAffected == 0) { | ||
throw new NoSuchElementException(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package roomescape.repository; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
import roomescape.entity.Time; | ||
|
||
@Repository | ||
public class TimeRepository { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이름을 Repository라고 지어주셨군요. Repository와 Dao는 어떤 차이가 있을까요? |
||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
@Autowired | ||
TimeRepository(JdbcTemplate jdbcTemplate) { | ||
this.jdbcTemplate = jdbcTemplate; | ||
} | ||
|
||
private final RowMapper<Time> rowMapper = (rs, rowNum) -> | ||
Time.create(rs.getLong("id"), rs.getString("time")); | ||
|
||
|
||
public List<Time> findAll() { | ||
String selectSql = "select * from time"; | ||
return jdbcTemplate.query(selectSql, rowMapper); | ||
} | ||
|
||
public Time create(String time) { | ||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
|
||
String insertSql = "INSERT INTO time (time) VALUES (?)"; | ||
jdbcTemplate.update((connection) -> { | ||
PreparedStatement ps = connection.prepareStatement(insertSql, new String[]{"id"}); | ||
ps.setString(1, time); | ||
return ps; | ||
}, keyHolder); | ||
|
||
Long generatedId = keyHolder.getKey().longValue(); | ||
return Time.create(generatedId, time); | ||
} | ||
|
||
public void deleteById(Long id) { | ||
String deleteSql = "DELETE FROM time WHERE id = ?"; | ||
|
||
int rowsAffected = jdbcTemplate.update(deleteSql, id); | ||
if (rowsAffected == 0) { | ||
throw new NoSuchElementException(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package roomescape.service; | ||
|
||
import java.util.List; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
import roomescape.entity.Reservation; | ||
import roomescape.repository.ReservationRepository; | ||
|
||
@Service | ||
public class ReservationService { | ||
|
||
@Autowired | ||
private ReservationRepository reservationRepository; | ||
|
||
|
||
public List<Reservation> searchAllReservations() { | ||
return reservationRepository.findAll(); | ||
} | ||
|
||
public Reservation makeReservation(Reservation reservation) { | ||
return reservationRepository.create(reservation); | ||
} | ||
|
||
public void cancelReservation(Long id) { | ||
reservationRepository.deleteById(id); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package roomescape.service; | ||
|
||
import java.util.List; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
import roomescape.entity.Time; | ||
import roomescape.repository.TimeRepository; | ||
|
||
@Service | ||
public class TimeService { | ||
|
||
TimeRepository timeRepository; | ||
|
||
@Autowired | ||
TimeService(TimeRepository timeRepository) { | ||
this.timeRepository = timeRepository; | ||
} | ||
|
||
|
||
public List<Time> searchAllTimes() { | ||
return timeRepository.findAll(); | ||
} | ||
|
||
public Time setTime(Time time) { | ||
return timeRepository.create(time.getTime()); | ||
} | ||
|
||
public void removeTime(Long timeId) { | ||
timeRepository.deleteById(timeId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
spring.h2.console.enabled=true | ||
spring.h2.console.path=/h2-console | ||
spring.datasource.url=jdbc:h2:mem:database;DATABASE_TO_UPPER=false | ||
spring.datasource.username=sa | ||
spring.sql.init.schema-locations=classpath:sql/schema.sql |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Autowired를 붙인것과 안 붙인거는 어떤 차이가 있나요?