我有2个实体Product和Review和1个界面ReviewItem
@Entity
public class Product
{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private long productid;
private String productname;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "reviewedProduct")
private Set<Review> productReviews;
... constructor, getters and setters
}
@Entity
public class Review
{
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private long reviewid;
Integer stars;
@ManyToOne
@JoinColumn(name = "productid")
private Product reviewedProduct;
... constructor, getters and setters
}
public interface ReviewItem
{
long getProductid();
String getProductname();
Double getAvgRating();
Integer getReviewCount();
}
简而言之,我试图编写一个条件查询,如下所示:
SELECT p.productid, p.productname, AVG(r.stars) AS avgRating,
COUNT(r.productid) AS reviewCount
FROM products p
LEFT JOIN reviews r ON p.productid=r.productid
WHERE p.productname LIKE "%bean%" AND p.productname LIKE "%lemon%"
GROUP BY p.productid, p.productname
ORDER BY avgRating DESC
但是,LIKE搜索项是动态的,因此我必须使用Criteria Builder。我无需连接即可编写条件查询:
public List<Product> dynamicQueryWithStringsLike(Set<String> searchSet
{
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> query = cb.createQuery(Product.class);
Root<Product> product = query.from(Product.class);
Path<String> productPath = product.get("productname");
List<Predicate> predicates = new ArrayList<>();
for (String word : searchSet)
{
word = "%" + word + "%";
predicates.add(cb.like(productPath, word));
}
query.select(product)
.where(cb.and(predicates
.toArray(new Predicate[predicates.size()])));
return entityManager.createQuery(query)
.getResultList();
}
但是我不太确定如何使用LEFT JOIN 并将其返回包装器。 到目前为止,我有:
public List<ReviewItem> testCriteria(Set<String> searchSet)
{
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ReviewItem> query = cb.createQuery(ReviewItem.class);
Root<Product> productRoot = query.from(Product.class);
Root<Review> reviewRoot = query.from(Review.class);
Path<String> productPath = productRoot.get("productname");
List<Predicate> predicates = new ArrayList<>();
for (String word : searchSet)
{
word = "%" + word + "%";
predicates.add(cb.like(productPath, word));
}
Join<Product, Review> productReviewJoin = productRoot
.join("productid", JoinType.LEFT);
query
.multiselect(
productRoot.get("productid"),
productRoot.get("productname"),
cb.avg(reviewRoot.get("stars")),
cb.count(reviewRoot.get("productid"))
)
.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
return entityManager.createQuery(query)
.getResultList();
}
我当前遇到的错误是“无法加入基本类型的属性” 但我确定我有多个错误。我一直在阅读文档,但似乎找不到使用返回包装器的Join的示例。
答案 0 :(得分:0)
按所有非聚合返回字段分组
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ReviewItem> query = cb.createQuery(ReviewItem.class);
Root<Product> productRoot = query.from(Product.class);
...
Join<Product, Review> reviewJoin = productRoot.join("productReviews", JoinType.LEFT);
query.select(cb.construct(ReviewItem.class,
productRoot.get("productid"),
productRoot.get("productname"),
cb.avg(reviewJoin.get("stars")),
cb.countDistinct(reviewJoin)
))
.where(cb.and(predicates.toArray(new Predicate[predicates.size()])))
.groupBy(productRoot.get("productid"), productRoot.get("productname"))
.orderBy(cb.desc(cb.avg(reviewJoin.get("stars"))));
List<ReviewItem> x = entityManager.createQuery(query).getResultList();
ReviewItem:
public class ReviewItem {
Long getProductid;
String getProductname;
Double getAvgRating;
Long getReviewCount;
public ReviewItem(Long getProductid, String getProductname, Double getAvgRating, Long getReviewCount) {
this.getProductid = getProductid;
this.getProductname = getProductname;
this.getAvgRating = getAvgRating;
this.getReviewCount = getReviewCount;
}