Skip to content
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] 정다영 미션 제출합니다. #274

Open
wants to merge 38 commits into
base: day024
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6ec0522
Create README.md
day024 May 10, 2024
cc9cecb
Update README.md
day024 May 10, 2024
2b8857c
feat: 의존성추가, homeController 생성
day024 May 10, 2024
a11027e
docs: 2단계 요구사항
day024 May 10, 2024
0e1d276
feat: [2단계] 예약페이지 응답 추가
day024 May 10, 2024
77d7d76
feat: [2단계] 예약페이지 응답 추가
day024 May 10, 2024
85045b2
feat: [2단계] 예약페이지 응답 추가
day024 May 10, 2024
1a8b0fb
docs: 2단계 요구사항 수정
day024 May 10, 2024
daaff1f
feat: 2단계 예약목록 조회 추가
day024 May 10, 2024
f70e5eb
docs: 3단계 요구사항 추가
day024 May 10, 2024
6a38973
feat: 예약추가 API구현
day024 May 12, 2024
d9ba166
feat: 예약 삭제 API 구현
day024 May 12, 2024
d436151
docs: 4단계 요구사항 수정
day024 May 12, 2024
5242bc4
feat: [4단계] 학습테스트 추가
day024 May 12, 2024
c43e527
feat: [4단계]InvalidRequestException 처리
day024 May 12, 2024
52fb4c0
feat: [4단계]NotFoundReservationException 처리
day024 May 12, 2024
2443995
fix: 공백제거
day024 May 14, 2024
23c4741
fix: 예외 문구 상수변경
day024 May 15, 2024
d9f0591
fix: 예외 문구 재수정
day024 May 15, 2024
d07daf8
fix: 유효성검증 메서드분리
day024 May 15, 2024
1f4e8b1
docs: 요구사항 수정
day024 May 15, 2024
98fa170
docs: [5단계] 요구사항 수정
day024 May 19, 2024
b1f8f59
docs: [5단계] 요구사항 수정
day024 May 19, 2024
6860177
chore: jdbc, h2 의존성추가
day024 May 19, 2024
9564f6e
chore: schema 파일, 테이블 생성
day024 May 19, 2024
0f7a02b
chore: H2 DB 설정 추가
day024 May 19, 2024
9f787e8
chore: H2 DB 설정 추가
day024 May 19, 2024
369ed2e
test: [5단계]학습테스트 추가
day024 May 19, 2024
7b9ff9e
docs: [6단계] 요구사항 추가
day024 May 19, 2024
ccc556e
test: [7단계] 요구사항 추가
day024 May 19, 2024
39d4e57
refactor: AtomicLong제거, 쿼리문변경
day024 May 20, 2024
badc33e
test: [6단계,7단계]test추가
day024 May 20, 2024
8e38268
test: 테스트 수정
day024 May 20, 2024
24b7879
feat: 삭제api 예외추가
day024 May 20, 2024
05053eb
Update README.md
day024 May 26, 2024
810a6a0
fix: conflicts해결
day024 May 26, 2024
819f180
Merge branch 'step5,6,7' of https://github.com/day024/spring-roomesca…
day024 May 26, 2024
b0dcc7a
rollback
day024 May 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Spring JDBC

<h3>5단계 요구사항</h3>

h2 데이터베이스를 활용하여 데이터를 저장하도록 수정
- [x] gradle 의존성 추가
- [x] 테이블 스키마 정의
- [x] 데이터베이스 설정
- [x] 요구 사항 테스트 진행

<h3>6단계 요구사항</h3>

예약 조회 API 처리 로직에서 저장된 예약을 조회할 때 데이터베이스를 활용하도록 수정
- [x] 데이터를 조회하는 기능을 구현
- SQL - SELECT 쿼리
- JdbcTemplate
- [x] 요구 사항 테스트 추가

<h3>7단계 요구사항</h3>
데이터 추가/삭제하기

