Как использовать проекции JPA с атрибутами «один ко многим» в Criteria API / JPQL - PullRequest
0 голосов
/ 28 мая 2019

У меня проблемы с созданием запроса с помощью 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.
}

1 Ответ

0 голосов
/ 07 июня 2019

Объединение дает вам набор строк, в результате которых получается sql:

Parent    Child
p1        c1
p1        c2
p1        c3

и так далее.Не существует механизма для передачи полученной коллекции в конструктор.

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

Кроме того, другая проблема заключается в том, что ваш запрос может вернуть более одного родителя или потомка.

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

Я предполагаю, что причина в том, что становится слишком сложным для базового поставщика JPA, чтобы знать, где его разбить или какие значения использовать для передачи дочернему конструктору, или, возможно, по более тонким причинам.не знаком с.В итоге требуется, чтобы вы предоставили код для разбора этой матрицы, и если вы собираетесь это сделать, вы можете просто проанализировать результат без JPA.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...