如何为多个级别创建Hibernate Criteria

时间:2015-03-14 12:00:14

标签: java hibernate hibernate-criteria

我正在尝试使用hibernate criteria API设计查询。目标是以java.util.Set的形式获得存储在第3级的项目的筛选列表,该列表匹配特定值。此外,列表应在最外层级过滤另一个特定值。以下是形成多层次关系的模型类:

Seller.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;
    }
}

现在我需要获取一个特定的股票对象,其中包含具有特定ID的商品,对于具有特定ID的卖家也是如此。我可以通过ID获取顶级卖方对象来轻松实现这一点,但这会给我的代码带来负担,以便手动过滤带有多个for循环的 Stock 生成列表不是个好主意。任何有关映射和/或标准逻辑的建议和指南都将非常受欢迎。 提前谢谢!

1 个答案:

答案 0 :(得分:2)

第一步应该是将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标准查询是标准。这意味着其他供应商也使用它。例如,如果您使用JPA标准而不是Hibernate Criteria,则可以在Hibernate和EclipseLink之间切换而不会出现很多问题,因为两个供应商都应该遵循标准(不是100%,而是高度)。