我希望能够在 Spring Boot 3.1.2 中使用本机 Hibernate 查询。其原因是,据我了解,HQL 可以比 JPQL 更好地处理嵌套 SELECT(而不是在 where 中)。
我的型号是 Bird 在 HealthCheck 上有 OneToMany HealthCheck 在任务上有 OneToMany(摘要) 长度测量(扩展任务) 体重测量(扩展任务)
鸟
public class Bird {
private Long id;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "bird")
private List<HealthCheck> listHealthCheck = new ArrayList<>();
健康检查
public class HealthCheck {
private Long id;
@JsonManagedReference
@ManyToOne(fetch = FetchType.EAGER, optional = false)
private Bird bird;
@JsonManagedReference
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="healthCheck")
private List<Task> tasks;
private LocalDateTime catchDateTime;
长度测量
public class LengthMeasurements extends Task {
private Double beakLength;
private Double legLength;
private Double armWidth;
private Double armDepth;
体重测量
public class WeightMeasurements extends Task {
private Double weight;
抽象类Task意味着我不能使用hibernate路径表达式。
我想最终构建一个查询,获取最近测量集合的最新(基于检查日期)平均值和最新权重的平均值。
类似:
+---------+-----------+---------------------+------------+-----------+-----------+--------------------+------------+
| bird.id | bird.name | recent measure date | avg length | avg width | avg depth | recent weight date | avg weight |
+---------+-----------+---------------------+------------+-----------+-----------+--------------------+------------+
有许多部分示例,但它们很旧,并且 Spring Boot 自动配置干扰了我迄今为止尝试过的内容。
为了提供迄今为止我的尝试的一些背景信息,我有以下代码:
@Repository
public class CustomBirdRepositoryImpl {
@PersistenceContext
private EntityManager entityManager;
final SessionFactory sf = entityManager
.unwrap( Session.class );
public void customHql(Long id) {
Session session = sf.openSession();
String hql = " SELECT MAX(h.catchDateTime), AVG(l.beakLength),
AVG(l.tarsusLength), AVG(l.tarsusWidth), b.name FROM LengthMeasurements l
JOIN l.healthCheck.bird b
JOIN l.healthCheck h
LEFT JOIN (SELECT MAX(w.healthCheck.catchDateTIme), AVG(w.weight), w.healthCheck.bird.id as bid FROM WeightMeasurements w WHERE w.healthCheck.bird.id=1) AS x
ON x.bid = w.healthCheck.bird.id
WHERE b.id=1 GROUP BY b.id, h.id ORDER BY l.healthCheck.catchDateTime DESC LIMIT 1";
org.hibernate.query.Query<BirdDetailsDto> query = session.createQuery(hql);
for(BirdDetailsDto b: query.getResultList()) {
b.toString();
}
}
}
如果我尝试在 JPA 中运行上面的查询,我会得到:
src\main\java\com\nz\kiwi\repository\BirdRepository.java:54: error: ')' expected
"WHERE b.id = :id ORDER BY l.healthCheck.catchDate DESC LIMIT 1) lm";)
更新
在 Hibernate 会话中尝试查询时,我收到了信息更丰富的错误:
Caused by: java.lang.IllegalArgumentException: Component at index 0 has no alias, but alias is required
如何执行这个子选择语句?
事实证明,hibernate 6 中的内部
SELECT
语句是相对较新的功能。
虽然内部
SELECT
可以单独工作,但在 LEFT JOIN
中使用时,值需要别名。
所以这个片段:
SELECT MAX(w.healthCheck.catchDateTime), AVG(w.weight)
变成 SELECT MAX(w.healthCheck.catchDateTime) AS weight_date, AVG(w.weight) AS avg_weight
把它们放在一起:
@Service
@Transactional
public class CustomBirdRepositoryImpl implements CustomBirdRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public Object customQuery(Long id) {
return entityManager.createQuery(
"SELECT MAX(h.catchDateTime), AVG(l.beakLength), AVG(l.tarsusLength), AVG(l.tarsusWidth), b.name, b.id, weight_date, avg_weight FROM LengthMeasurements l " +
"JOIN l.healthCheck h " +
"JOIN h.bird b " +
"LEFT JOIN(SELECT MAX(w.healthCheck.catchDateTime) AS weight_date, AVG(w.weight) AS avg_weight, w.healthCheck.bird.id as bid FROM WeightMeasurements w WHERE w.healthCheck.bird.id=:id) AS x " +
"ON b.id = x.bid " +
"WHERE b.id=:id " +
"GROUP BY b.id, h.id ORDER BY l.healthCheck.catchDateTime DESC LIMIT 1")
.setParameter("id", id)
.getSingleResult();
}
}
为了完整性 CustomBirdRepository
@Repository
public interface CustomBirdRepository {
Object customQuery(Long id);
}