In the Spring based test after adding @TestExecutionListeners({TransactionalTestExecutionListener.class}) on class level and @org.springframework.transaction.annotation.Transactional on method level my test started to fail.
The reason for it: the test prepares fixtures inside itself and after extending transaction to the test body the entities created & persisted as:
UpgradeJob upgradeJob = new UpgradeJob();
upgradeJobRepository.save(upgradeJob);
appear as non-proxies original objects in the session with nulls in some lazy loaded relations. We don’t want to propagate those relations but want Hibernate resolves them for use because they are of a special type - a primitive ID + a lazy relation reference to the same record, like:
Later the tested code accesses attributes which are null and not-lazy fetched because Session keeps non-proxies objects. So tests are failing with NPE.
Without the transaction on test level tested code doesn’t have global test transaction and so lacks Hibernate session so Hibernate retrieves entities from DB as proxy objects with lazy behavior. I see an indication of it, notice uninitialized when logged:
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void test() {
UpgradeRequest upgradeRequest = new UpgradeRequest();
upgradeRequestRepo.save(upgradeRequest);
UpgradeJob upgradeJob = new UpgradeJob();
upgradeJob.setUpgradeRequest(upgradeRequest);
upgradeJobRepo.save(upgradeJob);
Session session = entityManager.unwrap(org.hibernate.Session.class);
session.evict(upgradeJob);
session.evict(upgradeRequest);
...
}
Then the query loading a list of UpgradeRequest has all fields marked as Lazy resolved!
Reminder: a test set IDs but didn’t set representing entities, they marked as LAZY & insertable/updateble = false. All object are in the Session 1 level cache (except evicted) so no proxies created - all objects were resolved. No NPE as entities are resolved. During constructing test fixtures by new we didn’t set entities!
I don’t know if there are another magical calls, like Hibernate.initialize() or whatever which reloads insertable/updateble = false attributes in the 1 level cache. Eviction requires deep knowledge of object internals. I’d like to have a magic spell which refresh 1 level cache in a single call.