How to test whether lazy loaded JPA collection is initialized?
Asked Answered
I

3

60

I have a service that gets a JPA entity from outside code. In this service I would like to iterate over a lazily loaded collection that is an attribute of this entity to see if the client has added something to it relative to the current version in the DB.

However, the client may have never touched the collection so it's still not initialized. This results in the well known

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.SomeEntity.

Of course, if the client never touched the collection, my service doesn't have to check it for possible changes. The thing is that I can't seem to find a way to test whether the collection is initialized or not. I guess I could call size() on it and if it throws LazyInitializationException I would know, but I'm trying not to depend on such patterns.

Is there some isInitialized() method somewhere?

Inellineloquent answered 29/11, 2010 at 17:44 Comment(0)
H
65

Are you using JPA2?

PersistenceUnitUtil has two methods that can be used to determine the load state of an entity.

e.g. there is a bidirectional OneToMany/ManyToOne relationship between Organization and User.

public void test() {
    EntityManager em = entityManagerFactory.createEntityManager();
    PersistenceUnitUtil unitUtil =
        em.getEntityManagerFactory().getPersistenceUnitUtil();

    em.getTransaction().begin();
    Organization org = em.find(Organization.class, 1);
    em.getTransaction().commit();

    Assert.assertTrue(unitUtil.isLoaded(org));
    // users is a field (Set of User) defined in Organization entity
    Assert.assertFalse(unitUtil.isLoaded(org, "users"));

    initializeCollection(org.getUsers());
    Assert.assertTrue(unitUtil.isLoaded(org, "users"));
    for(User user : org.getUsers()) {
        Assert.assertTrue(unitUtil.isLoaded(user));
        Assert.assertTrue(unitUtil.isLoaded(user.getOrganization()));
    }
}

private void initializeCollection(Collection<?> collection) {
    // works with Hibernate EM 3.6.1-SNAPSHOT
    if(collection == null) {
        return;
    }
    collection.iterator().hasNext();
}
Honk answered 9/12, 2010 at 15:57 Comment(2)
You are right, thanks! I forgot to check back at stack overflow, so sorry for the late acceptance.Inellineloquent
Or more simply using the static method: Persistence.getPersistenceUtil().isLoaded(org,"user")Bureaucracy
T
36
org.hibernate.Hibernate.isInitialized(..)

There is no standard JPA solution to my knowledge. But if you want to actually initialize collections, you can create an utility method and iterate them (only one iteration is enough).

Tildatilde answered 29/11, 2010 at 17:47 Comment(6)
That's exactly what I'm looking for, thanks! The problem is that even though I happen to be using Hibernate, the code should be JPA compliant so unfortunately I'm not really able to use this :(Inellineloquent
entityManager.find() is pretty standard ;) Put differently, the code should also work on EclipseLink ;)Inellineloquent
isn't find() doing a different thing?Tildatilde
Yes, but I meant that you said "nothing is standard", but find, even though it does something else, works on both Hibernate and EclipseLink. So in other words "standards do exists".Inellineloquent
This works for me as well. Though I find the Hibernate abstraction very leaky. I don't want to have in my controllers code like 'Hibernate.xy()'. I see why cannot it be easily fixed since Hibernate traded a lot for the promise to work 'seamlessly' which then results in such abstraction leaks...Deadlight
"But if you want to actually initialize collections, you can create an utility method and iterate them" This is false at least with Hibernate. Actually if you do that on a detached initialized collection you will get LazyInitializationException: failed to lazily initialize a collection. Explanation: A lazy initialized collection is not a List or a Set but an Hibernate PersistentCollection, or PersistentBag. That thing has to have the session associated to, and be referenced in the persistent context, or else the initialisation won't be performedGrasso
S
3

For eclipselink, users cast the collection you are trying to access to an org.eclipse.persistence.indirection.IndirectList, and then call its isInstantiated() method. The following link has more information:

http://www.eclipse.org/eclipselink/api/1.1/org/eclipse/persistence/indirection/IndirectList.html#isInstantiated.

Setter answered 1/12, 2011 at 12:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.