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



好的,所以我有以下(缩写)3个实体和Hibernate Util类。

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();
}
}
}

我遇到的问题是,当我尝试使用Hibernate Util.pagedQuery(detatchedCriteria,start,size)查询Data类时,我的结果列表与size参数不匹配。我发现这是hibernate构建查询以包含标签(Data.Label.tags)的方式造成的

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

  1. 数据-1;标签:标签-1
  2. 数据-1;标签Tag-2
  3. 数据-2;标签标签-1
  4. 数据-2;标签Tag-2
  5. 等等

如果我用size=3调用它,那么返回的结果集将是

  1. 数据-1;标签:标签-1
  2. 数据-1;标签Tag-2
  3. 数据-2;标签标签-1

然而,Hibernate会将前两行分组在一起(因为它们是同一个Data对象),并且我返回的List对象的大小为2(Data-1&Data-2)

我试图用我在谷歌上找到的Projection方法替换setResultTransformer方法,但这只返回了Data对象的id。

有人给我什么建议吗?我不知道从这里到哪里去。。。

您正面临一个使用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();

最新更新