Поиск объектов гибернации без отношения «многие ко многим»

#java #hibernate

#java #переход в спящий режим

Вопрос:

Мне нужно найти все продукты, которые не содержат определенного аллергена, используя Hibernate.

Вот SQL, используемый для создания таблиц базы данных:

 CREATE TABLE ALLERGEN (id integer IDENTITY PRIMARY KEY, name varchar(20), UNIQUE (id), UNIQUE(name));
CREATE TABLE PRODUCT (id integer IDENTITY PRIMARY KEY, name varchar(20), UNIQUE (id), UNIQUE(name));
CREATE TABLE PRODUCT_ALLERGEN (product_id integer, allergen_id integer, UNIQUE (product_id, allergen_id), FOREIGN KEY (product_id) REFERENCES PRODUCT (id), FOREIGN KEY (allergen_id) REFERENCES ALLERGEN (id));
  

Вот классы Java с аннотациями Hibernate:

 @Entity
@Table(name = "ALLERGEN")
class Allergen {

    @Id
    @Column(unique = true, nullable = false)
    @GeneratedValue
    private Integer id;
    private String name;

    // ...
}

@Entity
@Table(name = "PRODUCT")
public class Product {

    @Id
    @Column(unique = true, nullable = false)
    @GeneratedValue
    private Integer id;
    private String name;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(inverseJoinColumns = {@JoinColumn(name = "allergen_id")})
    private final Set<Allergen> allergens = new HashSet<>();

    // ...
}
  

Этот SQL, похоже, дает мне желаемый результат, но я не вижу, как представить его, используя критерии гибернации.

 SELECT * FROM PRODUCT WHERE (SELECT COUNT(*) FROM PRODUCT_ALLERGEN WHERE product_id = PRODUCT.id AND allergen_id = 0) = 0;
  

Комментарии:

1. Вам нужен запрос в HQL или с использованием Criterions API?

2. Я искал решение в Criterions API, но эквивалент в HQL тоже был бы интересен.

3. mkyong.com/hibernate/hibernate-query-examples-hql

Ответ №1:

С помощью Criteria API вы должны иметь возможность получить весь продукт без аллергенов, создав левое соединение от продукта к аллергену и проверив, равно ли оно нулю:

 final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Product> c = builder.createQuery(Product.class);
final Root<Product> root = c.from(Product.class);
Join<Product, Allergen> allergenJoin = root.join("allergens", JoinType.LEFT);
c.where(builder.isNull(allergenJoin));
c.select(root);
List<Product> = entityManager.createQuery(c).getResultList(); 
  

Примечание: Я не указал, откуда вы получаете EntityManager. Обычно я использую для этого инъекцию, но есть и другие методы, такие как использование фабрики.

Комментарии:

1. Понял, что этот ответ правильный, но что мой вопрос был неоднозначным. Я имел в виду «поиск продуктов, которые не содержат определенного аллергена». не «найти продукты, которые не содержат аллергенов»..

Ответ №2:

Этот код использует JPQL для получения продуктов без определенного аллергена.

 List<Product> results = manager.createQuery(
        "SELECT p from Product AS p WHERE (SELECT COUNT(a) FROM p.allergens a WHERE a.name = :an) = 0",
        Product.class)
    .setParameter("an", "nuts")
    .getResultList();