JPQL-запрос для возврата пользовательского DTO, исходящего от родительской сущности и ее вложенной/дочерней сущности, С ВЛОЖЕННОЙ СУЩНОСТЬЮ, ДОПУСКАЮЩЕЙ ОБНУЛЕНИЕ

#java #hibernate #jpa #spring-data-jpa #jpql

Вопрос:

В настоящее время я борюсь с пользовательским запросом JPQL, который должен быть простым, по крайней мере, на первый взгляд.

Стек: Java 11, spring-boot 2.4.5, spring-boot-starter-данные-jpa 2.4.8, ядро гибернации 5.4.16, база данных Postgre.

дело

Мне просто нужен мой запрос JPQL для извлечения 3 полей, исходящих от родительской сущности, и это дочерняя/вложенная сущность (сопоставленная как однонаправленная связь один к одному), в пользовательском DTO, а не сущность из домена.

Домен выглядит так:

 @Entity
@Table(name = "Item")
public class ItemEntity {
  @Id
  private Long id;

  @NotNull
  private String field1;

  @ManyToOne
  @JoinColumn(name = "nestedEntity_id", referencedColumnName = "id")
  private NestedEntity nestedEntity;
  //...
}
 
 @Entity
@Table(name = "Nested")
public class NestedEntity {
  @Id
  private Long id;

  @NotNull
  private String field1;

  @NotNull
  private String field2;
  //...
}
 
 @Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MyDTO {
  @NotNull
  private String myField;

  private String concatedNestedFields;

  private String otherNestedField;
  //...
}
 

Я думал, что это легко, и сделал что-то вроде этого:

 @Query("SELECT new my.package.MyDto(itemEntity.field1, CONCAT(itemEntity.nestedEntity.field1, ' ', itemEntity.nestedEntity.field2), itemEntity.nestedEntity.field3) FROM ItemEntity itemEntity WHERE itemEntity.country = :country")
MyDTO findByCountry(@Param("country") CountryEnum country);
 

ОСОБЕННОСТИ

Я не знаю, имеет ли это отношение к делу или нет, но поля вложенных сущностей @NotNull аннотированы.

проблема

Проблема возникает, когда значение nestedEntity равно нулю: запрос ничего не возвращает, несмотря на то, что элемент «родительский» существует. Если значение nestedEntity не равно нулю, запрос работает.

ЧТО Я ПЫТАЛСЯ

Я попытался использовать функцию COALESCE (), которая возвращает первое ненулевое значение из приведенных нами параметров для каждого поля вложенности, как показано ниже:

 @Query("SELECT new my.package.MyDto(itemEntity.field1, COALESCE(CONCAT(itemEntity.nestedEntity.field1, ' ', itemEntity.nestedEntity.field2),'-'), COALESCE(itemEntity.nestedEntity.field3), '-') FROM ItemEntity itemEntity WHERE itemEntity.country = :country")
MyDTO findByCountry(@Param("country") CountryEnum country);
 

Но это тоже не работает.


ОБНОВЛЕНИЕ Я только что попробовал что-то, чтобы устранить некоторые коренные причины. Если я выполняю именованный запрос JPA, предоставленный spring-data / JPA, и возвращаю сущность вместо моего пользовательского DTO, он работает даже с вложенной сущностью null. Он извлекает сущность с вложенной нулевой сущностью. Запрос выглядит так:

 ItemEntity findItemEntityByCountry(@Param("country") CountryEnum country);
 

Я не уверен, какой вывод можно сделать по этому поводу, но это может помочь тем, кто понимает JPA лучше, чем я (а это многие люди… XD).

Я не нашел никаких онлайн-ресурсов по этому делу, и я немного заблудился.

Я был бы очень признателен, если бы вы, ребята, помогли мне с этим маленьким удивительно сложным запросом!

Большое спасибо за ваше время, ребята! Надеюсь, это тоже поможет другим 🙂

Комментарии:

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

Ответ №1:

попробуйте выполнить следующее левое соединение

 @Query("SELECT new my.package.MyDto(itemEntity.field1,
                        CONCAT(nestedEntity.field1, ' ', nestedEntity.field2),
                        nestedEntity.field3)
        FROM ItemEntity itemEntity left join itemEntity.nestedEntity as nestedEntity
        WHERE itemEntity.country = :country")
MyDTO findByCountry(@Param("country") CountryEnum country);
 

в противном случае не используйте concat, а просто передайте nestedEntity.поле 2 и выполните объединение на стороне java

Комментарии:

1. Спасибо за ответ, но, к сожалению, это не работает для меня. У меня все еще есть пустой возврат, с использованием или без использования функции CONCAT…