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

[1단계 - 지하철 정보 관리 기능] 조이(김성연) 미션 제출합니다. #54

Merged
merged 21 commits into from
May 20, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
# jwp-subway-path
# jwp-subway-path

## 요구 사항
### API 기능 요구사항
- [x] 노선에 역 등록 API 신규 구현
- [x] 노선에 역 제거 API 신규 구현
- [x] 노선 조회 API
- [x] 노선 포함된 역 목록 조회 API 수정
- [x] 노선에 포함된 역을 순서대로 보여주도록 응답을 개선합니다.


## 기능 목록
- [x] 노선에 역을 등록한다.
- [x] 노선에 역을 등록할 때 거리 정보도 계산한다.
- [ ] 거리 정보는 양의 정수로 제한
- [x] 노선 가운데 역이 등록 될 경우 거리 정보를 새롭게 계산해야 한다.
- [x] 노선 가운데 역이 등록 될 경우, 가능한 거리를 초과한 경우 예외를 발생시킨다.
- [ ] 최초 등록 시 한 역만 등록하려는 경우 예외를 발생시킨다.

- [x] 노선에서 역을 제거한다.
- [x] 노선에서 역이 제거될 경우 역과 역 사이의 거리도 재배정되어야 한다.
- [x] 노선에서 역을 제거할 경우 정상 동작을 위해 재배치 되어야 한다.
- [x] 노선에 등록된 역이 2개 인 경우 하나의 역을 제거할 때 두 역이 모두 제거되어야 한다.

3 changes: 2 additions & 1 deletion src/main/java/subway/application/LineService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package subway.application;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import subway.dao.LineDao;
import subway.domain.Line;
import subway.dto.LineRequest;
Expand All @@ -9,6 +10,7 @@
import java.util.List;
import java.util.stream.Collectors;

