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 的复合 DTO 投影

JPQL 构造函数表达式 - org.hibernate.hql.ast.QuerySyntaxException:表未映射

在 JPA 2 中使用构造函数表达式时排序

IN 表达式中的 JPQL LOWER 函数

输入参数集合包含 null 的 JPQL 查询

Python 一些好玩的函数