解决JPQL子查询SemanticException:正确编写FROM子查询SELECT语句
问题背景
出现SemanticException错误:使用JPQL子查询时,SELECT列表中位置1的项没有别名(原错误信息:Select item at position 1 in select list has no alias when using JPQL subselect)
给定实体类
@Entity @Data public class Collection { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String createdBy; @OneToMany(mappedBy = "collection") private List<CollectionAccess> collectionAccesses; } @Entity @Data public class CollectionAccess { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn( foreignKey = @ForeignKey( value = ConstraintMode.PROVIDER_DEFAULT, name = "fk_collectionaccess_collection_collections_id")) private Collection collection; private Integer accessType; private LocalDateTime expirationAtUtc; }
给定数据库数据
select id, created_by from collection; +----+------------+ | id | created_by | +----+------------+ | 40 | ABC123 | +----+------------+
select * from collection_access; +----+-------------+----------------------------+---------------+ | id | access_type | expiration_at_utc | collection_id | +----+-------------+----------------------------+---------------+ | 2 | 0 | 2011-12-03 03:15:30.000000 | 40 | | 3 | 1 | 2011-12-03 03:15:30.000000 | 40 | +----+-------------+----------------------------+---------------+
引发错误的查询代码
em.createQuery( "SELECT t FROM (SELECT c FROM Collection c " + "LEFT JOIN c.collectionAccesses ca WHERE c.id = ?1 " + "AND (c.createdBy = ?2 OR (ca.accessType = ?3 AND ca.expirationAtUtc <= ?4))) t " + "JOIN FETCH t.collectionAccesses ", Collection.class) .setParameter(1, id) .setParameter(2, "ABC123") .setParameter(3, 1) .setParameter(4, now) .getSingleResult();
错误栈
java.lang.IllegalArgumentException: org.hibernate.query.SemanticException: 使用JPQL子查询时,SELECT列表中位置1的项没有别名(别名在CTE和FROM子句中的子查询中是必需的) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:143) ~[hibernate-core-6.4.4.Final.jar:6.4.4.Final]
问询问题
如何在JPQL/Hibernate中正确编写包含FROM子查询的SELECT查询?
解决方案
错误原因
Hibernate的JPQL语法强制要求:FROM子句中的子查询,其SELECT列表里的每一项都必须显式指定别名,哪怕是查询实体对象也不能省略。你原查询的子查询SELECT c没有给c指定别名,触发了这个语义校验错误。
两种修复方式
方式1:给子查询的SELECT项添加别名
如果必须保留嵌套子查询的结构,只需给子查询中选中的实体添加别名即可:
em.createQuery( "SELECT t FROM (SELECT c AS c FROM Collection c " + "LEFT JOIN c.collectionAccesses ca WHERE c.id = ?1 " + "AND (c.createdBy = ?2 OR (ca.accessType = ?3 AND ca.expirationAtUtc <= ?4))) t " + "JOIN FETCH t.collectionAccesses ", Collection.class) .setParameter(1, id) .setParameter(2, "ABC123") .setParameter(3, 1) .setParameter(4, now) .getSingleResult();
这里SELECT c AS c就是给子查询中的Collection实体指定别名,满足JPQL的语法要求,错误即可消除。
方式2:简化查询,去掉不必要的子查询
你的查询逻辑其实不需要嵌套子查询,直接在主查询中完成过滤并关联集合加载即可,这样更简洁高效:
em.createQuery( "SELECT DISTINCT c FROM Collection c " + "LEFT JOIN c.collectionAccesses ca " + "JOIN FETCH c.collectionAccesses " + "WHERE c.id = ?1 " + "AND (c.createdBy = ?2 OR (ca.accessType = ?3 AND ca.expirationAtUtc <= ?4))", Collection.class) .setParameter(1, id) .setParameter(2, "ABC123") .setParameter(3, 1) .setParameter(4, now) .getSingleResult();
- 添加
DISTINCT是因为JOIN FETCH会加载关联的collectionAccesses集合,产生笛卡尔积导致重复的Collection结果,用DISTINCT可以去重。 - 这种方式避免了嵌套子查询,查询逻辑更清晰,性能也更优。
注意事项
- 当在FROM子句中使用子查询时,所有SELECT项必须显式指定别名,包括实体、字段、聚合函数结果等。
- 使用
JOIN FETCH加载集合关联时,要注意笛卡尔积问题,必要时用DISTINCT去重,或者通过@Fetch(FetchMode.SUBSELECT)等注解调整加载策略。
内容的提问来源于stack exchange,提问作者SoT
相关产品推荐
相关产品推荐

