JPQL:在构造函数表达式中接收集合
Posted
技术标签:
【中文标题】JPQL:在构造函数表达式中接收集合【英文标题】:JPQL: Receiving a Collection in a Constructor Expression 【发布时间】:2011-08-25 07:11:34 【问题描述】:我正在使用 JPQL,并希望在构造函数表达式中接收一些普通参数和一个集合,以直接创建 DTO 对象。但是如果Collection是空的,我总是会报错,因为他没有找到正确的构造函数:
DTO 类如下所示:
public class DTO
private long id;
private String name;
private Collection<Child> children;
public DTO (long id, String name, Collection<Child> children)
this.id = id;
this.name = name;
this.children= children;
子类:
public class Child
private String name;
private int age;
现在构造函数表达式如下所示:
return (List<DTO>) getEm().createQuery("SELECT DISTINCT NEW de.DTO(p.id, p.name, p.childs)
FROM Parent p").getResultList();
当前的问题是,如果 Collection p.childs 为空,它说它没有找到正确的构造函数,它需要 (long, String, Child) 而不是 (long, String, Collection)。
您有任何解决方案,还是根本不可能在构造函数表达式中使用集合?
还有一件事:如果我轻松地创建两个构造函数(...、Collection childs AND ...、Child childs),我不会得到任何结果,但也没有错误...在我看来并不是很满意:- /
【问题讨论】:
我认为您忘记发布您的Parent
课程。 child
的复数形式也是children
。
您是否尝试过在查询中添加条件 - "WHERE p.childs IS NOT EMPTY AND SIZE(p.childs) 0"
我也在寻找类似的东西,我能找到的最接近你所问的东西(不依赖额外的库)是@VladMihalcea 的标题为How to fetch a one-to-many DTO projection with JPA and Hibernate 的帖子 -这是我对Q46681780 的评论的副本,因为它看起来非常接近这个评论。如果这被视为垃圾邮件或其他内容,我们深表歉意......
【参考方案1】:
JPA 规范(至少 2.0、2.1 和 2.2 版)不允许在构造函数表达式中使用集合作为参数。 4.8 节定义了一个这样的构造函数表达式:
constructor_expression ::=
NEW constructor_name ( constructor_item , constructor_item* )
constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable
single_valued_path_expression
听起来像 - 一个指向某种标量的属性表达式(例如 p.id
),scalar_expression
也是一个指向标量的表达式,aggregate_expression
是像sum
这样的函数的应用,它将多值表达式简化为标量表达式,identification_variable
是对您正在查询的类型的引用。没有一个可以收藏价值。
所以,如果您的 JPA 提供程序允许您在构造函数表达式中使用集合值参数,那是因为它超出了规范。这是非常好的,但不是你必须依赖的东西!
【讨论】:
嗨,汤姆·安德森,非常感谢您提供这些信息。我只是使用 Eclipse Link,所以我想,不使用 Hibernate 或其他东西,它也作用于更高层,我不会成功。 @评论:是的,对,我忘记了父类,但我也只有一个 id、一个名字和一个有或没有孩子的 Collection。所以像 IS NOT EMPTY 这样的条件不符合我的目标,我也需要没有孩子的父母。也感谢您的帮助。 在 JPA 2.1 中相同,参见第 4.14 章 BNF,第 211 页。【参考方案2】:试试,
public DTO (long id, String name, Object children)
【讨论】:
【参考方案3】:我遇到了类似的问题,并按照 James 的建议在 DTO 构造函数中尝试了“Object”,但是传入了一个子对象,它似乎只是第一个子对象,而不是预期的子对象列表/数组。
我最终得到了一个“正常”查询,在 for 循环中创建了所有 DTO。
TypedQuery<Parent> query = em.createQuery("SELECT p FROM Parent p", Parent class);
List<Parent> list = query.getResultList();
List<DTO> result = new ArrayList<>();
for (Parent p : list)
DTO dto = new DTO();
//set dto props and fill collection
result.add(obj);
return result;
【讨论】:
【参考方案4】:这不可能像其他人已经回答的那样开箱即用,但我认为这是 Blaze-Persistence Entity Views 的完美用例。
我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。
使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:
@EntityView(Parent.class)
public interface ParentDto
@IdMapping
Long getId();
String getName();
Set<ChildDto> getChildren();
@EntityView(Child.class)
interface ChildDto
@IdMapping
Long getId();
String getName();
int getAge();
查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。
ParentDto a = entityViewManager.find(entityManager, ParentDto.class, id);
Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ParentDto> findAll(Pageable pageable);
最好的部分是,它只会获取实际需要的状态!
【讨论】:
以上是关于JPQL:在构造函数表达式中接收集合的主要内容,如果未能解决你的问题,请参考以下文章
JPQL 构造函数表达式 - org.hibernate.hql.ast.QuerySyntaxException:表未映射