簡體   English   中英

使用 JPA 和 Hibernate 在測試期間檢測 N+1 查詢問題

[英]Detecting N+1 query issues during testing with JPA and Hibernate

我們正在使用https://github.com/vladmihalcea/db-util這是一個很棒的工具,但是我們面臨着 Session 緩存的挑戰,正如另一個問題中提到的,它不能被禁用

因此,以下測試失敗,因為findOne從 session 緩存中獲取 object:

    @Test
    public void validateQueries() {
        // when (scenario definition)
        TestObject testObject = new TestObject(1L);
        repository.save(testObject);
        SQLStatementCountValidator.reset();

        repository.findOne(1L);
        SQLStatementCountValidator.assertSelectCount(1);
    }

每次調用SQLStatementCountValidator.reset()時,都會調用entityManager.clear()的解決方法。

現在,解決方法很好,但容易出錯,因為現在我們必須注入 EntityManager 作為測試的依賴項,並記住在保存代表我們場景的所有對象后調用entityManager.clear()

問題

  1. 實現這一目標的最佳方法是什么?
  2. 您是否希望 SQLStatementCountValidator 也清除 entityManager?

在這里可以查看日志語句(最后一條)

09:59.956 [main] [TRACE] o.h.e.i.AbstractSaveEventListener - Transient instance of: TestObject
09:59.957 [main] [TRACE] o.h.e.i.DefaultPersistEventListener - Saving transient instance
09:59.962 [main] [TRACE] o.h.e.i.AbstractSaveEventListener - Saving [TestObject#<null>]
Hibernate: 
    insert 
    into
        test_object
        (id, creation_time, "update_time", "name") 
    values
        (null, ?, ?, ?)
10:00.005 [main] [TRACE] o.h.e.i.AbstractFlushingEventListener - Flushing session
10:00.005 [main] [DEBUG] o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
10:00.007 [main] [DEBUG] o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
10:00.007 [main] [TRACE] o.h.e.i.AbstractFlushingEventListener - Flushing entities and processing referenced collections
10:00.011 [main] [TRACE] o.h.e.i.AbstractFlushingEventListener - Processing unreferenced collections
10:00.011 [main] [TRACE] o.h.e.i.AbstractFlushingEventListener - Scheduling collection removes/(re)creates/updates
10:00.011 [main] [DEBUG] o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
10:00.011 [main] [DEBUG] o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
10:00.015 [main] [TRACE] o.h.e.i.AbstractFlushingEventListener - Executing flush
10:00.015 [main] [TRACE] o.h.e.i.AbstractFlushingEventListener - Post flush
10:02.780 [main] [TRACE] o.h.e.i.DefaultLoadEventListener - Loading entity: [TestObject#1]
10:08.439 [main] [TRACE] o.h.e.i.DefaultLoadEventListener - Attempting to resolve: [TestObject#1]
10:08.439 [main] [TRACE] o.h.e.i.DefaultLoadEventListener - Resolved object in session cache: [TestObject#1]

com.vladmihalcea.sql.exception.SQLSelectCountMismatchException: Expected 1 statements but recorded 0 instead!

解決方法代碼如下所示:

    @Test
    public void validateQueries() {
        // when (scenario definition)
        TestObject testObject = new TestObject(1L);
        repository.save(testObject);
        entityManager.clear();
        SQLStatementCountValidator.reset();

        repository.findOne(1L);
        SQLStatementCountValidator.assertSelectCount(1);
    }

事務處理

每個測試都應該管理事務。 因此,您應該刪除在 class 級別添加的@Transactional注釋。

所以,你注入一個TransactionTemplate bean:

@Autowired
private TransactionTemplate transactionTemplate;

然后,您將實體保存在一個事務中:

@Test
public void validateQueries() {
    try {
        transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
            TestObject testObject = new TestObject(1L);
            repository.save(testObject);

            return null;
        });
    } catch (TransactionException e) {
        LOGGER.error("Failure", e);
    }

    SQLStatementCountValidator.reset();
    repository.findOne(1L); 
    SQLStatementCountValidator.assertSelectCount(1);
}

您可以在基本 class 方法中提取事務處理邏輯以簡化異常處理。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM