Как создать критерии гибернации для нескольких уровней

Я пытаюсь создать запрос с использованием API критериев гибернации. Цель состоит в том, чтобы получить отфильтрованный список элементов, хранящихся на 3-м уровне, в форме java.util.Set, которые соответствуют определенному значению. Далее список должен быть отфильтрован на самом внешнем уровне для другого конкретного значения. Ниже приведены классы моделей, которые образуют многоуровневые отношения:

Продавец.java

@Entity
@Table(name="seller")
public class Seller {
    private Integer id;
    private Set<Store> stores;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    public Integer getId(){
        return id;
    }
    public void setId(Integer id){
        this.id = id;
    }

    @ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinTable(name="seller_store",
        joinColumns={@JoinColumn(name="sellertid")},
        inverseJoinColumns={@JoinColumn(name="storeid", referencedColumnName="id")})
    @ForeignKey(name="fk_sellerid_seller_store", inverseName="fk_storeid_seller_store")
    public Set<Store> getStores() {
        return stores;
    }
    public void setStores(Set<Store> stores) {
        this.stores = stores;
    }
}

Store.java

@Entity
@Table(name="store")
public class Store {
    private Integer id;
    private Stock stock;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    public Integer getId(){
        return id;
    }
    public void setId(Integer id){
        this.id = id;
    }

    @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER, orphanRemoval=true)
    @JoinColumn(name="stockid", referencedColumnName="id")
    @ForeignKey(name="fk_stockid_store")
    public Stock getStock() {
        return stock;
    }
    public void setStock(Stock stock) {
        this.stock = stock;
    }
}

Stock.java

@Entity
@Table(name="stock")
public class Stock {
    private Integer id;
    private Set<Item> stockItems;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    public Integer getId(){
        return id;
    }
    public void setId(Integer id){
        this.id = id;
    }

    @ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinTable(name="stock_stockitem",
            joinColumns={@JoinColumn(name="stockid")},
            inverseJoinColumns={@JoinColumn(name="stockitemid")})
    @ForeignKey(name="fk_stockid_stock", inverseName="fk_stockitemid_stock")
    public Set<Item> getStockItems() {
        return stockItems;
    }
    public void setStockItems(Set<Item> stockItems) {
        this.stockItems = stockItems;
    }

}

Item.java

@Entity
@Table(name="item")
public class Item {
    private Integer id;
    private String name;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    public Integer getId(){
        return id;
    }
    public void setId(Integer id){
        this.id = id;
    }

    @Column(name="name", unique=true)
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}

Теперь мне нужно получить конкретный объект Stock, содержащий Item с определенным идентификатором, а также для продавца с определенным идентификатором. Я мог бы легко сделать это, получив объект Продавец верхнего уровня по идентификатору, но это возложит на мой код бремя ручной фильтрации, генерирующего список Акции с несколькими циклами for, которые определенно не лучшая идея. Будем очень признательны за любые предложения и рекомендации по отображению и / или логике критериев. Заранее спасибо!


person Sachin    schedule 14.03.2015    source источник
comment
Может ли кто-нибудь помочь в этом, пожалуйста?   -  person Sachin    schedule 19.03.2015


Ответы (1)


Первым шагом должно быть изменение сущности Store для поддержки многих продавцов, затем я бы начал писать запрос jpql.

SELECT * FROM stock s 
JOIN stock.items si 
JOIN stock.store.sellers sss
WHERE si.id IN(:itemIds) 
AND sss.id IN(:sellerIds)

второй шаг - переписать этот запрос в соответствии с критериями запроса.

CriteriaQuery<Stock> cq = cb.createQuery(Stock.class);
Metamodel m = em.getMetamodel();
EntityType<Stock> Stock_ = m.entity(Stock.class);
Root<Stock> stock = cq.from(Stock.class);
Join<Stock, Item> items = stock.join(Stock_.items);
Join<Stock, Store> store = stock.join(Stock_.store);
Join<Store, Seller> sellers = store.join(Store_.sellers);
cq.where(cb.in(items.get(Item_.id), Arrays.asList(1, 2, 3, 4))
    .and(cb.in(sellers.get(Seller_.id), Arrays.asList(1, 2, 3, 4));

Я не тестировал этот запрос, поэтому не уверен, что он сработает. Однако это идея.

ОБНОВЛЕНИЕ

Для Hibernate Criteria Query применяется аналогичная концепция для Joins, но в этом случае создается подкритерий:

List stocks = sess.createCriteria(Stock.class)
  .createCriteria("items")
    .add( Restrictions.in("id", {1, 2, 3, 4, 5}) )
  .createCriteria("store")
    .createCriteria("sellers")
      .add( Restrictions.like("id", {1, 2, 3, 4, 5}) )
  .list();

или используя псевдоним:

List stocks = sess.createCriteria(Cat.class)
  .createAlias("items", "si")
  .createAlias("store.sellers", "sss")
    .add( Restrictions.in("si.id", {1, 2, 3, 4, 5}) )
    .add( Restrictions.in("sss.id", {1, 2, 3, 4, 5}) )
  .list();

Опять же, я не уверен, сработает ли какой-либо из этих запросов.

И также в качестве последнего совета: вам следует избегать использования инструментов, специфичных для производителя. Запросы критериев JPA являются стандартными. Это означает, что его используют и другие производители. Например, вы можете без проблем переключаться между Hibernate и EclipseLink, если вы используете критерии JPA вместо Hibernate Criteria, поскольку оба поставщика должны следовать стандарту (не на 100%, но в высокой степени).

person Luis Vargas    schedule 23.03.2015
comment
Спасибо за ответ и ответ @bigluis; Я понимаю, что при присоединении смогу получить накопленный набор Stock; Но я не понимаю, что у Магазина может быть отношение Many (OneToMany или ManyToMany?) К Продавцу. Как видите, я настроил для Продавца ManyToMany с отдельной таблицей, исходя из того факта, что у Продавца может быть более 1 "Магазина". Более того, многословный «запрос критериев», который я написал в заголовке своего вопроса, вероятно, вызвал путаницу. Я хочу сделать это с помощью Hibernate Criteria API, а не JPA Crtieria API. - person Sachin; 24.03.2015
comment
посмотрите на это: en.wikibooks.org/wiki/Java_Persistence/ManyToMany. Там вы можете видеть, что оба объекта привязаны к ManyToMany. Я имею в виду, что вы также должны аннотировать Store.sellers с помощью @ManyToMany. - person Luis Vargas; 24.03.2015
comment
вы должны пометить Store.sellers с помощью @ManyToMany (mappedBy = sellers). Это сообщает спящему режиму, что Store.sellers будет получен из таблицы продавца. Вы не должны аннотировать Store.sellers с помощью @JoinTable, поскольку вы уже аннотировали Seller.stores. - person Luis Vargas; 24.03.2015
comment
Ok. Я последую вашему совету и постараюсь сделать это способом JPA. :) - person Sachin; 24.03.2015
comment
Большое спасибо за то, что вы переписали аромат критериев Hibernate. - person Sachin; 24.03.2015