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

여행 생성 및 여행 상세 조회 쿼리 개선 #651

Merged
merged 16 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package hanglog.city.domain.repository;

import hanglog.city.domain.City;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface CityRepository extends JpaRepository<City, Long> {

@Query("""
SELECT c
FROM City c
WHERE c.id in :ids
""")
List<City> findCitiesByIds(@Param("ids") final List<Long> ids);

@Query("""
SELECT c
FROM City c, TripCity tc
WHERE c.id = tc.city.id
AND tc.trip.id = :tripId
""")
List<City> findCitiesByTripId(@Param("tripId") final Long tripId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hanglog.expense.dto.response;


import hanglog.city.domain.City;
import hanglog.city.dto.response.CityResponse;
import hanglog.currency.domain.Currency;
import hanglog.expense.domain.Amount;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ public class RequestLoggingFilter extends AbstractRequestLoggingFilter {

@Override
protected void beforeRequest(@NonNull final HttpServletRequest request, @NonNull final String message) {
logger.info(message);
if (!message.contains("prometheus")) {
logger.info(message);
}
Comment on lines +13 to +15
Copy link
Collaborator

Choose a reason for hiding this comment

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

오 ㅋㅋㅋ 👍 👍 👍 👍 👍

}

@Override
protected void afterRequest(@NonNull final HttpServletRequest request, @NonNull final String message) {
logger.info(message);
}
}
14 changes: 12 additions & 2 deletions backend/src/main/java/hanglog/trip/domain/DayLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
import jakarta.persistence.OrderBy;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
Expand Down Expand Up @@ -47,7 +49,7 @@ public class DayLog extends BaseEntity {

@OneToMany(mappedBy = "dayLog", cascade = REMOVE, orphanRemoval = true)
@OrderBy(value = "ordinal ASC")
private List<Item> items = new ArrayList<>();
private Set<Item> items = new HashSet<>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
private Set<Item> items = new HashSet<>();
private Set<Item> items = new LinkedHashSet<>();

아이템 순서도 보장하게 LinkedHashSet을 사용하는건 어떤가요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@OrderBy가 붙여져있으면 내부적으로 LinkedHashSet으로 처리해줘서 그냥 두었는데 명시적으로 LinkedHashSet으로 할당하는 것도 좋겠군여

Copy link
Member Author

@jjongwa jjongwa Oct 4, 2023

Choose a reason for hiding this comment

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

순서는 @OrderBy로 보장하고 있어서
굳이 HashSet보다 성능이 떨어지는(물론 둘 다 O(1)이긴 하지만) LinkedHashSet로 바꿀 필요까지는 없다는 생각입니다.!

Copy link
Collaborator

Choose a reason for hiding this comment

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

내부적으로 LinkedHashSet으로 처리한다면, 어떤 부분에서 성능이 다른가용 ?? 갖고 있을 때 메모리를 더 잡아먹는다 ??
저는 명시적으로 + 혹시 모를 버그 예방을 위해 LinkedHashSet에 한 표 하겠습니다 ㅎㅎ

Copy link
Collaborator

Choose a reason for hiding this comment

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

@OrderBy를 쓰면 HashSet으로 생성을 해도 순서가 보장이 되는지 몰랐네요.

저도 홍고와 라온 말처럼 명시적으로 혹은 혹시 모를 버그를 방지하기 위한 LinkedHashSet으로 할당하는 것에 한표 하겠습니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

성능 차이는 일반 List와 LinkedList의 차이와 같습니다.!
어떤 자료구조를 사용하냐인데 이건 답글로 일일이 달 필요까지는 없다고 생각합니다.(저희 ArrayList LinkedList 미니 미션도 했으니까요..!)

그리고 명시적으로 라는 부분도 잘 모르곘는게,
Trip의 DayLogs라면 이해가 됩니다. 저희가 추가 삭제 할 때마다 순서에 맞게 기타 DayLog의 실제 위치를 변경했으니까요.!

그런데 DayLog의 Items는 다르다고 생각합니다.
저희가 아이템 위치 바꿀 때 기존에 존재하는 아이템들 사이에 끼워 넣나요?
그냥 order 필드만 바꾸고 있습니다..! 따라서 시간복잡도는 그냥 O(1)이죠
애초에 저희가 순서 변경을 할 때 컬랙션 내에서 item의 위치를 바꾸는게 아닌데
왜 LinkedHashSet으로 바꾸는게 명시적인 효과가 있는건지 잘 모르겠다는 의견입니다..!

그으으으런데 홍고의 말대로 @OrderBy가 붙여져있을 때
HashSet으로 선언해놔도 알아서 LinkedHashSet 형태로 저장되어 있다면
LinkedHashSet으로 바꿔도 좋을 것 같습니다. (근데 진짜 그렇게 바뀌나요? 그러면 코드 단에서 밑줄로 잡아줘야 하는거 아닌가)


public DayLog(
final Long id,
Expand All @@ -60,7 +62,7 @@ public DayLog(
this.title = title;
this.ordinal = ordinal;
this.trip = trip;
this.items = items;
this.items = new HashSet<>(items);
}

public DayLog(
Expand All @@ -83,4 +85,12 @@ public LocalDate calculateDate() {
final LocalDate startDate = trip.getStartDate();
return startDate.plusDays((long) ordinal - DEFAULT_DAY);
}

public void addItem(final Item item) {
items.add(item);
}

public List<Item> getItems() {
return new ArrayList<>(items);
}
}
18 changes: 16 additions & 2 deletions backend/src/main/java/hanglog/trip/domain/Trip.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
import jakarta.persistence.OrderBy;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
Expand Down Expand Up @@ -77,7 +79,7 @@ public class Trip extends BaseEntity {

@OneToMany(mappedBy = "trip", cascade = {PERSIST, REMOVE, MERGE}, orphanRemoval = true)
@OrderBy(value = "ordinal ASC")
private List<DayLog> dayLogs = new ArrayList<>();
private Set<DayLog> dayLogs = new HashSet<>();

@OneToOne(mappedBy = "trip", cascade = {PERSIST, REMOVE, MERGE}, orphanRemoval = true)
private SharedTrip sharedTrip;
Expand All @@ -104,7 +106,7 @@ public Trip(
this.endDate = endDate;
this.description = description;
this.sharedTrip = sharedTrip;
this.dayLogs = dayLogs;
this.dayLogs = new HashSet<>(dayLogs);
this.sharedStatus = sharedStatus;
this.publishedStatus = publishedStatus;
}
Expand Down Expand Up @@ -169,4 +171,16 @@ public void changePublishedStatus(final Boolean isPublished) {
public Boolean isWriter(final Long memberId) {
return this.member.getId().equals(memberId);
}

public void addDayLog(final DayLog dayLog) {
dayLogs.add(dayLog);
}

public void removeDayLog(final DayLog dayLog) {
dayLogs.remove(dayLog);
}

public List<DayLog> getDayLogs() {
return new ArrayList<>(dayLogs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hanglog.trip.domain.repository;

import hanglog.trip.domain.DayLog;
import java.util.List;

public interface CustomDayLogRepository {

void saveAll(final List<DayLog> dayLogs);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package hanglog.trip.domain.repository;

import hanglog.trip.domain.DayLog;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

@RequiredArgsConstructor
@Repository
public class CustomDayLogRepositoryImpl implements CustomDayLogRepository {

private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

@Override
public void saveAll(final List<DayLog> dayLogs) {
final String sql = """
INSERT INTO day_log (created_at, modified_at, ordinal, status, title, trip_id)
VALUES (:createdAt, :modifiedAt, :ordinal, :status, :title, :tripId)
""";
namedParameterJdbcTemplate.batchUpdate(sql, getDayLogToSqlParameterSources(dayLogs));
}

private MapSqlParameterSource[] getDayLogToSqlParameterSources(final List<DayLog> dayLogs) {
return dayLogs.stream()
.map(this::getDayLogToSqlParameterSource)
.toArray(MapSqlParameterSource[]::new);
}

private MapSqlParameterSource getDayLogToSqlParameterSource(final DayLog dayLog) {
final LocalDateTime now = LocalDateTime.now();
return new MapSqlParameterSource()
.addValue("createdAt", now)
.addValue("modifiedAt", now)
.addValue("ordinal", dayLog.getOrdinal())
.addValue("status", dayLog.getStatus().name())
.addValue("title", dayLog.getTitle())
.addValue("tripId", dayLog.getTrip().getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hanglog.trip.domain.repository;

import hanglog.city.domain.City;
import java.util.List;

public interface CustomTripCityRepository {

void saveAll(final List<City> cities, final Long tripId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package hanglog.trip.domain.repository;

import static hanglog.global.type.StatusType.USABLE;

import hanglog.city.domain.City;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

@RequiredArgsConstructor
@Repository
public class CustomTripCityRepositoryImpl implements CustomTripCityRepository {

private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;

@Override
public void saveAll(final List<City> cities, final Long tripId) {
final String sql = """
INSERT INTO trip_city (city_id, created_at, modified_at, status, trip_id)
VALUES (:cityId, :createdAt, :modifiedAt, :status, :tripId)
""";
namedParameterJdbcTemplate.batchUpdate(sql, getTripCityToSqlParameterSources(cities, tripId));
}

private MapSqlParameterSource[] getTripCityToSqlParameterSources(final List<City> cities, final Long tripId) {
return cities.stream()
.map(city -> getTripCityToSqlParameterSource(city, tripId))
.toArray(MapSqlParameterSource[]::new);
}

private MapSqlParameterSource getTripCityToSqlParameterSource(final City city, final Long tripId) {
final LocalDateTime now = LocalDateTime.now();
return new MapSqlParameterSource()
.addValue("cityId", city.getId())
.addValue("createdAt", now)
.addValue("modifiedAt", now)
.addValue("status", USABLE.name())
.addValue("tripId", tripId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,18 @@ public interface TripRepository extends JpaRepository<Trip, Long> {

void deleteAllByMemberId(final Long memberId);

@Query("""
SELECT trip
FROM Trip trip
LEFT JOIN FETCH trip.sharedTrip sharedTrip
LEFT JOIN FETCH trip.dayLogs dayLogs
LEFT JOIN FETCH dayLogs.items items
LEFT JOIN FETCH items.images images
LEFT JOIN FETCH items.expense expense
LEFT JOIN FETCH items.place place
LEFT JOIN FETCH expense.category expense_category
LEFT JOIN FETCH place.category place_category
WHERE dayLogs.trip.id = :tripId
""")
Optional<Trip> findById(@Param("tripId") final Long tripId);
}
Loading