Skip to content

batch-size 사용을 통한 N+1 문제 해결#242

Merged
mete0rfish merged 6 commits intomainfrom
fix-nplus1
Dec 3, 2025
Merged

batch-size 사용을 통한 N+1 문제 해결#242
mete0rfish merged 6 commits intomainfrom
fix-nplus1

Conversation

@mete0rfish
Copy link
Copy Markdown
Member

@mete0rfish mete0rfish commented Dec 3, 2025

User description

#️⃣ 이슈

  • close #이슈번호

🔎 작업 내용

https://meteorfish.tistory.com/6


PR Type

Bug fix, Enhancement


Description

  • Resolved N+1 problem in blueprint search.

    • Removed unnecessary fetch joins.
    • Added default_batch_fetch_size property.
  • Added cart builder pattern.

  • Modified cascade types for CartBlueprint and OrderBlueprint.


Diagram Walkthrough

flowchart LR
  A["Blueprint Search Business"] --> B{"Blueprint Repository"};
  B -- "findAllNameAndCreatorContaining" --> C[Blueprint];
  C --> D{"OrderBlueprints"};
  D -- "Lazy Loading with Batch Fetch" --> E[OrderBlueprint];
Loading

File Walkthrough

Relevant files
Bug fix
3 files
BlueprintSearchBusiness.java
Refactor blueprint search to remove N+1 issue                       
+1/-3     
BlueprintRepository.java
Remove unnecessary fetch join queries                                       
+1/-16   
BlueprintSearchService.java
Remove unused fetch join methods                                                 
+0/-16   
Enhancement
1 files
Cart.java
Add builder pattern to Cart entity                                             
+3/-3     
Configuration changes
5 files
CartBlueprint.java
Modify cascade type in CartBlueprint entity                           
+2/-2     
OrderBlueprint.java
Modify cascade type in OrderBlueprint entity                         
+1/-1     
application-dev.properties
Add `default_batch_fetch_size` property                                   
+3/-1     
application-local.properties
Add `default_batch_fetch_size` property                                   
+2/-0     
application.properties
Add `default_batch_fetch_size` property                                   
+2/-0     
Tests
4 files
TestDataLoader.java
Modify test data generation                                                           
+74/-134
BlueprintSearchBusinessTest.java
Update blueprint search business test                                       
+0/-4     
BlueprintReoisutiryTest.java
Add N+1 problem test for blueprint repository                       
+59/-0   
application-test.yml
Configure test environment for N+1 detection                         
+9/-5     

@mete0rfish mete0rfish temporarily deployed to fix-nplus1 - backend PR #242 December 3, 2025 04:44 — with Render Destroyed
@mete0rfish mete0rfish changed the title batch-size 사용을 통한 N+1 문제 해결 batch-size 사용을 통한 N+1 문제 해결 Dec 3, 2025
@mete0rfish
Copy link
Copy Markdown
Member Author

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Cascade Type

The cascade type for CartBlueprint and OrderBlueprint has been modified. Verify that the changes in cascade types do not introduce unintended side effects, such as data inconsistencies or unexpected deletions.

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JoinColumn(name = "cart_id")
private Cart cart;

//도면 상품은 도면 엔티티가 나오는 대로 짤게요
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JoinColumn(name = "blueprint_id")
private Blueprint blueprint;
Test Data

The TestDataLoader has been significantly expanded. Ensure that the generated test data is sufficient and representative of real-world scenarios to adequately test the application's functionality.

firstCategoryRepository.saveAll(Arrays.asList(
        FirstCategory.builder().id(1L).name("building").build(),
        FirstCategory.builder().id(2L).name("civil").build(),
        FirstCategory.builder().id(3L).name("interior").build(),
        FirstCategory.builder().id(4L).name("machine").build(),
        FirstCategory.builder().id(5L).name("electric").build(),
        FirstCategory.builder().id(6L).name("etc").build()
));

for (int i = 1; i <= 20; i++) {
    // 1. Create Member
    Member member = memberJpaRepository.save(
            Member.builder()
                    .email("test" + i + "@test.com")
                    .password(passwordEncoder.encode("password"))
                    .name("User " + i)
                    .build()
    );

    // 2. Create Blueprint
    Blueprint blueprint = blueprintRepository.save(
            Blueprint.builder()
                    .blueprintName("테스트 마을 " + i)
                    .blueprintDetails("테스트 마을의 청사진입니다.")
                    .creatorName("작가 " + i)
                    .standardPrice(50000L)
                    .salePrice(40000L)
                    .program("CAD")
                    .downloadLink("https://onetool.com/download")
                    .extension(".dwg")
                    .blueprintImg("https://s3.bucket.image.com/")
                    .categoryId((long) (i % 6 + 1))
                    .secondCategory("주거")
                    .inspectionStatus(InspectionStatus.PASSED)
                    .build()
    );

    // 3. Create Cart and Order for the Member
    Cart cart = cartRepository.save(Cart.builder().member(member).build());
    Orders order = orderRepository.save(Orders.builder().member(member).build());

    // 4. Create CartBlueprint
    cartBlueprintRepository.save(
            CartBlueprint.builder()
                    .cart(cart)
                    .blueprint(blueprint)
                    .build()
    );

    // 5. Create OrderBlueprint
    orderBlueprintRepository.save(
            OrderBlueprint.builder()
                    .order(order)
                    .blueprint(blueprint)
                    .build()
    );
}
N+1 Test

The test keyword_n_plus_1_test verifies that the N+1 problem is resolved. Confirm that the query count assertion is correct and that the test accurately reflects the expected behavior.

@DisplayName("키워드 검색 시, N+1 문제가 발생하지 않는다")
@Test
@Transactional
void keyword_n_plus_1_test() {
    // given
    Pageable pageable = PageRequest.of(0, 20);

    Session session = entityManager.unwrap(Session.class);
    Statistics statistics = session.getSessionFactory().getStatistics();
    statistics.setStatisticsEnabled(true);
    statistics.clear();

    // when
    Page<Blueprint> blueprints = blueprintRepository.findAllNameAndCreatorContaining("마을", InspectionStatus.PASSED, pageable);

    for (Blueprint blueprint : blueprints.getContent()) {
        blueprint.getOrderBlueprints().size();
    }

    // then
    long queryCount = statistics.getPrepareStatementCount();
    System.out.println("Executed queries: " + queryCount);

    assertThat(queryCount).as("N+1 문제가 발생했습니다. 예상 쿼리 수: 3, 실제 쿼리 수: " + queryCount).isEqualTo(3L);
}

@mete0rfish
Copy link
Copy Markdown
Member Author

Failed to generate code suggestions for PR

@mete0rfish mete0rfish merged commit 26c4500 into main Dec 3, 2025
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant