简体   繁体   English

如何在Criteria API / JPQL中使用具有一对多属性的JPA投影

[英]How to use JPA projections with one-to-many attributes in Criteria API / JPQL

I have difficulties creating a query with Criteria API that projects attributes of the queried entity and instantiates a DTO. 我很难使用Criteria API创建查询,该API 用于投影查询实体的属性并实例化DTO。 One of the projected attributes maps a one-to-many relationship with another entity, so it is a set of dependent entities . 其中一个投影属性将一对多关系映射到另一个实体,因此它是一组依赖实体 I am using fetch join to retrieve the set. 我正在使用fetch join来检索集合。 But I am getting the following error: 但是我收到以下错误:

org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list

I have already tried using a regular join, but in this case the set of dependent entities won't get populated. 我已经尝试过使用常规连接,但在这种情况下,将不会填充依赖实体集。 Removing the join and / or fetch completely didn't help either. 完全删除联接和/或获取也没有帮助。

I am using JPA specification 2.0, Hibernate 4.2.21.Final, Spring Data JPA 1.10.11.RELEASE. 我正在使用JPA规范2.0,Hibernate 4.2.21.Final,Spring Data JPA 1.10.11.RELEASE。

Could anybody advise me on this? 有人可以就此提出建议吗? I would be happy for a working JPQL also. 我也很高兴工作的JPQL。

This is my implementation of the query: 这是我对查询的实现:

@Override
public List<EntityADto> findByPartialKey1OrderByPartialKey2(String partialKey1) {
    // Create query
    final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    final CriteriaQuery<EntityADto> criteriaQuery = criteriaBuilder.createQuery(EntityADto.class);

    // Define FROM clause
    final Root<EntityA> root = criteriaQuery.from(EntityA.class);
    root.fetch(EntityA_.oneToManyAttribute);

    // Define DTO projection
    criteriaQuery
            .select(criteriaBuilder.construct(
                    EntityADto.class,
                    root.get(EntityA_.id).get(EntityAId_.partialKey1),
                    root.get(EntityA_.id).get(EntityAId_.partialKey2),
                    root.get(EntityA_.stringAttribute1),
                    root.get(EntityA_.stringAttribute2),
                    root.get(EntityA_.oneToManyAttribute)))
            .orderBy(criteriaBuilder.asc(root.get(EntityA_.id).get(EntityAId_.partialKey2)))
            .distinct(true);

    // Define WHERE clause
    final ParameterExpression<String> parameterPartialKey1 = criteriaBuilder.parameter(String.class);
    criteriaQuery.where(criteriaBuilder.equal(root.get(EntityA_.id).get(EntityAId_.partialKey1), parameterPartialKey1));

    // Execute query
    final TypedQuery<EntityADto> typedQuery = entityManager.createQuery(criteriaQuery);
    typedQuery.setParameter(parameterPartialKey1, partialKey1);

    return typedQuery.getResultList();
}

The entities look as follows: 实体看起来如下:

@Entity
@Table(name = "TABLE_A", uniqueConstraints = 
@UniqueConstraint(columnNames = {
    "PARTIAL_KEY_1", "STRING_ATTR_1", "STRING_ATTR_2" }))
public class EntityA {
    @EmbeddedId
    @AttributeOverrides({
        @AttributeOverride(name = "partialKey1", column = @Column(name = "PARTIAL_KEY_1", nullable = false)),
        @AttributeOverride(name = "partialKey2", column = @Column(name = "PARTIAL_KEY_2", nullable = false))})
    private EntityAId id;
    @Column(name = "STRING_ATTR_1", nullable = false)
    private String stringAttribute1;
    @Column(name = "STRING_ATTR_2", nullable = false)
    private String stringAttribute2;
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "entityA")
    private Set<EntityB> entityBs;

    // getters and setters omitted for brevity.
}

@Entity
@Table(name = "TABLE_2")
public class EntityB {
    @EmbeddedId
    @AttributeOverrides({
        @AttributeOverride(name = "partialKey3", column = @Column(name = "PARTIAL_KEY_3", nullable = false)),
        @AttributeOverride(name = "partialKey1", column = @Column(name = "PARTIAL_KEY_1", nullable = false)),
        @AttributeOverride(name = "partialKey2", column = @Column(name = "PARTIAL_KEY_2", nullable = false))})
    private EntityBId id;
    @Column(name = "VALUE")
    private String value;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({
        @JoinColumn(name = "PARTIAL_KEY_1", referencedColumnName = "PARTIAL_KEY_1", nullable = false, insertable = false, updatable = false),
        @JoinColumn(name = "PARTIAL_KEY_2", referencedColumnName = "PARTIAL_KEY_2", nullable = false, insertable = false, updatable = false)})
    private EntityA entityA;

    // getters and setters omitted for brevity.

} }

And finally the DTO: 最后是DTO:

public class EntityADto implements Serializable {
    private static final long serialVersionUID = -5343329086697620178L;

    private String partialKey1;
    private Integer partialKey2;
    private String stringAttribute1;
    private String stringAttribute2;
    private Map<String, String> additionalAttributes;

    public ProzessdatStandardDto() { }

    public ProzessdatStandardDto(String partialKey1,
                                 Integer partialKey2,
                                 String stringAttribute1,
                                 String stringAttribute2,
                                 Set<EntityB> entityBs) {
        this.partialKey1 = partialKey1;
        this.partialKey2 = partialKey2;
        this.stringAttribute1 = stringAttribute1;
        this.stringAttribute2 = stringAttribute2;

        final Map<String, String> entityBsConverted = new HashMap<>();
        if (!CollectionUtils.isEmpty(entityBs)) {
            for (EntityB entityB : entityBs) {
                entityBsConverted.put(entityB.getPartialKey3(), entityB.getValue());
            }
        }
        this.additionalAttributes = prozessdatExpansionsConverted;
    }

    // getters and setters omitted for brevity.
}

A join gives you a collection of rows result in sql: 连接为您提供sql中的行集合:

Parent    Child
p1        c1
p1        c2
p1        c3

and so on. 等等。 There is no mechanism for passing the resulting collection into a constructor. 没有将结果集合传递给构造函数的机制。

JPA Spec 4.14 JPA Spec 4.14

constructor_expression ::=
NEW constructor_name ( constructor_item {, constructor_item}* )

constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable

Also, another issue is that your query might return more than one parent or child. 另外,另一个问题是您的查询可能会返回多个父级或子级。

Parent    Child    Child2
p1        c111     c121
p1        c121     
p1        c131     c122
p2        c211     c211
p2        c221     c212
p2        c231     

I'm guessing that the reason is that becomes too complicated for the underlying JPA provider to know where to split this up or which values to use to pass to a child constructor or perhaps more subtle reasons I'm not familiar with. 我猜测的原因是底层JPA提供程序变得太复杂,无法知道在哪里拆分它或者使用哪些值来传递给子构造函数,或者可能是我不熟悉的更微妙的原因。 Bottom line it requires that you provide code for parsing this matrix and if you're going to do that you might as well just parse the result without JPA. 最重要的是,它要求您提供用于解析此矩阵的代码,如果您要这样做,您也可以在没有JPA的情况下解析结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM