如何有条件地加载映射到对象中的集合?

时间:2012-02-23 02:34:13

标签: java hibernate hibernate-4.x

给出以下映射

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

5 个答案:

答案 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())对小猫集合有类似的效果。

阅读API here或示例here

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

然后我们可以使用这些方法来确定我们是否可以安全地访问该集合。

无论如何,希望这有助于某人。