Skip to content
Open
9 changes: 9 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:5.3.1'
}
Expand Down
1 change: 0 additions & 1 deletion src/main/java/roomescape/RoomescapeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ public class RoomescapeApplication {
public static void main(String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
}

}
68 changes: 68 additions & 0 deletions src/main/java/roomescape/controller/ReservationController.java
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})
public ResponseEntity<Void> handleException(Exception e) {
return ResponseEntity.badRequest().build();
}
}
59 changes: 59 additions & 0 deletions src/main/java/roomescape/controller/TimeController.java
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;

@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();
}
}
19 changes: 19 additions & 0 deletions src/main/java/roomescape/entity/Reservation.java
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);
}
}
18 changes: 18 additions & 0 deletions src/main/java/roomescape/entity/Time.java
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) {
return new Time(id, time);
}
}
73 changes: 73 additions & 0 deletions src/main/java/roomescape/repository/ReservationRepository.java
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
JdbcTemplate jdbcTemplate;

private final RowMapper<Reservation> rowMapper = (resultSet, rowNum) ->
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();
}
}
}
55 changes: 55 additions & 0 deletions src/main/java/roomescape/repository/TimeRepository.java
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 {

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();
}
}
}
27 changes: 27 additions & 0 deletions src/main/java/roomescape/service/ReservationService.java
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);
}
}
31 changes: 31 additions & 0 deletions src/main/java/roomescape/service/TimeService.java
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);
}
}
5 changes: 5 additions & 0 deletions src/main/resources/application.properties
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
Loading