如何使用querydsl从子查询平均值集合中获取总平均值
How to get total average value from subquery average collection using querydsl
我有下一个实体:
@Entity
@Table(name = "search_request_items")
public class SearchRequestItem extends LongIdEntity {
@Column(name = "date")
private Instant date;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@Column(name = "result_count")
private Long resultCount;
/**
* Request's text.
*/
@Column(name = "request")
private String request;
/**
* Request's quality. It may take 0 or 1.
*/
@Column(name = "quality")
private Integer quality;
...
}
然后我有下一个 queryDSL 查询,return 按请求文本分组的质量平均值和用户数的集合:
public JPAQuery<Tuple> prepareTotalQuery() {
QSearchRequestItem requestItem = QSearchRequestItem.searchRequestItem;
QUser user = QUser.user;
NumberExpression<Double> qualityAvgExpression = requestItem.quality.avg();
NumberExpression<Long> qualityCountExpression = requestItem.user.countDistinct();
JPAQuery<Tuple> query = queryFactory
.select(qualityAvgExpression, qualityCountExpression)
.from(requestItem)
.leftJoin(requestItem.user, user)
.groupBy(requestItem.request)
.having(qualityAvgExpression.isNotNull(),
qualityCountExpression.gt(2));
return query;
}
但我需要 return 这个集合的总平均值,就像这个本机查询一样:
select avg(n1.avg_quality)
from (select count(distinct user_id), avg(quality) as avg_quality
from search_request_items
group by request
having avg(quality) is not null and count(distinct user_id) > 2
) n1;
那么,如何更新我的 querydsl 查询以获得此结果?
这里的问题是您使用的是 JPA,而 JPA 不允许在 from 子句中使用子查询作为连接目标。
Blaze-Persistence is an extension of JPA and integrates well with Hibernate. It adds Common Table Expressions and subselect (even lateral) joins to JPQL. Blaze-Persistence also has a Querydsl integration,允许您编写如下查询:
List<Number> fetch = new BlazeJPAQuery<>(entityManager, cbf)
.with(cteType, new BlazeJPAQuery<>()
.bind(cteType.avgQuantity, requestItem.quality.avg())
.from(requestItem)
.leftJoin(requestItem.user, user)
.groupBy(requestItem.request)
.having(qualityAvgExpression.isNotNull(), qualityCountExpression.gt(2))))
)
.select(cteType.avgQuantity.avg())
.from(cteType)
.fetch();
但是,对于普通的 JPA 和 Hibernate,没有简单的方法可以做到这一点。
但前提是你只是对一组数字进行平均,这些数字在 JDBC 上的序列化并不密集,并且不会遇到潜在的 N+1 问题,我建议只做最后的记忆中的平均步数:
queryFactory
.select(qualityCountExpression)
.from(requestItem)
.leftJoin(requestItem.user, user)
.groupBy(requestItem.request)
.having(qualityAvgExpression.isNotNull(),
qualityCountExpression.gt(2))
.stream()
.collect(Collectors.averagingDouble(i -> i.doubleValue()))
我有下一个实体:
@Entity
@Table(name = "search_request_items")
public class SearchRequestItem extends LongIdEntity {
@Column(name = "date")
private Instant date;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@Column(name = "result_count")
private Long resultCount;
/**
* Request's text.
*/
@Column(name = "request")
private String request;
/**
* Request's quality. It may take 0 or 1.
*/
@Column(name = "quality")
private Integer quality;
...
}
然后我有下一个 queryDSL 查询,return 按请求文本分组的质量平均值和用户数的集合:
public JPAQuery<Tuple> prepareTotalQuery() {
QSearchRequestItem requestItem = QSearchRequestItem.searchRequestItem;
QUser user = QUser.user;
NumberExpression<Double> qualityAvgExpression = requestItem.quality.avg();
NumberExpression<Long> qualityCountExpression = requestItem.user.countDistinct();
JPAQuery<Tuple> query = queryFactory
.select(qualityAvgExpression, qualityCountExpression)
.from(requestItem)
.leftJoin(requestItem.user, user)
.groupBy(requestItem.request)
.having(qualityAvgExpression.isNotNull(),
qualityCountExpression.gt(2));
return query;
}
但我需要 return 这个集合的总平均值,就像这个本机查询一样:
select avg(n1.avg_quality)
from (select count(distinct user_id), avg(quality) as avg_quality
from search_request_items
group by request
having avg(quality) is not null and count(distinct user_id) > 2
) n1;
那么,如何更新我的 querydsl 查询以获得此结果?
这里的问题是您使用的是 JPA,而 JPA 不允许在 from 子句中使用子查询作为连接目标。
Blaze-Persistence is an extension of JPA and integrates well with Hibernate. It adds Common Table Expressions and subselect (even lateral) joins to JPQL. Blaze-Persistence also has a Querydsl integration,允许您编写如下查询:
List<Number> fetch = new BlazeJPAQuery<>(entityManager, cbf)
.with(cteType, new BlazeJPAQuery<>()
.bind(cteType.avgQuantity, requestItem.quality.avg())
.from(requestItem)
.leftJoin(requestItem.user, user)
.groupBy(requestItem.request)
.having(qualityAvgExpression.isNotNull(), qualityCountExpression.gt(2))))
)
.select(cteType.avgQuantity.avg())
.from(cteType)
.fetch();
但是,对于普通的 JPA 和 Hibernate,没有简单的方法可以做到这一点。
但前提是你只是对一组数字进行平均,这些数字在 JDBC 上的序列化并不密集,并且不会遇到潜在的 N+1 问题,我建议只做最后的记忆中的平均步数:
queryFactory
.select(qualityCountExpression)
.from(requestItem)
.leftJoin(requestItem.user, user)
.groupBy(requestItem.request)
.having(qualityAvgExpression.isNotNull(),
qualityCountExpression.gt(2))
.stream()
.collect(Collectors.averagingDouble(i -> i.doubleValue()))