Re-introducing entity as proxied to the Session after "persist" a "new" object

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:

@Column(name = "request_id")
private Long requestId;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "request_id", insertable = false, updatable = false)
private UpgradeRequest upgradeRequest;

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:

org.hibernate.internal.util.EntityPrinter
  com.db.access.UpgradeRequest{clubMember=<uninitialized>,

Is there a way to reintroduce entity to the Session so it will be a Hibernate managed proxy instead of original new created object?

So I made it working!

@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.

Eviction requires deep knowledge of object internals. I’d like to have a magic spell which refresh 1 level cache in a single call.

You could control what needs to be detached in the object graph through CascadeType.DETACH on associations.

with null s in some lazy loaded relations

You have three options.

  1. Maintain the associations yourself.
  2. Refresh the entities. Note that you can control how an object graph is refreshed with CascadeType.REFRESH on the associations.
  3. Clear the EntityManager/Session after flush to let Hibernate reload the entities when needed.
1 Like