Hibernate 延迟加载集合和 PropertyChangeSupport
我有一个延迟初始化 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对我有用的是立即将集合转变为另一种类型的集合:
我们只能使用旧版本的 Hibernate。所以我的猜测是 Hibernate 代码中有错误。通过立即构造另一个 java Collection,它将对象复制到(非易失性)物理内存,而不是 hibernate 缓存。
因此,最初的问题将解决为:
希望这有帮助。
What worked for me was to immediately turn the set into another type of Collection:
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:
Hope this helps.
当您设置 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.