延迟加载数据表显示FetchType.EAGER的重复结果

时间:2013-11-12 17:55:14

标签: java hibernate primefaces lazy-loading criteria

我知道有很多关于此的问题,但没有一个解决方案帮助了我。

我正在使用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,但它不起作用我不知道如何。我还尝试将DISTINCTCriteria一起使用,但由于我使用的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对结果进行排序,结果将会重复!怎么样??我怎么能订购我的结果?

谢谢!

编辑:学生数只是一个例子,我需要在其他情况下获取每个相关实体内的信息。

2 个答案:

答案 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问题,例如herehere解决方案。

为了提高性能,您需要修改Hibernate将生成的查询。通常我在Hibernate中做一个左外连接来获得它,例如

sessionFactory.getCurrentSession().createCriteria(University.class)
.createAlias("students", "students_alias", JoinType.LEFT_OUTER_JOIN)
.list();

最好保持Hibernate默认的延迟加载。