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

标签 java hibernate spring-data-jpa jpql criteria-api

我在使用 Criteria API 创建查询时遇到困难该查询会投影查询实体的属性并实例化 DTO。其中一个投影属性与另一个实体映射一对多关系,因此它是一组依赖实体。我正在使用 fetch join 来检索该集合。但我收到以下错误:

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

我已经尝试过使用常规联接,但在这种情况下,依赖实体集不会被填充。完全删除连接和/或获取也没有帮助。

我正在使用 JPA 规范 2.0、Hibernate 4.2.21.Final、Spring Data JPA 1.10.11.RELEASE。

有人可以给我建议吗?我也很高兴能有一个可用的 JPQL。

这是我的查询实现:

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

实体如下所示:

@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.

}

最后是 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.
}

最佳答案

连接为您提供 sql 中的行结果集合:

Parent    Child
p1        c1
p1        c2
p1        c3

等等。没有将结果集合传递给构造函数的机制。

JPA 规范 4.14

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

constructor_item ::=
single_valued_path_expression |
scalar_expression |
aggregate_expression |
identification_variable

此外,另一个问题是您的查询可能会返回多个父级或子级。

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

我猜测原因是底层 JPA 提供程序变得太复杂,无法知道在哪里分割它或使用哪些值传递给子构造函数,或者可能是我不熟悉的更微妙的原因。最重要的是,它要求您提供用于解析该矩阵的代码,如果您打算这样做,您也可以在不使用 JPA 的情况下解析结果。

关于java - 如何在 Criteria API/JPQL 中使用具有一对多属性的 JPA 投影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56340777/

相关文章:

java - 透明JFrame背景

Java 挪威语小数分隔符在 Java 6 和 7 中有所不同

java - 如何查找哪个应用程序保留了 Postgres 连接?

java - 在每个 Spring Jpa Repository 调用上自动添加条件

java - 黑莓对齐的布局设计问题

java - Spring 3.0.5 和 Hibernate 3.5.3 - 包引用错误?

postgresql - 使用 Postgresql 和 hibernate 进行全文搜索 - 缺少 tsvector 和运算符 @@ 的类型

java - 无法将 NULL 插入列

mysql - JPA 更新与连接

spring - 在我的 Spring 批处理中添加 maven 依赖项会出现特定错误