【问题标题】:Hibernate Criteria n+1 issue with maxresults休眠条件 n+1 问题与 maxresults
【发布时间】:2011-07-30 21:05:53
【问题描述】:

使用 hibernate ctiteria 我想选择一个对象,它与 oneToMany 对象列表相关联。我想通过这个列表分页避免可怕的休眠 n+1 选择问题

这是一个可行的解决方案,它需要 11 次访问数据库以获取 10 个父对象。

Criteria criteria = this.getSession().createCriteria(Mother.class);
criteria.addOrder(Order.asc("title"))
.setMaxResults(details.getMaxRows())
.setFirstResult(details.getStartResult())
.setFetchMode("kittens", FetchMode.SELECT);
List test = criteria.list();

这是一个解决方案,它只执行一个 sql 语句(欢呼)但无法处理分页,即父对象 Mother 上的 setMaxResults 和 setFirstResult 不正确(嘘)

Criteria criteria = this.getSession().createCriteria(Mother.class);
criteria.addOrder(Order.asc("title"))
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.setMaxResults(details.getMaxRows())
.setFirstResult(details.getStartResult())
.setFetchMode("kittens", FetchMode.JOIN);
List test = criteria.list();

这似乎是一个常见的要求,但我一直在寻找一个没有运气的解决方案。

有接受者吗?

【问题讨论】:

    标签: hibernate criteria


    【解决方案1】:

    将其减少到 1 个查询很困难(即我不知道可移植的解决方案),但将其减少到 2 个查询(与 n 无关)非常简单:

    Criteria criteria = this.getSession().createCriteria(Mother.class);
    criteria.addOrder(Order.asc("title"))
        .setMaxResults(details.getMaxRows())
        .setFirstResult(details.getStartResult())
        .setProjection(Projections.id());
    List<?> ids = criteria.list();
    
    criteria = getSession().createCriteria(Mother.class)
        .add(Restrictions.in("id", ids))
        .setFetchMode("children", FetchMode.JOIN)
        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
    
    return criteria.list();
    

    对于某些数据库,子选择获取 children 也可能有效。

    【讨论】:

    • 很好的答案。感谢和尊重。
    • 我希望我能多次对此表示赞同。巧妙的解决方法。
    • 伟大的安德威;在我的情况下,由于一对多加入,我得到了重复的 ID,所以我将第 5 行的投影替换为 .setProjection(Projections.distinct(Projections.id()))
    【解决方案2】:

    据我所知,除了以下使用原生 SQL 查询的技巧(确切的 SQL 语法取决于您的 DBMS)之外,没有什么好的方法可以解决这个问题:

    List<Mother> result = s.createSQLQuery(
        "select {m.*}, {k.*} " +
        "from (select limit :firstResult :maxResults * from Mother m) m " +
        "left join Kitten k on k.motherId = m.id"
        )
        .addEntity("m", Mother.class)
        .addJoin("k", "m.kittens")
        .setParameter("firstResult", ...)
        .setParameter("maxResults", ...)
        .setResultTransformer(MyDistrictRootEntityResultTransformer.INSTANCE)
        .list();
    
    ...
    
    // Unfortunately built-in DistrictRootEntityResultTransformer cannot be used
    // here, since it assumes that root entity is the last in the tuple, whereas
    // with addEntity()/addJoin() it's the first in the tuple
    public class MyDistrictRootEntityResultTransformer implements ResultTransformer {
        public static final MyDistrictRootEntityResultTransformer INSTANCE = new MyDistrictRootEntityResultTransformer();
    
        public Object transformTuple(Object[] tuple, String[] aliases) {
            return tuple[0];
        }
    
        public List transformList(List collection) {
            return DistinctResultTransformer.INSTANCE.transformList(collection);
        }
    }
    

    【讨论】:

    • 这是更好的解决方案。只有一个 SQL 调用。
    猜你喜欢
    • 2011-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多