给出以下映射
<class name="com.domain.Season" table="cm.pub.jsn_mstr">
<id name="seasonCode" column="season_code" length="1"/>
<property name="name" type="string" column="name" length="20"/>
<set name="promotions" lazy="false">
<key column="season_code"/>
<one-to-many class="com.domain.Promotion" not-found="ignore"/>
</set>
</class>
如何包含或排除promotions
的加载?我可以使用lazy="true"
虽然我使用Jackson来序列化会话结束后的结果。
public Collection<Season> getSeasons(boolean withPromotions) {
final Session session = sessionFactory.getCurrentSession();
try {
session.beginTransaction();
return (List<Season>) session.createQuery("from Season s").list();
} finally {
session.getTransaction().commit();
}
}
更新:使用延迟加载时出现问题。
上面的getSeasons
方法用于将检索季节的MVC控制器,然后使用jackson将它们序列化为JSON(使用Spring / MVC的视图解析器),因此我实际上并不自己访问这些对象,因此任何延迟加载集合的尝试都会导致异常(因为jackson将在所有集合属性上调用迭代器)。
这是一个显示异常将被抛出的示例:
public Collection<Season> getSeasons(boolean withPromotions) {
final Session session = sessionFactory.getCurrentSession();
final List<Season> r;
try {
session.beginTransaction();
r = (List<Season>) session.createQuery(
withPromotions
? "from Season s join fetch s.promotions"
: "from Season s"
).list();
} finally {
session.getTransaction().commit();
}
try {
for (Season s : r) {
for (Promotion p : s.getPromotions()) {
// Exception thrown here as we attempted to get an iterator.
LOG.debug("Promotion: " + p.getName());
}
}
} catch (Exception ex) {
LOG.error("Couldn't get promotions", ex);
}
return r;
}
当然,这次映射需要lazy="true"
,否则它总是急于阅读集合。
<class name="com.domain.Season" table="cm.pub.jsn_mstr">
<id name="seasonCode" column="jsn_seas" length="1"/>
<set name="promotions" lazy="true">
<key column="jpr_seas"/>
<one-to-many class="com.domain.Promotion" not-found="ignore"/>
</set>
</class>
promotions
字段的数据类型为Collection<Promotion>
。
答案 0 :(得分:3)
试试这个:
session.createQuery("from Season s join fetch s.promotions").list();
来自hibernate参考: “fetch”连接允许使用单个select来初始化值的关联或集合。
答案 1 :(得分:2)
您可以使用Hibernate.initialize()
初始化代理中的集合。在您的示例中,添加以下代码。
public Collection<Season> getSeasons(boolean withPromotions) {
final Session session = sessionFactory.getCurrentSession();
try {
session.beginTransaction();
List<Season> list = session.createQuery("from Season s").list();
for(Season s : list){
if(condition){
Hibernate.initialize(s.getPromotions());
}
}
return list;
} finally {
session.getTransaction().commit();
}
}
从API文档开始,
静态方法Hibernate.initialize()和Hibernate.isInitialized()为应用程序提供了一种使用延迟初始化集合或代理的便捷方式。只要其Session仍处于打开状态,Hibernate.initialize(cat)将强制执行代理cat的初始化。 Hibernate.initialize(cat.getKittens())对小猫集合有类似的效果。
答案 2 :(得分:0)
我发现我能做到这一点的唯一方法就是不映射集合,而是发送两个查询,然后自己添加到集合中。
这使我能够更好地控制关联并提高性能,因为我只发送两个查询而不是1 + n(其中n是从第一个查询中检索到的行)。
答案 3 :(得分:0)
基本上,你的问题是杰克逊 - 读取豆子里的所有东西。不是Hibernate。
或者:
1)正确控制杰克逊,传输您实际需要的数据 - 并避免您不希望它“拉上”。
2)使用Spring的'OpenSessionInViewFilter'或类似的方法将会话绑定到线程本地,并使其保持打开状态直到视图运行 - 使Jackson仍然“拉上”&amp;在视图呈现阶段加载集合。
3)或者,将两个不同的类(一个完整的细节,一个轻量级)映射到同一个表,并检索您实际需要的那个。
SeasonLite可能是Season的祖先(完整细节),但请注意,你没有使用Hibernate映射继承 - 你正在映射同一个表的两个独立视图。
PS:像'getSomeData(boolean retrieveThisData)'这样的方法似乎是一种常见的反模式。我以前见过很多次。
答案 4 :(得分:0)
对不起,这是一个非常晚的答案,但我只是在寻找其他东西时才发现你的问题。
正如一些答案中所述,问题是Hibernate使用'占位符'PersistentCollection
来进行延迟加载集合。要阻止Hibernate在访问集合时尝试填充集合,您可以null
出现查询返回的每个Promotions
实体中的实际Season
列表。在代码中,像这样:
if (!withPromotions) {
for (Season s : r) {
s.setPromotions(null);
}
}
将Promotions
设置为null将清除对Hibernate集合对象的引用。你应该能够在会议之外这样做。
我们做的是在我们的实体上为每个与此类似的集合创建方法:
public boolean isPromotionsLoaded() {
if (((promotions instanceof org.hibernate.collection.PersistentCollection)
&& !((org.hibernate.collection.PersistentCollection) promotions).wasInitialized())
|| (getPromotions() == null)) {
return false;
} else {
return true;
}
}
然后我们可以使用这些方法来确定我们是否可以安全地访问该集合。
无论如何,希望这有助于某人。