2

We're using Hypersistence Utils, which is a great tool, but we're facing a challenge regarding Session cache, as mentioned in another question; it can't be disabled.

So, the following test fails because findOne fetches the object from the session cache:

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

There's a workaround calling entityManager.clear() every time SQLStatementCountValidator.reset() is called.

Now, the workaround is fine, but error-prone because now we have to inject EntityManager as a dependency of our tests and remember to call entityManager.clear() after saving all the objects that represent our scenario.

Questions

  1. What would be the best way of achieving this?
  2. Would you expect SQLStatementCountValidator to also clear the entityManager?

Here you can check the log statements (the last one)

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!

This is how the workaround code looks like:

    @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);
    }
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
Gastón Fournier
  • 956
  • 1
  • 9
  • 25

1 Answers1

1

Transaction handling

Each test should manage transactions. So, you should remove the @Transactional annotation you added at the class level.

So, you inject a TransactionTemplate bean:

@Autowired
private TransactionTemplate transactionTemplate;

And, then you save the entity in one transaction:

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

You can extract the transaction handling logic in a base class method to simplify the exception handling.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911