JPQL:在构造函数表达式中接收一个集合

20

我正在使用JPQL,希望在一个构造表达式中接收一些普通参数和一个集合,以直接创建DTO对象。但如果集合是空的,我总是会得到一个错误,因为它找不到正确的构造函数:

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();

目前的问题是,如果p.childs为空,它会提示找不到正确的构造函数,需要的是(long, String, Child)而不是(long, String, Collection)。

你有任何解决方案吗?或者在构造函数表达式中使用Collection根本不可能吗?

还有一件事:如果我轻松创建两个构造函数(..., Collection childs AND ..., Child childs),我既没有结果,也没有错误... 在我看来并不是很令人满意 :-/


我认为您忘记发布您的“Parent”类了。此外,“child”的复数形式是“children”。 - Behrang
你尝试过在查询中添加条件 "WHERE p.childs IS NOT EMPTY AND SIZE(p.childs) <> 0" 吗? - Nayan Wadekar
我也在寻找类似的东西,最接近你所问的(不依赖于额外库)的是@VladMihalcea的文章如何使用JPA和Hibernate获取一对多DTO投影 - 这是我在Q46681780上的评论副本,因为它看起来非常接近这个问题。如果这被视为垃圾邮件或其他什么,请谅解... - OzgurH
4个回答

19

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提供程序允许您在构造函数表达式中使用集合值参数,那么它就超出了规范范围。这非常好,但您不能保证一定会有效!


嗨,Tom Anderson,非常感谢您提供的信息。我只是使用Eclipse Link,所以我想,如果不使用Hibernate或其他在更高层次上起作用的东西,我将无法成功。@Comments: 是的,你说得对,我忘记了父类,但我也只有一个id、一个名称和一个带或不带子项的集合。因此,像IS NOT EMPTY这样的条件不能满足我的目标,我还需要没有孩子的父母。谢谢你的帮助。 - Florian
1
在JPA 2.1中相同,参见第4.14章BNF,第211页。 - Arend v. Reinersdorff
1
仍然与JPA 3.1相同 https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1#a5438 - undefined

4
尝试一下,
public DTO (long id, String name, Object children)

2

我曾经遇到过类似的问题,按照James建议在DTO构造函数中尝试使用"Object",但是传递进来的是一个子对象,而不是预期的子对象列表/数组。

最终,我通过在for循环中创建所有DTOs的"正常"查询解决了这个问题。

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;

0

像其他人已经回答过的那样,这是无法通过现有工具轻松实现的,但我认为这是 Blaze-Persistence Entity Views的完美应用案例。

我创建了这个库,以便轻松地将JPA模型和自定义接口或抽象类定义的模型进行映射,就像Spring Data Projections一样。其思想是您可以根据需要定义目标结构(领域模型),并通过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/zh_CN/index.html#spring-data-features

Page<ParentDto> findAll(Pageable pageable);

最好的部分是,它只会获取实际上必要的状态!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接