Я знаю, что есть много вопросов по этому поводу, но ни одно из решений не помогло мне.
Я использую PrimeFaces для создания таблицы данных с ленивой загрузкой. Это означает, что этот список данных является списком LazyDataModel, и мне пришлось разработать реализацию LazyDataModel, в которой я переопределил метод загрузки. Все это можно узнать из демонстрации PrimeFaces, и это прекрасно работает для большинства случаев, когда dataable использует только одну таблицу из базы данных (что не мой случай).
Теперь у меня есть две сущности:
@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
, результаты будут дублироваться, как и ожидалось, и объясняться здесь а>. Я попытался реализовать методы equals()
и hashCode()
в объекте University
, чтобы использовать LinkedHashSet
, как было предложено в последней ссылке, но это не сработало, я не знаю, как это сделать. Я также пытался использовать DISTINCT
с Criteria
, но это не работает из-за addOrder
, который я использую, где он запрашивает соединения.
Итак, я нашел другое предложение, которое работал отлично. В основном решение состоит в том, чтобы выполнить второй запрос Criteria
, ища только университеты с идентификатором, включенным в исходный поиск. Как это:
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
поиске они будут отсортированы для первой страницы, и те же 100 правильных строк будут присутствовать после моего второго Criteria
поиска, но теперь они будут не отсортированы. Это правильные строки для первой страницы, но не отсортированные внутри первой страницы. Я не могу использовать «addOder» во втором Criteria
, иначе они будут дублироваться.
И самое странное: если я попытаюсь отсортировать результаты с помощью Collections.sort
, результаты будут продублированы!!! Как?? Как я могу заказать свой результат в конце концов?
Спасибо!!
EDIT: количество студентов - это просто пример, мне нужно в других сценариях получить информацию внутри каждого связанного объекта.