Hibernate Criteria Many to Many. Выбрать коллекцию, в которой есть все элементы из другой коллекции

 
 
 
Сообщения:10
Добрый день! Есть две сущности с отношением Many to Many:
@Entity
@Table(name = "items")
public class Item implements Comparable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "item_id")
    private Integer itemId;
   ....
 @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
    @JoinTable(name = "items_criteria",
            joinColumns = @JoinColumn(name = "item_id"),
            inverseJoinColumns = @JoinColumn(name = "filter_criterion_id"))
    private List<FilterCriterion> filterCriteria;
   ....
}

и
@Entity
@Table(name = "filter_criteria")
public class FilterCriterion {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "filter_criterion_id")
    private Integer filterCriterionId;
   ....
    @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
    @JoinTable(name = "items_criteria",
            joinColumns = @JoinColumn(name = "filter_criterion_id"),
            inverseJoinColumns = @JoinColumn(name = "item_id"))
    private List<Item> items;
   ....

В классе ItemDao нужно реализовать метод, который будет принимать в качестве аргумента коллекцию FilterCriterion и возвращать коллекцию Item, такую, чтобы у каждого Item в этой коллекции, коллекция FilterCriterion содержала все элементы коллекции, данной в качестве аргумента. При такой реализации:
 @Override
    public List<Item> getItems(List<FilterCriterion> currentFilterCriteria) {
        
        Criteria criteria = ht.getSessionFactory().getCurrentSession().createCriteria(Item.class);
        List<Integer>currentFilterCriteriaId = new ArrayList<Integer>();
        for(FilterCriterion criterion : currentFilterCriteria){
            currentFilterCriteriaId.add(criterion.getFilterCriterionId());
        }
        if(!currentFilterCriteriaId.isEmpty()){
            criteria.createAlias("filterCriteria", "f");
            criteria.add(Restrictions.in("f.filterCriterionId", currentFilterCriteriaId));
        }
        return criteria.list();
    }

в результат попадают все Item, у которых хотя бы один FilterCriterion имеется в коллекции, данной в качестве аргумента. Если
 criteria.add(Restrictions.in("f.filterCriterionId", currentFilterCriteriaId));

заменить на
for(Integer currentId : currentFilterCriteriaId){
                criteria.add(Restrictions.eq("f.filterCriterionId", currentId));
            }

результат все равно неверный. Как правильно сформировать criteria, чтобы в результате получить только те Item, коллекции FilterCriterion которых содержат все элементы данной в качестве аргумента коллекции FilterCriterion?
Изменен:10 янв 2014 06:13
 
 
Сообщения:2
Есть какое-либо решение данного вопроса? Облазил весь интернет... Для одного параметра "criteria.createAlias" работает нормально, а как сделать запрос, если параметров от 2?
Изменен:18 июл 2016 05:11
 
 
Сообщения:153
Целый день ломал голову над данным вопросом, правда использовал JPQL (но думаю, что его можно переделать в Criteria API).
Делал на своём примере: есть сущности Book и Author. Между ними связь многие ко многим (посредством связующей таблицы Authors_Books).
Код:
    @Override
    public List<Book> getBooksByAuthors(List<Author> authors)
    {
        TypedQuery<Book> query = entityManager.createQuery("select b from Book b join b.authors ba" +
                                                           " where size(b.authors) = :authors_count" +
                                                           " and ba in (:authors)" +
                                                           " group by b having count (b) = :authors_count"
                , Book.class);

        query.setParameter("authors_count", authors.size());
        query.setParameter("authors", authors);

        return query.getResultList();
    }

Запрос возвращает результат, если только передаваемый список авторов в точности совпадает со списком авторов конкретной книги.

P/S: думаю, что можно как-то этот запрос упростить...
 
 
Сообщения:153
Тоже самое, но с использованием JPA Criteria API:
@Override
public List<Book> getBooksByAuthors(List<Author> authors)
{
   CriteriaBuilder builder = entityManager.getCriteriaBuilder();
   CriteriaQuery<Book> query = builder.createQuery(Book.class);

   Root<Book> rootBook = query.from(Book.class);
   ListJoin<Book, Author> joinBookAuthors = rootBook.join(Book_.authors);

   Expression<List<Author>> bookAuthors = rootBook.get(Book_.authors);
   Expression<Integer> countOfBookAuthors = builder.size(bookAuthors);
   Expression<Long> countOfBooksInGroup = builder.count(rootBook);

   Predicate predicateCountOfBookAuthorsEqualsInputListSize = builder.equal(countOfBookAuthors, authors.size());
   Predicate predicateBookAuthorsInInputList = joinBookAuthors.in(authors);


   query.where(builder.and(predicateCountOfBookAuthorsEqualsInputListSize, predicateBookAuthorsInInputList))
           .groupBy(rootBook)
           .having(builder.equal(countOfBooksInGroup, authors.size()));

   return entityManager.createQuery(query).getResultList();
}


Код сущностей:
@Entity
public class Author
{
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   @NotNull
   @Size(min = 3, max = 30)
   @Column(length = 30, nullable = false)
   private String name;
   @ManyToMany(mappedBy = "authors")
   @OrderBy("title ASC")
   private List<Book> books = new ArrayList<>();
}

@Entity
public class Book
{
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   @NotNull
   @Column(nullable = false, updatable = false)
   private String title;
   private Float price;
   @Size(min = 5, max = 200)
   private String description;
   private String isbn;
   @Column(nullable = false)
   private Integer nbOfPage;
   private Boolean illustration;
   @ElementCollection(fetch = FetchType.LAZY)
   @CollectionTable(name = "tag")
   @Column(name = "value")
   private List<String> tags;
   @ManyToMany(cascade = {CascadeType.PERSIST})
   @JoinTable(name = "authors_books",
                    joinColumns = @JoinColumn(name = "book_id"),
                    inverseJoinColumns = @JoinColumn(name = "author_id"))
   private List<Author> authors = new ArrayList<>();
}
 
 
Сообщения:2
VPS, спасибо большое за ответы, есть над чем поразмыслить. Вот бы для Hibernate Criteria API увидеть пример...
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет