简体   繁体   中英

Hibernate lazy-loaded collections and PropertyChangeSupport

I have an entity that lazily initializes a Set, and I have also added PropertyChangeSupport to that entity class. Here's how the setter looks like for the Set:

public void setAskPrices(Set<AskPrice> askPrices) {
    propertyChangeSupport.firePropertyChange(ASKPRICES_PROPERTY, this.askPrices,
            this.askPrices = askPrices);
}

At some other point in my code, I build a Criteria query, and I want it to eagerly fetch this collection:

List<PriceRequest> pr = session.createCriteria(PriceRequest.class)
                        .setFetchMode("askPrices", FetchMode.JOIN)
                        .add(Restrictions.ilike("reqNum", "%" + reqNum + "%")).list();

When I run the query above, I get an exception in Hibernate:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:122)
at org.hibernate.collection.PersistentSet.size(PersistentSet.java:162)
at java.util.AbstractSet.equals(AbstractSet.java:75)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:273)
at com.frc_agencies.model.persistent.PriceRequest.setAskPrices(PriceRequest.java:160)

After digging around, I found out that the firePropertyChange() function calls oldValu.equals(newValue). In my case newValue is the new persistent Set. The equals() function at some point calls size() on the new Set, which in turn calls org.hibernate.collection.AbstractPersistentCollection.readSize(), which looks like this:

protected boolean readSize() {
    if (!initialized) {
        if ( cachedSize!=-1 && !hasQueuedOperations() ) {
            return true;
        }
        else {
            throwLazyInitializationExceptionIfNotConnected();
            CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this);
            CollectionPersister persister = entry.getLoadedPersister();
            if ( persister.isExtraLazy() ) {
                if ( hasQueuedOperations() ) {
                    session.flush();
                }
                cachedSize = persister.getSize( entry.getLoadedKey(), session );
                return true;
            }
        }
    }
    read();
    return false;
}

The exception is thrown at throwLazyInitializationExceptionIfNotConnected(). It calls the following method:

/**
 * Is the collection currently connected to an open session?
 */
private final boolean isConnectedToSession() {
    return session!=null && 
            session.isOpen() &&
            session.getPersistenceContext().containsCollection(this);
}

It returns false on session.getPersistenceContext().containsCollection(this); So for some reason, at this point the persistent collection is not part of the current session.

I decided to make an experiment. I removed the setFetchMode() call on the Criteria query, and simply after the query call returned, I called:

Hibernate.initialize(pr.getAskPrices());

And that seemed to work fine!

But I don't want to keep calling Hibernate.initialize() all the time. Can anyone suggest what I may possibly do to make this work using my original setFetchMode() call?

Thanks.

Edit: Posting the relevant mapping

<set name="askPrices" table="ASK_PRICE" inverse="true" lazy="true" fetch="select" cascade="all-delete-orphan">
    <meta attribute="bound">ASKPRICES_PROPERTY</meta>
    <key on-delete="cascade">
        <column name="REQ_ID" not-null="true" />
    </key>
    <one-to-many class="AskPrice" />
</set>

What worked for me was to immediately turn the set into another type of Collection:

    List listOfObjects =  new ArrayList(aMethod.getASetOfObjects());

We are stuck using an older version of Hibernate. So my guess is that there is an error in the Hibernate code. By immediately constructing another java Collection, it copies the object to (non-volatile) physical memory as opposed to hibernate cache.

So the original question would resolve to something like:

    List this.askPrices = new ArrayList(askPrices)

Hope this helps.

When you set this.askprice = askprice, you made your this.askprice not persistence, so the next statement with session will throw lazy exception. You can change your lazy to false in the Xml mapping (lazy="false"), which is a bad practice I am using or can try using Spring and Hibernatetemplate, this may solve the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM