Quarkus Panache投影@ElementCollection集合属性失败问题排查
解决Quarkus Panache投影@ElementCollection集合属性失败的问题
问题概述
使用Quarkus Panache对带有@ElementCollection注解的集合属性进行投影时,无法正常提取结果集,触发SQL语法错误;但对普通列(如series)的投影可正常运行。
实体类代码
@Builder @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Entity(name = "table") public class Table implements Serializable { @Id @Column(length = 10) private String name; @Column(name = "series") private String series; @Fetch(FetchMode.SUBSELECT) @BatchSize(size = 50) @ElementCollection @CollectionTable( name = "table_word", joinColumns = @JoinColumn(name = "name") ) @Column(name = "value") private List<String> tWord; }
投影类代码
@RegisterForReflection @Setter @Getter @Builder @AllArgsConstructor public class TableAttribute<T> { private T t; }
Repository代码
public class TableRepository implements PanacheRepository<Table> { // 正常运行的普通列投影 find( "select v.series from " + Table.class.getName() + " v" ).project(TableAttribute.class) .page(Page.ofSize(limit)) .list(); // 运行失败的集合属性投影 find( "select v.tWord from " + Table.class.getName() + " v" ).project(TableAttribute.class) .page(Page.ofSize(limit)) .list(); }
报错信息
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet ... Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "." ...
生成的SQL日志
-- 投影series时的正常SQL Hibernate: select table0_.series as col_0_0_ from fss.table vehicle0_ -- 投影tWord时的错误SQL Hibernate: select . as col_0_0_ from fss.table table0_ inner join fss.table_word tword1_ on table0_.name=sas1_.name
解决方案
问题根源
JPQL中直接选择集合属性(v.tWord)时,Hibernate无法将多值的集合关联表映射为单个投影列,导致生成无效SQL(出现空的.)。同时原JPQL使用Table.class.getName()获取全限定类名作为实体名,虽Hibernate兼容,但更规范的写法是使用@Entity注解指定的name属性值(即table)。
具体解决方法
方法1:使用构造函数投影
修改JPQL,通过构造函数直接实例化投影类,Hibernate会正确处理集合属性的映射:
find( "select new com.yourpackage.TableAttribute(v.tWord) from table v" ).page(Page.ofSize(limit)) .list();
注意替换
com.yourpackage为TableAttribute类的实际包路径,同时使用实体名table替代全限定类名。
方法2:直接查询集合元素再包装
如果需要获取集合中的每个元素而非整个集合列表,可以直接查询关联表元素,再手动包装为投影类:
// 查询所有tWord元素 List<String> words = find( "select value from table t join t.tWord value" ).page(Page.ofSize(limit)) .list(); // 手动包装为TableAttribute List<TableAttribute<String>> attributes = words.stream() .map(TableAttribute::new) .collect(Collectors.toList());
方法3:跳过投影类直接获取集合列表
如果不需要投影类包装,可直接返回集合列表再按需处理:
List<List<String>> result = find( "select v.tWord from table v" ).page(Page.ofSize(limit)) .list();
内容的提问来源于stack exchange,提问作者Homer
相关产品推荐
相关产品推荐

