问题
I am trying to design a query using hibernate criteria API. The goal is to get a filtered list of items stored at 3rd level in form of java.util.Set which match a particular value. Further the list should be filtered at the outer most level for another specific value. Below are the model classes that form a multi level relationship:
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;
}
}
Now I need to get a specific Stock object containing Items with a particular id and that too for a Seller with a particular id. I could easily do this by getting the top level Seller object by id but that will put a burden on my code to manually filtering generating list of Stock with multiple for loops which definitely is not a good idea. Any suggestions and guidelines for mapping and/or criteria logic will be most appreciated. Thanks in advance!
回答1:
First step should be change the Store entity to suport toMany sellers, then I would begin writing a jpql query
SELECT * FROM stock s
JOIN stock.items si
JOIN stock.store.sellers sss
WHERE si.id IN(:itemIds)
AND sss.id IN(:sellerIds)
the second step would be rewrite this query on a criteria query manner
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));
I haven't test this query, so I'm not sure if it is going to work. However that's the idea.
UPDATE
For Hibernate Criteria Query applies a similar concept to Joins but in this case is creating a sub criteria:
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();
or using alias:
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();
Again I'm not sure if any of those queries are going to work.
And also as final advice, you should avoid using vendor specific tools. JPA criteria queries are the standard. It means that other vendors also use it. For example you could switch between Hibernate and EclipseLink without many problems if you use JPA criteria instead Hibernate Criteria, since both vendors should follow the standard (not 100%, but highly).
来源:https://stackoverflow.com/questions/29048663/how-to-create-hibernate-criteria-for-multiple-levels