У меня 2 сущности Product and 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 и вернуть его с помощью 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();
}
Текущая ошибка, которая у меня возникла: «Не удается присоединиться к атрибуту базового типа»
но я уверен, что у меня более 1 ошибки. Я читал документацию, но я не могу найти пример, который использует соединение, которое возвращает оболочку.