Обнаружение проблем с N + 1 во время тестирования с использованием JPA и Hibernate - PullRequest
1 голос
/ 10 апреля 2020

Мы используем https://github.com/vladmihalcea/db-util, который является отличным инструментом, но мы сталкиваемся с проблемой, касающейся кэша сеанса, которая, как упоминалось в другом вопросе, не может быть отключена 1005 *.

Итак, следующий тест не пройден, потому что findOne выбирает объект из кэша сеанса:

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

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

Существует обходной путь, вызывающий entityManager.clear() каждый раз, когда вызывается SQLStatementCountValidator.reset().

Теперь, обходной путь хорош, но подвержен ошибкам, потому что теперь мы должны внедрить 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);
    }

1 Ответ

1 голос
/ 10 апреля 2020

Обработка транзакций

Каждый тест должен управлять транзакциями. Итак, вы должны удалить аннотацию @Transactional, добавленную вами на уровне класса.

Итак, вы вводите компонент TransactionTemplate:

@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);
}

Вы можете извлечь логи обработки транзакций c в методе базового класса, чтобы упростить обработку исключений.

...