@Transactional
@Service
public class LineService {
private final LineDao lineDao;
Expand Down Expand Up @@ -49,5 +51,4 @@ public void updateLine(Long id, LineRequest lineUpdateRequest) {
public void deleteLineById(Long id) {
lineDao.deleteById(id);
}

}
140 changes: 140 additions & 0 deletions src/main/java/subway/application/SectionService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package subway.application;

import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import subway.dao.LineDao;
import subway.dao.SectionDao;
import subway.dao.StationDao;
import subway.domain.Direction;
import subway.domain.Section;
import subway.domain.Sections;
import subway.domain.Station;
import subway.dto.SectionRequest;

@Transactional
@Service
public class SectionService {
private final StationDao stationDao;
private final SectionDao sectionDao;
private final LineDao lineDao;

public SectionService(final StationDao stationDao, final SectionDao sectionDao, final LineDao lineDao) {
this.stationDao = stationDao;
this.sectionDao = sectionDao;
this.lineDao = lineDao;
}

public long addSection(final long lineId, SectionRequest request) {
validateLineId(lineId);
Station requestUpStation = findVerifiedStationByName(request.getUpStationName());
Station requestDownStation = findVerifiedStationByName(request.getDownStationName());

Sections sections = sectionDao.findSectionsByLineId(lineId);

Section requestedSection = new Section(requestUpStation, requestDownStation, request.getDistance());

if (sections.isInitialSave()) {
return sectionDao.save(requestedSection, lineId);
}
if (sections.isDownEndAppend(requestedSection)) {
long savedSectionId = sectionDao.save(requestedSection, lineId);
Section downEndSection = sections.getDownEndSection();
sectionDao.updateNextSection(savedSectionId, downEndSection.getId());
return savedSectionId;
}
if (sections.isUpEndAppend(requestedSection)) {
Section upEndSection = sections.getUpEndSection();
Section newSection = new Section(requestUpStation, requestDownStation, request.getDistance(),
upEndSection.getNextSectionId());
return sectionDao.save(newSection, lineId);
}

Section includeSection = sections.getIncludeSection(requestedSection);
Direction direction = includeSection.checkDirection(requestedSection);
if (direction == Direction.INNER_LEFT) {
Section innerRight = new Section(requestedSection.getDownStation(), includeSection.getDownStation(),
includeSection.getDistance() - requestedSection.getDistance(),
includeSection.getNextSectionId());

long savedId = sectionDao.save(innerRight, lineId);

Section innerLeft = new Section(includeSection.getId(), requestedSection.getUpStation(),
requestedSection.getDownStation(), requestedSection.getDistance(), savedId);

sectionDao.update(innerLeft);
return savedId;
}

Copy link

Choose a reason for hiding this comment

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

메서드를 분리해볼까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

기존에 SectionService의 로직 분리가 어렵다고 느껴 전체 구조를 변경해보았습니다!
수정하면서 메서드 분리도 함께 진행하였습니다.
감사합니다!

Section innerRight = new Section(requestedSection.getUpStation(), requestedSection.getDownStation(),
requestedSection.getDistance(), includeSection.getNextSectionId());
long savedId = sectionDao.save(innerRight, lineId);

Section innerLeft = new Section(includeSection.getId(), includeSection.getUpStation(),
requestedSection.getUpStation(),
includeSection.getDistance() - requestedSection.getDistance(),
savedId);
sectionDao.update(innerLeft);
return savedId;
}

public void removeStation(long stationId, long lineId) {
Sections sections = sectionDao.findSectionsByLineId(lineId);

validateStationInLine(stationId, sections);

// 역이 2개 남았을 때
if (sections.size() == 1) {
sectionDao.deleteByLineId(lineId);
}
// 상행 종점일 때
if (sections.getUpEndSection().isSameUpStationId(stationId)) {
Copy link

Choose a reason for hiding this comment

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

get하지 않고 바로 물어볼 수 있을 것 같아요~

Copy link
Member Author

Choose a reason for hiding this comment

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

수정하였습니다!

sectionDao.deleteSectionByUpStationId(stationId, lineId);
return;
}
// 하행 종점일 때
Section downEndSection = sections.getDownEndSection();
if (downEndSection.isSameDownStationId(stationId)) {
Section section = sections.findSectionByNextSection(downEndSection);
Copy link

Choose a reason for hiding this comment

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

여기도 동일하게 변경해보면 좋겠네요

Copy link
Member Author

Choose a reason for hiding this comment

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

수정하였습니다!

sectionDao.deleteSectionByDownStationId(stationId, lineId);
sectionDao.updateNextSection(null, section.getId());
return;
}
Section innerRight = sections.findSectionByUpStation(stationId);
Section innerLeft = sections.findSectionByDownStation(stationId);
Section newSection = new Section(innerLeft.getId(), innerLeft.getUpStation(), innerRight.getDownStation(),
innerLeft.getDistance() + innerRight.getDistance(),
innerRight.getNextSectionId());
sectionDao.update(newSection);
sectionDao.deleteById(innerRight.getId());
}

private void validateStationInLine(final long stationId, final Sections sections) {
if (sections.isNotExistStation(stationId)) {
throw new IllegalArgumentException("해당 노선에 존재하지 않는 역입니다.");
}
}

public Sections findAllByLindId(long lineId) {
return sectionDao.findSectionsByLineId(lineId);
}

public List<Station> findSortedAllStationsByLindId(long lineId) {
Sections sections = sectionDao.findSectionsByLineId(lineId);
return sections.getSortedStations();
}

private void validateLineId(long lineId) {
if (!lineDao.existsById(lineId)) {
throw new IllegalArgumentException("존재하지 않는 호선입니다.");
}
}

private Station findVerifiedStationByName(String name) {
Copy link

Choose a reason for hiding this comment

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

어떤 의도의 네이밍일까요?
해당 메서드가 두가지일을 하고 있는 것 같아요~

Copy link
Member Author

Choose a reason for hiding this comment

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

Station Name을 통해 Station을 찾고 없으면 생성하여 반환해주고자 하였습니다.
또링의 말씀대로 두 가지일을 하고 있는 것 같습니다.
해당 메서드는 리팩터링 과정에서 제거하였습니다!

if(!stationDao.existsByName(name)) {
return stationDao.insert(new Station(name));
}

return stationDao.findByName(name);
}
}
4 changes: 3 additions & 1 deletion src/main/java/subway/application/StationService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package subway.application;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import subway.dao.StationDao;
import subway.domain.Station;
import subway.dto.StationRequest;
Expand All @@ -9,6 +10,7 @@
import java.util.List;
import java.util.stream.Collectors;

@Transactional
@Service
public class StationService {
private final StationDao stationDao;
Expand Down Expand Up @@ -41,4 +43,4 @@ public void updateStation(Long id, StationRequest stationRequest) {
public void deleteStationById(Long id) {
stationDao.deleteById(id);
}
}
}
25 changes: 17 additions & 8 deletions src/main/java/subway/dao/LineDao.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
package subway.dao;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import subway.domain.Line;

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

@Repository
public class LineDao {
private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert insertAction;

private RowMapper<Line> rowMapper = (rs, rowNum) ->
private final RowMapper<Line> rowMapper = (rs, rowNum) ->
new Line(
rs.getLong("id"),
rs.getString("name"),
rs.getString("color")
);

public LineDao(JdbcTemplate jdbcTemplate, DataSource dataSource) {
public LineDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.insertAction = new SimpleJdbcInsert(dataSource)
this.insertAction = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("line")
.usingGeneratedKeyColumns("id");
}
Expand Down Expand Up @@ -58,4 +56,15 @@ public void update(Line newLine) {
public void deleteById(Long id) {
jdbcTemplate.update("delete from Line where id = ?", id);
}

public boolean existsById(Long id) {
String sql = "SELECT COUNT(*) FROM Line WHERE id = ?";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class, id);

if (integer == null) {
return false;
}

return integer > 0;
}
}
105 changes: 105 additions & 0 deletions src/main/java/subway/dao/SectionDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package subway.dao;

import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import subway.domain.Section;
import subway.domain.Sections;
import subway.domain.Station;

@Repository
public class SectionDao {

private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert simpleJdbcInsert;

public SectionDao(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("SECTIONS")
.usingGeneratedKeyColumns("id");
}

public Sections findSectionsByLineId(final long lineId) {

String sql = "SELECT SEC.id, S1.id, S1.name, S2.id, S2.name, SEC.distance, SEC.next_id"
+ " FROM SECTIONS AS SEC "
+ " JOIN STATION AS S1 ON SEC.up_id = S1.id "
+ " JOIN STATION AS S2 ON SEC.down_id = S2.id "
+ " WHERE SEC.line_id = ?";

List<Section> sections = jdbcTemplate.query(sql, upStationMapper(), lineId);

return new Sections(lineId, sections);
}

private RowMapper<Section> upStationMapper() {
return (rs, rowNum) -> new Section(
rs.getLong(1),
new Station(rs.getLong(2), rs.getString(3)),
new Station(rs.getLong(4), rs.getString(5)),
rs.getInt(6),
rs.getLong(7));
}

public long save(final Section section, final long lineId) {
SqlParameterSource sqlParameterSource = new MapSqlParameterSource()
.addValue("id", section.getId())
.addValue("up_id", section.getUpStationId())
.addValue("down_id", section.getDownStationId())
.addValue("distance", section.getDistance())
.addValue("line_id", lineId)
.addValue("next_id", section.getNextSectionId());

return simpleJdbcInsert.executeAndReturnKey(sqlParameterSource).longValue();
}

public void updateNextSection(Long nextId, long sectionId) {
String sql = "UPDATE SECTIONS SET next_id = ? WHERE id = ?";
jdbcTemplate.update(sql, nextId, sectionId);
}

public void update(final Section section) {
String sql = "UPDATE SECTIONS SET up_id = ?, down_id = ?, distance = ?, next_id = ? WHERE id = ?";
jdbcTemplate.update(sql,
section.getUpStationId(),
section.getDownStationId(),
section.getDistance(),
section.getNextSectionId(),
section.getId());
}

public void deleteById(final long sectionId) {
String sql = "delete SECTIONS where id = ?";
jdbcTemplate.update(sql, sectionId);
}

public void deleteSectionByUpStationId(final long stationId, final long lineId) {
String sql = "delete SECTIONS where up_id = ? AND line_id = ?";
jdbcTemplate.update(sql, stationId, lineId);
}

public void deleteSectionByDownStationId(final long stationId, final long lineId) {
String sql = "delete SECTIONS where down_id = ? AND line_id = ?";
jdbcTemplate.update(sql, stationId, lineId);
}

public void deleteByLineId(long lineId) {
String sql = "delete SECTIONS where line_id = ?";
jdbcTemplate.update(sql, lineId);
}

public Section findById(long sectionId) {
String sql = "SELECT SEC.id, S1.id, S1.name, S2.id, S2.name, SEC.distance, SEC.next_id"
+ " FROM SECTIONS AS SEC "
+ " JOIN STATION AS S1 ON SEC.up_id = S1.id "
+ " JOIN STATION AS S2 ON SEC.down_id = S2.id "
+ " WHERE SEC.id = ?";

return jdbcTemplate.queryForObject(sql, upStationMapper(), sectionId);
}
}
Loading