-
Notifications
You must be signed in to change notification settings - Fork 117
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 JDBC] 신예린 미션 제출합니다. #255
base: nyeroni
Are you sure you want to change the base?
Changes from 39 commits
92bd86a
0b72bb7
882f0bb
f63189e
6251646
26f4a4f
f390405
3c2cea6
1e5ad44
d0b93d4
dba44ed
bfac22a
e61c095
5374dcd
b491654
df43e64
1945b54
f5ac7f0
236ef81
9dac830
29efa97
757fd81
bcd7982
2727b15
131bb78
1222d95
a99cd04
37f3451
68d10ff
05960f2
e751780
5bfb047
24cc815
f662620
a06dd35
2729ac3
48b0bd9
0a79912
53a3367
2eee21d
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,13 @@ | ||
package roomescape.controller; | ||
|
||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
|
||
@Controller | ||
public class HomeController { | ||
|
||
@GetMapping("/") | ||
public String home() { | ||
return "home"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package roomescape.controller; | ||
|
||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
import roomescape.dto.ReservationRequest; | ||
import roomescape.dto.ReservationResponse; | ||
import roomescape.exception.BadRequestException; | ||
import roomescape.service.ReservationService; | ||
|
||
import java.net.URI; | ||
import java.util.List; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
public class ReservationApiController { | ||
|
||
private final ReservationService reservationService; | ||
|
||
@GetMapping("/reservations") | ||
public ResponseEntity<List<ReservationResponse>> reservations(){ | ||
List<ReservationResponse> responseDtoList = reservationService.findAllReservations(); | ||
|
||
return ResponseEntity.ok(responseDtoList); | ||
} | ||
|
||
@PostMapping("/reservations") | ||
public ResponseEntity<ReservationResponse> addReservation(@RequestBody ReservationRequest reservationRequest){ | ||
reservationRequest.validate(); | ||
ReservationResponse responseDto = reservationService.addReservation(reservationRequest); | ||
return ResponseEntity.created(URI.create("/reservations/" + responseDto.getId())) | ||
.body(responseDto); | ||
} | ||
|
||
@DeleteMapping("/reservations/{id}") | ||
public ResponseEntity<Void> cancelReservation(@PathVariable Long id){ | ||
reservationService.cancelReservation(id); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@ExceptionHandler(BadRequestException.class) | ||
public ResponseEntity<String> handleBadRequestException(BadRequestException ex) { | ||
return ResponseEntity.badRequest().body(ex.getMessage()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package roomescape.controller; | ||
|
||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
|
||
@Controller | ||
public class ReservationController { | ||
|
||
@GetMapping("/reservation") | ||
public String reservation() { | ||
return "reservation"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package roomescape.domain; | ||
|
||
import lombok.Data; | ||
|
||
import java.time.LocalDate; | ||
import java.time.LocalDateTime; | ||
import java.time.LocalTime; | ||
|
||
@Data | ||
public class Reservation { | ||
private Long id; | ||
private String name; | ||
private LocalDate date; | ||
private LocalTime time; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package roomescape.dto; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
import roomescape.exception.BadRequestException; | ||
|
||
import java.time.LocalDate; | ||
import java.time.LocalTime; | ||
|
||
@Getter | ||
@Setter | ||
public class ReservationRequest { | ||
private String name; | ||
private LocalDate date; | ||
private LocalTime time; | ||
|
||
public void validate() { | ||
if (name == null || name.isEmpty()) { | ||
throw new BadRequestException("예약 이름은 필수 입력값입니다."); | ||
} | ||
|
||
if (date == null) { | ||
throw new BadRequestException("예약 날짜는 필수 입력값입니다."); | ||
} | ||
|
||
if (time == null) { | ||
throw new BadRequestException("예약 시간은 필수 입력값입니다."); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package roomescape.dto; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
import roomescape.domain.Reservation; | ||
|
||
@Setter | ||
@Getter | ||
public class ReservationResponse { | ||
|
||
private Long id; | ||
private String name; | ||
private String date; | ||
private String time; | ||
|
||
public static ReservationResponse from(Reservation reservation) { | ||
ReservationResponse dto = new ReservationResponse(); | ||
dto.setId(reservation.getId()); | ||
dto.setName(reservation.getName()); | ||
dto.setDate(reservation.getDate().toString()); | ||
dto.setTime(reservation.getTime().toString()); | ||
return dto; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package roomescape.exception; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.ResponseStatus; | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
public class BadRequestException extends RuntimeException{ | ||
public BadRequestException(String message){ | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package roomescape.repository; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
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.domain.Reservation; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.sql.Statement; | ||
import java.util.List; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
public class ReservationJdbcRepository implements ReservationRepositoryImpl{ | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
private final RowMapper<Reservation> reservationRowMapper = (rs, rowNum) -> { | ||
Reservation reservation = new Reservation(); | ||
reservation.setId(rs.getLong("id")); | ||
reservation.setName(rs.getString("name")); | ||
reservation.setDate(rs.getDate("date").toLocalDate()); | ||
reservation.setTime(rs.getTime("time").toLocalTime()); | ||
return reservation; | ||
}; | ||
|
||
@Override | ||
public List<Reservation> findAll() { | ||
String sql = "SELECT * FROM reservation"; | ||
return jdbcTemplate.query(sql, reservationRowMapper); | ||
} | ||
|
||
@Override | ||
public Reservation save(Reservation reservation) { | ||
String sql = "INSERT INTO reservation (name, date, time) VALUES (?, ?, ?)"; | ||
|
||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
|
||
jdbcTemplate.update(connection -> { | ||
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); | ||
ps.setString(1, reservation.getName()); | ||
ps.setDate(2, java.sql.Date.valueOf(reservation.getDate())); | ||
ps.setTime(3, java.sql.Time.valueOf(reservation.getTime())); | ||
return ps; | ||
}, keyHolder); | ||
|
||
Long generatedId = keyHolder.getKey().longValue(); | ||
reservation.setId(generatedId); | ||
return reservation; | ||
} | ||
|
||
@Override | ||
public Reservation findById(Long id) { | ||
String sql = "SELECT * FROM reservation WHERE id = ?"; | ||
try { | ||
return jdbcTemplate.queryForObject(sql, reservationRowMapper, id); | ||
} catch (EmptyResultDataAccessException e) { | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public void deleteById(Long id) { | ||
String sql = "DELETE FROM reservation WHERE id = ?"; | ||
jdbcTemplate.update(sql, id); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package roomescape.repository; | ||
|
||
import org.springframework.stereotype.Repository; | ||
import roomescape.domain.Reservation; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
|
||
|
||
@Repository | ||
public class ReservationRepository implements ReservationRepositoryImpl{ | ||
private final Map<Long, Reservation> reservations = new HashMap<>(); | ||
private final AtomicLong atomicLong = new AtomicLong(0); | ||
|
||
public List<Reservation> findAll() { | ||
return new ArrayList<>(reservations.values()); | ||
} | ||
|
||
@Override | ||
public Reservation save(Reservation reservation) { | ||
if (reservation == null) { | ||
throw new IllegalArgumentException("예약 정보가 없습니다."); | ||
} | ||
|
||
if (reservation.getId() != null && reservations.containsKey(reservation.getId())) { | ||
throw new IllegalArgumentException("이미 등록된 예약입니다."); | ||
} | ||
reservation.setId(atomicLong.incrementAndGet()); | ||
reservations.put(reservation.getId(), reservation); | ||
|
||
return reservation; | ||
} | ||
|
||
@Override | ||
public Reservation findById(Long id) { | ||
return reservations.get(id); | ||
} | ||
|
||
@Override | ||
public void deleteById(Long id) { | ||
reservations.remove(id); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package roomescape.repository; | ||
|
||
import roomescape.domain.Reservation; | ||
|
||
import java.util.List; | ||
|
||
public interface ReservationRepositoryImpl { | ||
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. 저장소가 변경되더라도 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. interface에 차이를 주기 위해 implement의 줄임표현인 Impl를 사용했습니다! 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. 인터페이스를 구현하는 클래스에 |
||
|
||
List<Reservation> findAll(); | ||
Reservation save(Reservation reservation); | ||
Reservation findById(Long id); | ||
void deleteById(Long id); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package roomescape.service; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import roomescape.domain.Reservation; | ||
import roomescape.repository.ReservationJdbcRepository; | ||
import roomescape.repository.ReservationRepository; | ||
import roomescape.dto.ReservationRequest; | ||
import roomescape.dto.ReservationResponse; | ||
import roomescape.exception.BadRequestException; | ||
|
||
import java.time.LocalDate; | ||
import java.time.LocalTime; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ReservationService { | ||
|
||
private final ReservationRepository reservationRepository; | ||
private final ReservationJdbcRepository reservationJdbcRepository; | ||
public List<ReservationResponse> findAllReservations() { | ||
List<Reservation> reservations = reservationJdbcRepository.findAll(); | ||
List<ReservationResponse> responseDtoList = reservations.stream() | ||
.map(ReservationResponse::from) | ||
.collect(Collectors.toList()); | ||
|
||
return responseDtoList; | ||
} | ||
|
||
public ReservationResponse addReservation(ReservationRequest reservationRequest){ | ||
String name = reservationRequest.getName(); | ||
LocalDate date = reservationRequest.getDate(); | ||
LocalTime time = reservationRequest.getTime(); | ||
|
||
Reservation reservation = new Reservation(); | ||
reservation.setName(name); | ||
reservation.setDate(date); | ||
reservation.setTime(time); | ||
|
||
Reservation savedReservation = reservationJdbcRepository.save(reservation); | ||
|
||
return ReservationResponse.from(savedReservation); | ||
} | ||
|
||
public void cancelReservation(Long id) { | ||
ReservationResponse reservationResponse = findById(id); | ||
reservationJdbcRepository.deleteById(reservationResponse.getId()); | ||
} | ||
|
||
public ReservationResponse findById(Long id) { | ||
Reservation reservation = reservationJdbcRepository.findById(id); | ||
if(reservation == null) { | ||
throw new BadRequestException("예약을 찾을 수 없습니다. (id=" + id + ")"); | ||
} | ||
return ReservationResponse.from(reservation); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
spring: | ||
h2: | ||
console: | ||
enabled: true | ||
path: /h2-console | ||
datasource: | ||
url: jdbc:h2:mem:database | ||
hikari: | ||
username: sa | ||
password: | ||
driver-class-name: org.h2.Driver |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
CREATE TABLE reservation | ||
( | ||
id BIGINT NOT NULL AUTO_INCREMENT, | ||
name VARCHAR(255) NOT NULL, | ||
date VARCHAR(255) NOT NULL, | ||
time VARCHAR(255) NOT NULL, | ||
PRIMARY KEY (id) | ||
); |
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.
도메인
reservation
은 모든 필드에setter
가 있기 때문에 저장소 뿐만 아니라 어디에서도 필드에 접근하여 다른 값으로 변경 할 수 있네요.예린님은
setter
의 사용에 대해서 어떻게 생각하시는지 궁금합니다 :)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.
원랜 엔티티에서
setter
를 사용하지 않는데, 이번엔 잘못 넣은 것 같네요!Reservation
에setter
를 두지 않고 필요할 때 추가하는 것이 맞다고 생각합니다!