- [x] 예약 추가/취소 API 처리 로직에서 데이터베이스를 활용하도록 수정
- 기존에 사용하던 List 및 AtomicLong 제거
- [x] 요구 사항 테스트 추가
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'

testImplementation 'org.assertj:assertj-core:3.20.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:5.3.1'

runtimeOnly 'com.h2database:h2'
}

test {
useJUnitPlatform()
}
}
64 changes: 64 additions & 0 deletions src/main/java/roomescape/DAO/ReservationDAO.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공통 피드백에서 나온 내용을 잘 반영하셨네요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package roomescape.DAO;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import roomescape.domain.Reservation;
import roomescape.domain.Time;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class ReservationDAO {

private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert simpleJdbcInsert;

public ReservationDAO(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("reservation")
.usingGeneratedKeyColumns("id");
}

public List<Reservation> findAll() {
String sql = "SELECT r.id, r.name, r.date, t.id as time_id, t.time FROM reservation r INNER JOIN time t ON r.time_id = t.id";
return jdbcTemplate.query(sql, (rs, rowNum) -> {
long id = rs.getLong("id");
String name = rs.getString("name");
String date = rs.getString("date");
Time time = new Time(rs.getLong("time_id"), rs.getString("time"));
return new Reservation(id, name, date, time);
});
}

public Reservation findById(long id) {
String sql = "SELECT r.id, r.name, r.date, t.id as time_id, t.time FROM reservation r INNER JOIN time t ON r.time_id = t.id WHERE r.id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, (rs, rowNum) -> {
long reservationId = rs.getLong("id");
String name = rs.getString("name");
String date = rs.getString("date");
Time time = new Time(rs.getLong("time_id"), rs.getString("time"));
return new Reservation(reservationId, name, date, time);
});
}

public long save(Reservation reservation) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", reservation.getName());
parameters.put("date", reservation.getDate());
parameters.put("time_id", reservation.getTime().getId());

Number key = simpleJdbcInsert.executeAndReturnKey(parameters);
return key.longValue();
}

public long deleteById(long id) {
String sql = "DELETE FROM reservation WHERE id = ?";
int rowsAffected = jdbcTemplate.update(sql, id);
return rowsAffected > 0 ? id : -1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rowsAffected가 0인 경우에는 controller에서 예외처리를 하셨는데
DAO 에서 바로 예외처리를하면 해당 예외가 데이터베이스와 관련된 것임을 명확히 할 수 있지 않을까요?

}
}
48 changes: 48 additions & 0 deletions src/main/java/roomescape/DAO/TimeDAO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package roomescape.DAO;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import roomescape.domain.Time;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository
public class TimeDAO {
private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert simpleJdbcInsert;

public TimeDAO(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("time")
.usingGeneratedKeyColumns("id");
}

public List<Time> findAll() {
String sql = "SELECT id, time FROM time";
return jdbcTemplate.query(sql, (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time")));
}

public Time findById(long id) {
String sql = "SELECT id, time FROM time WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time")));
}

public long save(Time time) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("time", time.getTime());

Number key = simpleJdbcInsert.executeAndReturnKey(parameters);
return key.longValue();
}

public long deleteById(long id) {
String sql = "DELETE FROM time WHERE id = ?";
int rowsAffected = jdbcTemplate.update(sql, id);
return rowsAffected > 0 ? id : -1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reservationDAO 와 동일합니다!

}
}
3 changes: 1 addition & 2 deletions 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);
}

}
}
13 changes: 13 additions & 0 deletions src/main/java/roomescape/controller/HomeController.java
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";
}
}
60 changes: 60 additions & 0 deletions src/main/java/roomescape/controller/ReservationController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package roomescape.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import roomescape.domain.Reservation;
import roomescape.DAO.ReservationDAO;
import roomescape.exception.NotFoundReservationException;
import roomescape.service.ReservationService;

import javax.sql.DataSource;
import java.net.URI;
import java.util.List;

