如何使用具有ManyToMany关联的嵌套对象在Hibernate中正确分页?

时间:2012-11-12 05:29:54

标签: java hibernate jersey jax-rs

好的,我有以下(缩写)3实体和HibernateUtil类。

public class Tag {
    @Id
    BigDecimal id;

    String tag

    @ManyToMany( mappedBy="tags" )
    List<Label> labels;
}

public class Label {
    @Id
    BigDecimal id;

    String label;

    @ManyToMany( targetEntity=Tag.class )
    List<Tag> tags;
}

public class Data {
    @Id
    BigDecimal id;

    BigDecimal data;

    @ManyToOne
    Label label;
}


public class HibernateUtil {

    public static List pagedQuery(DetachedCriteria detachedCriteria, Integer start, Integer size) throws WebApplicationException {
        Session session = getSession();
        try {
            Transaction transaction = session.beginTransaction();

            List records = detachedCriteria.getExecutableCriteria(session)
                    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                    .setFirstResult(start)
                    .setMaxResults(size)
                    .list();

            transaction.commit();

            return records;
        } catch (Exception e) {
        // Place Logger here...
            throw new WebApplicationException(e);
        } finally {
            session.close();
        }
    }

}

我遇到的问题是,当我尝试使用HibernateUtil.pagedQuery(detatchedCriteria,start,size)查询Data类时,我的结果列表与size参数不匹配。我发现其原因是hibernate构建查询以包含标记(Data.Label.Tags)的方式。

例如,当Label有多个关联的Tags时,完整的分页查询中使用的Data对象子查询的结果列表将如下所示(我通过解析sql Hibernate吐出到控制台找到了这个)< / p>

  1. 数据-1;标签:标签-1
  2. 数据-1;标签;标签-2
  3. 数据-2;标签;标签-1
  4. 数据-2;标签;标签-2
  5. 等...
  6. 如果我用size = 3调用它,那么返回的结果集将是

    1. 数据-1;标签:标签-1
    2. 数据-1;标签;标签-2
    3. 数据-2;标签;标签-1
    4. 然而,Hibernate会将前两行组合在一起(因为它们是相同的Data对象),而我返回的List对象的大小为2(Data-1&amp; Data-2)

      我尝试使用我通过Google找到的Projection方法替换setResultTransformer方法,但之后只返回了Data对象的id。

      有人对我有任何建议吗?我不知道从哪里开始......

1 个答案:

答案 0 :(得分:2)

你正面临着一个用hibernate分页的常见问题。 resultTransformer应用于“Java”端,因此已经在DB端进行了分页。

最简单的(可能不是最优化的)是做两个查询,一个是投影和分页(就像你已经做过的那个),另一个是使用投影id。这是一个例子:

//get the projection    
Criteria criteria = factory.getCurrentSession().createCriteria(getEntityClass());
    criteria.setProjection(Projections.distinct((Projections.projectionList().add(Projections.id()).add(Projections.property("name")))));
//paginate the results
    criteria.setMaxResults(pageSize);
    criteria.setFirstResult(first);

List<Object[]> idList = criteria.list();
//get the id's from the projection
        List<Long> longList = new ArrayList<Long>();
        for (Object[] long1 : idList) {
            Object[] record = long1;
            longList.add((Long) record[0]);
        }

if (longList.size() > 0) {
//get all the id's corresponding to the projection, 
//then apply distinct root entity
            criteria = factory.getCurrentSession().createCriteria(getEntityClass());
            criteria.add(Restrictions.in("id", longList));
            criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        } else {
//no results, so let's ommit the second query to the DB
            return new ArrayList<E>();
        }

return criteria.list();