我知道有很多关于此的问题,但没有一个解决方案帮助了我。
我正在使用PrimeFaces来构建一个惰性的loadind数据表。这意味着这个数据表列表是一个LazyDataModel列表,我必须开发一个LazyDataModel实现,其中我覆盖了load方法。所有这些都可以从PrimeFaces showcase中学习,并且在大多数情况下,当datable只使用数据库中的一个表时(这不是我的情况),它可以正常工作。
现在,我有两个实体:
@Entity
@Table(name="UNIVERSITY")
public class University {
@Id
@Column(name="ID_UNIVERSITY")
@GeneratedValue(strategy = GenerationType.AUTO ,generator="SQ_UNIVERSITY")
@SequenceGenerator(name="SQ_UNIVERSITY", sequenceName="SQ_UNIVERSITY")
private Long idUniversity;
@Column(name="NAME")
private String name;
@ManyToOne
@JoinColumn(name="ID_DIRECTOR")
private Director director;
@OneToMany(fetch=FetchType.EAGER, mappedBy = "university")
private Set<Students> students = new HashSet<Students>(0);
...
public int getStudentsQty(){
return this.students.size();
}
...
我将使用getStudentsQty()
方法填充数据表中的一列。这是学生实体:
@Entity
@Table(name="STUDENTS")
public class Students
{
@Id
@Column(name="ID_STUDENTS")
@GeneratedValue(strategy = GenerationType.AUTO ,generator="SQ_STUDENTS")
@SequenceGenerator(name="SQ_STUDENTS", sequenceName="SQ_STUDENTS")
private Long idStudent;
@ManyToOne
@JoinColumn(name="ID_UNIVERSITY")
private University student;
@Column(name="NAME")
private String name;
...
以下是我的加载实现将使用的搜索方法:
public List<University> find(int startingAt, int maxPerPage,
final String sortField, final SortOrder sortOrder, Map<String, String> filters) {
session = HibernateUtil.getSession();
Criteria criteria =session.createCriteria(University.class);
List<String> aliases = new ArrayList<String>();
if(maxPerPage > 0){
criteria.setMaxResults(maxPerPage);
}
criteria.setFirstResult(startingAt);
addFiltersToCriteria(filters, criteria, aliases);
Order order = Order.desc("name");
if(sortField != null && !sortField.isEmpty()){
if(sortField.contains(".")){
String first = (sortField.split("\\."))[0];
if(!aliases.contains(first)){
criteria.createAlias(first, first);
aliases.add(first);
}
}
if(sortOrder.equals(SortOrder.ASCENDING)){
order = Order.asc(sortField);
}
else if(sortOrder.equals(SortOrder.DESCENDING)){
order = Order.desc(sortField);
}
}
criteria.addOrder(order);
return (List<University>) criteria.list();
}
现在,我的问题。如果我使用FetchType.LAZY
,一切正常,但性能很差,所以我希望使用EAGER
。如果我使用EAGER
,结果将按预期重复并解释here。我尝试在equals()
实体中实施hashCode()
和University
方法,以使用最后一个链接中建议的LinkedHashSet
,但它不起作用我不知道如何。我还尝试将DISTINCT
与Criteria
一起使用,但由于我使用的addOrder
,它要求加入,因此无效。
所以,我发现this other suggestion完美无缺。基本上,解决方案是进行第二次Criteria
查询,仅搜索原始搜索中包含ID的大学。像这样:
private List<University> removeDuplicates(Order order,
List<University> universities) {
Criteria criteria;
List<University> distinct = null;
if(universities.size() > 0){
Set<Long> idlist = new HashSet<Long>();
for(University univ: universities){
idlist.add(univ.getIdUniversity());
}
criteria = session.createCriteria(University.class);
criteria.add(Restrictions.in("id", idlist)) ;
distinct = (List<University>) criteria.list();
return distinct;
}
else{
return universities;
}
}
因此,它将为我的懒惰装载分页数据表带来前100行。在第一次Criteria
搜索中,它们将针对第一页进行排序,并且在我的第二次Criteria
搜索后将显示相同的100个正确行,但现在它们将未排序 。它是第一页的正确行,但在第一页内未排序。我不能在第二个Criteria
中使用“addOder”,否则它们会重复出现。
最奇怪的是:如果我尝试用Collections.sort
对结果进行排序,结果将会重复!怎么样??我怎么能订购我的结果?
谢谢!
编辑:学生数只是一个例子,我需要在其他情况下获取每个相关实体内的信息。
答案 0 :(得分:1)
如果我理解正确,您输出的是列出大学的表格,并且您希望显示每所大学的学生人数。如果是这样,将x000学生记录加载到内存中只是为了获得计数是疯狂的(无论你是热切地还是懒惰地进行)。
请尝试以下方法之一:
<强>一强>
而不是加载相关学生来获取计数使用Hibernates @Formula功能并向您的大学实体添加派生属性。
@Formula(value = "select count(*) from students where university_id = ?")
private int studentCount;
<强>两个强>
创建一个数据库视图,例如university_summary_data,其中包含此计数并创建一个映射到此视图的实体(就像表一样工作)然后在大学中:
@OneToOne
private UniversitySummaryData data;
<强>三强>
查看Hibernate的@LazyCollection(LazyCollectionOption.EXTRA),它允许您在不加载所有学生的情况下调用映射集合上的size()。
所有多更简单的解决方案,即您所拥有的。
答案 1 :(得分:0)
你说你想切换到EAGER加载,因为延迟加载的性能很糟糕,但是使用EAGER加载你的性能会是一样的。您仍然可以解决选择n + 1问题,例如here和here解决方案。
为了提高性能,您需要修改Hibernate将生成的查询。通常我在Hibernate中做一个左外连接来获得它,例如
sessionFactory.getCurrentSession().createCriteria(University.class)
.createAlias("students", "students_alias", JoinType.LEFT_OUTER_JOIN)
.list();
最好保持Hibernate默认的延迟加载。