-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Api: ✏️ 월별 지출내역 조회시 발생하는 N+1 문제 개선 (#110)
* test: 커스텀 카테고리를 가지는 spendingfixture를 만들기위한 customcategoryfixture 작성 * test: 커스텀 카테고리를 가지는 spending 조회시 lazy loading 확인용 테스트 작성 * test: fetch 테스트를 위한 spendingfixture 추가작성 * test: 테스트용 customcategory 벌크 삽입연산 메소드 작성 및 customcategory spending 생성 메소드 변경 * test: searchspendings customcategory fetch 테스트 작성 * fix: 월별 지출내역 조회시 N+1 문제 개선을 위한 fetchjoin 사용 * test: 테스트 assertion 수정 * test: 랜덤한 커스텀 카테고리가 아닌 동일한 커스텀 카테고리 사용 * refactor: searchspendingservice 제거를 위한 spendingcustomrepository 연월별 조회 메서드 작성 * test: desc sorted 검증 추가
- Loading branch information
Showing
11 changed files
with
205 additions
and
60 deletions.
There are no files selected for viewing
41 changes: 0 additions & 41 deletions
41
...ernal-api/src/main/java/kr/co/pennyway/api/apis/ledger/service/SpendingSearchService.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
...l-api/src/test/java/kr/co/pennyway/api/apis/ledger/service/SpendingSearchServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package kr.co.pennyway.api.apis.ledger.service; | ||
|
||
import jakarta.persistence.EntityManager; | ||
import jakarta.persistence.PersistenceContext; | ||
import kr.co.pennyway.api.config.ExternalApiDBTestConfig; | ||
import kr.co.pennyway.api.config.ExternalApiIntegrationTest; | ||
import kr.co.pennyway.api.config.fixture.SpendingFixture; | ||
import kr.co.pennyway.api.config.fixture.UserFixture; | ||
import kr.co.pennyway.domain.domains.spending.domain.Spending; | ||
import kr.co.pennyway.domain.domains.spending.service.SpendingService; | ||
import kr.co.pennyway.domain.domains.user.domain.User; | ||
import kr.co.pennyway.domain.domains.user.service.UserService; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.hibernate.engine.spi.SessionFactoryImplementor; | ||
import org.hibernate.engine.spi.SessionImplementor; | ||
import org.hibernate.stat.Statistics; | ||
import org.junit.jupiter.api.*; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.time.LocalDate; | ||
import java.util.List; | ||
import java.util.stream.IntStream; | ||
|
||
@Slf4j | ||
@ExtendWith(MockitoExtension.class) | ||
@ExternalApiIntegrationTest | ||
class SpendingSearchServiceTest extends ExternalApiDBTestConfig { | ||
@Autowired | ||
private UserService userService; | ||
@Autowired | ||
private SpendingService spendingService; | ||
@Autowired | ||
private NamedParameterJdbcTemplate jdbcTemplate; | ||
|
||
@PersistenceContext | ||
private EntityManager entityManager; | ||
private Statistics statistics; | ||
|
||
|
||
@BeforeEach | ||
public void setUp() { | ||
SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) ((SessionImplementor) entityManager.getDelegate()).getSessionFactory(); | ||
statistics = sessionFactory.getStatistics(); | ||
statistics.setStatisticsEnabled(true); | ||
} | ||
|
||
@AfterEach | ||
public void tearDown() { | ||
statistics.clear(); | ||
} | ||
|
||
@Test | ||
@Transactional | ||
@DisplayName("커스텀 카테고리 지출 내역을 기간별 조회시 카테고리를 바로 fetch 한다.") | ||
void testReadSpendingsLazyLoading() { | ||
// given | ||
User user = userService.createUser(UserFixture.GENERAL_USER.toUser()); | ||
SpendingFixture.bulkInsertSpending(user, 100, true, jdbcTemplate); | ||
|
||
// when | ||
List<Spending> spendings = spendingService.readSpendings(user.getId(), LocalDate.now().getYear(), LocalDate.now().getMonthValue()).orElseThrow(); | ||
|
||
int size = spendings.size(); | ||
for (Spending spending : spendings) { | ||
log.info("지출내역 id : {} 커스텀 카테고리 id : {} 커스텀 카테고리 name : {}", | ||
spending.getId(), | ||
spending.getSpendingCustomCategory().getId(), | ||
spending.getSpendingCustomCategory().getName() | ||
); | ||
} | ||
|
||
// then | ||
log.info("쿼리문 실행 횟수: {}", statistics.getPrepareStatementCount()); | ||
log.info("readSpendings로 조회해온 지출 내역 개수: {}", size); | ||
|
||
Assertions.assertEquals(2, statistics.getPrepareStatementCount()); | ||
|
||
boolean isSortedDescending = IntStream.range(0, spendings.size() - 1) | ||
.allMatch(i -> !spendings.get(i).getSpendAt().isBefore(spendings.get(i + 1).getSpendAt())); | ||
Assertions.assertTrue(isSortedDescending); | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...al-api/src/test/java/kr/co/pennyway/api/config/fixture/SpendingCustomCategoryFixture.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package kr.co.pennyway.api.config.fixture; | ||
|
||
import kr.co.pennyway.domain.domains.spending.domain.SpendingCustomCategory; | ||
import kr.co.pennyway.domain.domains.spending.type.SpendingCategory; | ||
import kr.co.pennyway.domain.domains.user.domain.User; | ||
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource; | ||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; | ||
import org.springframework.jdbc.core.namedparam.SqlParameterSource; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
|
||
public enum SpendingCustomCategoryFixture { | ||
GENERAL_SPENDING_CUSTOM_CATEGORY("커스텀 지출 내역 카테고리", SpendingCategory.FOOD); | ||
|
||
private final String name; | ||
private final SpendingCategory icon; | ||
|
||
SpendingCustomCategoryFixture(String name, SpendingCategory icon) { | ||
this.name = name; | ||
this.icon = icon; | ||
} | ||
|
||
public static void bulkInsertCustomCategory(User user, int capacity, NamedParameterJdbcTemplate jdbcTemplate) { | ||
Collection<SpendingCustomCategory> customCategories = getCustomCategories(user, capacity); | ||
|
||
String sql = String.format(""" | ||
INSERT INTO `%s` (name, icon, user_id, created_at, updated_at, deleted_at) | ||
VALUES (:name, 1, :user.id, NOW(), NOW(), null) | ||
""", "spending_custom_category"); | ||
SqlParameterSource[] params = customCategories.stream() | ||
.map(BeanPropertySqlParameterSource::new) | ||
.toArray(SqlParameterSource[]::new); | ||
jdbcTemplate.batchUpdate(sql, params); | ||
} | ||
|
||
private static List<SpendingCustomCategory> getCustomCategories(User user, int capacity) { | ||
List<SpendingCustomCategory> customCategories = new ArrayList<>(capacity); | ||
|
||
for (int i = 0; i < capacity; i++) { | ||
customCategories.add(SpendingCustomCategoryFixture.GENERAL_SPENDING_CUSTOM_CATEGORY.toCustomSpendingCategory(user)); | ||
} | ||
return customCategories; | ||
} | ||
|
||
public SpendingCustomCategory toCustomSpendingCategory(User user) { | ||
return SpendingCustomCategory.of(name, icon, user); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
...main/java/kr/co/pennyway/domain/domains/spending/repository/SpendingCustomRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,13 @@ | ||
package kr.co.pennyway.domain.domains.spending.repository; | ||
|
||
import kr.co.pennyway.domain.domains.spending.domain.Spending; | ||
import kr.co.pennyway.domain.domains.spending.dto.TotalSpendingAmount; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public interface SpendingCustomRepository { | ||
Optional<TotalSpendingAmount> findTotalSpendingAmountByUserId(Long userId, int year, int month); | ||
|
||
Optional<List<Spending>> findByYearAndMonth(Long userId, int year, int month); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.