使用左连接、自定义包装器和动态搜索词的条件查询
Criteria Query Using Left Join, Custom Wrapper, and Dynamic search terms
我有 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 和 return 使用 Wrapper。
到目前为止我有:
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();
}
我目前的错误是"Cannot join to attribute of basic type"
但我确定我有不止 1 个错误。我一直在阅读文档,但似乎找不到使用 return 包装器的 Join 的示例。
- 使用 cb.construct() 进行基于构造函数的查询
- 使用字段名称 productReviews 加入评论
- 而不是 reviewRoot 使用 reviewJoin
按所有非聚合返回字段分组
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();
评论项目:
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;
}
我有 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 和 return 使用 Wrapper。 到目前为止我有:
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();
}
我目前的错误是"Cannot join to attribute of basic type" 但我确定我有不止 1 个错误。我一直在阅读文档,但似乎找不到使用 return 包装器的 Join 的示例。
- 使用 cb.construct() 进行基于构造函数的查询
- 使用字段名称 productReviews 加入评论
- 而不是 reviewRoot 使用 reviewJoin
按所有非聚合返回字段分组
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();
评论项目:
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;
}