@Controller
public class ReservationController {
private final String NOT_FOUND_RESERVATION_MESSAGE = "삭제할 예약을 찾을 수 없습니다.";
private final ReservationService reservationService;

@Autowired
public ReservationController(ReservationService reservationService) {
this.reservationService = reservationService;
}

@GetMapping("/reservation")
public String newReservation() {
return "new-reservation";
}

@GetMapping("/reservations")
public ResponseEntity<List<Reservation>> getReservations() {
List<Reservation> reservationList = reservationService.findAll();
return ResponseEntity.ok(reservationList);
}

@PostMapping("/reservations")
public ResponseEntity<Reservation> addReservation(@RequestBody Reservation request) {

long id = reservationService.save(request);
Reservation reservation = reservationService.findById(id);
URI location = URI.create("/reservations/" + id);

return ResponseEntity.created(location).body(reservation);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findById와 save 메서드를 분리하신 이유가 있을까요?

@DeleteMapping("/reservations/{id}")
public ResponseEntity<Void> cancelReservation(@PathVariable long id) {
long deletedId = reservationService.deleteById(id);

if (deletedId == -1) {
throw new NotFoundReservationException(NOT_FOUND_RESERVATION_MESSAGE);
}

URI location = URI.create("/reservations/" + deletedId);
ResponseEntity.noContent().location(location).build();

return ResponseEntity.noContent().build();
}
}
51 changes: 51 additions & 0 deletions src/main/java/roomescape/controller/TimeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package roomescape.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import roomescape.domain.Time;
import roomescape.service.TimeService;

import javax.sql.DataSource;
import java.net.URI;
import java.util.List;

@Controller
public class TimeController {
private final TimeService timeService;

@Autowired
public TimeController(TimeService timeService) {
this.timeService = timeService;
}

@GetMapping("/time")
public String times() {
return "time";
}

@PostMapping("/times")
public ResponseEntity<Time> addTime(@RequestBody Time request) {
long id = timeService.save(request);
Time time = timeService.findById(id);
URI location = URI.create("/times/" + id);

return ResponseEntity.created(location).body(time);
}

@GetMapping("/times")
public ResponseEntity<List<Time>> getTimes() {
List<Time> timeList = timeService.findAll();
return ResponseEntity.ok(timeList);
}

@DeleteMapping("/times/{id}")
public ResponseEntity<Void> deleteTime(@PathVariable long id) {
long deletedId = timeService.deleteById(id);
if (deletedId == -1) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
}
65 changes: 65 additions & 0 deletions src/main/java/roomescape/domain/Reservation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package roomescape.domain;

import roomescape.exception.InvalidRequestException;

public class Reservation {
private Long id;
private String name;
private String date;
private Time time;

private final String INVALID_NAME_REQUEST_MESSAGE = "이름 정보가 공백입니다.";
private final String INVALID_DATE_REQUEST_MESSAGE = "날짜 정보가 공백입니다.";
private final String INVALID_TIME_REQUEST_MESSAGE = "시간 정보가 공백입니다.";

public Reservation(long id, String name, String date, Time time) {
this.id = id;
this.name = name;
this.date = date;
this.time = time;
}

public long getId() {
return id;
}

public String getDate() {
return date;
}

public String getName() {
return name;
}

public Time getTime() {
return time;
}

public void Validate(){
nameValidate();
dateValidate();
timeValidate();
}

public void nameValidate() {
if (isBlank(name)) {
throw new InvalidRequestException(INVALID_NAME_REQUEST_MESSAGE);
}
}

public void dateValidate() {
if (isBlank(date)) {
throw new InvalidRequestException(INVALID_DATE_REQUEST_MESSAGE);
}
}

public void timeValidate() {
if (isBlank(time.getTime())) {
throw new InvalidRequestException(INVALID_TIME_REQUEST_MESSAGE);
}
}

private boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
}
27 changes: 27 additions & 0 deletions src/main/java/roomescape/domain/Time.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package roomescape.domain;

public class Time {
private Long id;
private String time;

public Time(Long id, String time) {
this.id = id;
this.time = time;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getTime() {
return time;
}

public void setTime(String time) {
this.time = time;
}
}
Loading