Hibernate 延迟加载集合和 PropertyChangeSupport

发布于 2024-12-03 19:38:11 字数 3818 浏览 0 评论 0原文

我有一个延迟初始化 Set 的实体,并且我还向该实体类添加了 PropertyChangeSupport。 Set 的设置器如下所示:

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

在我的代码中的某个其他点,我构建了一个 Criteria 查询,并且我希望它急切地获取此集合:

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

当我运行上面的查询时,我在 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)

挖掘后周围,​​我发现 firePropertyChange() 函数调用 oldValue.equals(newValue)。就我而言, newValue 是新的持久集。 equals() 函数在某个时刻调用新 Set 上的 size(),后者又调用 org.hibernate.collection.AbstractPersistentCollection.readSize(),如下所示:

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

在 throwLazyInitializationExceptionIfNotConnected() 处抛出异常。 它调用以下方法:

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

它在 session.getPersistenceContext().containsCollection(this) 上返回 false; 因此,由于某种原因,此时持久集合不是当前会话的一部分。

我决定做一个实验。我删除了 Criteria 查询上的 setFetchMode() 调用,并且在查询调用返回后,我调用了:

Hibernate.initialize(pr.getAskPrices());

这似乎工作正常!

但我不想一直调用 Hibernate.initialize() 。谁能建议我可以做什么来使用我原来的 setFetchMode() 调用来完成这项工作?

谢谢。

编辑: 发布相关映射

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

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

天暗了我发光 2024-12-10 19:38:11

对我有用的是立即将集合转变为另一种类型的集合:

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

我们只能使用旧版本的 Hibernate。所以我的猜测是 Hibernate 代码中有错误。通过立即构造另一个 java Collection,它将对象复制到(非易失性)物理内存,而不是 hibernate 缓存。

因此,最初的问题将解决为:

    List this.askPrices = new ArrayList(askPrices)

希望这有帮助。

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.

你不是我要的菜∠ 2024-12-10 19:38:11

当您设置 this.askprice = askprice 时,您使 this.askprice 不再持久化,因此下一个带有 session 的语句将抛出惰性异常。您可以在Xml映射中将lazy更改为false(lazy =“ false”),这是我正在使用的不好的做法,或者可以尝试使用Spring和Hibernatetemplate,这可能会解决问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文