Подтвердить что ты не робот

Spring Data JPA отображает собственный результат запроса в не-Entity POJO

У меня есть метод репозитория Spring с собственным запросом

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
    GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

и я хотел бы сопоставить результат с Non-Entity POJO GroupDetails.

Возможно ли, и если да, не могли бы вы привести пример?

4b9b3361

Ответ 1

Предполагая GroupDetails, как в ответе orid, вы пробовали JPA 2.1 @ConstructorResult?

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID"),
                @ColumnResult(name="USER_ID")
            }
        )
    }
)

@NamedNativeQuery(name="getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")

и использовать следующее в интерфейсе репозитория:

GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

Согласно Spring Документация JPA , Spring сначала попытается найти именованный запрос, соответствующий вашему имени метода, поэтому с помощью @NamedNativeQuery, @SqlResultSetMapping и @ConstructorResult, вы сможете достичь этого поведения

Ответ 2

Я думаю, что самый простой способ сделать это - использовать так называемую проекцию. Он может сопоставить результаты запроса с интерфейсами. Использование SqlResultSetMapping и делает ваш код уродливым :).

Пример прямо из исходного кода данных JPA:

public interface UserRepository extends JpaRepository<User, Integer> {

   @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
   NameOnly findByNativeQuery(Integer id);

   public static interface NameOnly {

     String getFirstname();

     String getLastname();

  }
}

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

Проверьте эту весеннюю запись JPA документов для получения дополнительной информации о прогнозах.

Примечание 1:

Помните, что ваша сущность User определена как обычная - поля из проектируемого интерфейса должны совпадать с полями в этой сущности. В противном случае отображение поля может быть нарушено (getFirstname() может вернуть значение фамилии и так далее).

Заметка 2:

Если вы используете SELECT table.column... нотация всегда определяет псевдонимы, соответствующие именам из сущности. Например, этот код не будет работать должным образом (проекция будет возвращать нули для каждого получателя):

@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

Но это прекрасно работает:

@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);

В случае более сложных запросов я бы предпочел использовать JdbcTemplate с собственным репозиторием.

Ответ 3

Я думаю, что Михал подход лучше. Но есть еще один способ получить результат из собственного запроса.

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
String[][] getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

Теперь вы можете преобразовать этот массив двухмерных строк в нужный объект.

Ответ 4

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

@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")

Создать DTO:

package example;

public class CountryAndCapital {
    public String countryName;
    public String capitalName;

    public CountryAndCapital(String countryName, String capitalName) {
        this.countryName = countryName;
        this.capitalName = capitalName;
    }
}

Ответ 5

Если GroupDetails что-то вроде:

public class GroupDetails {

    int groupId;
    int userId;

    public GroupDetails(int groupId , int userId) {
        this.groupId = groupId;
        this.userId = userId;
    }

    // getters setters, etc.
}

Следующее должно работать:

@Query(value = "SELECT new GroupDetails(gm.group_id, gm.user_id) FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId")

Ответ 6

Вы можете сделать что-то вроде

@NamedQuery(name="IssueDescriptor.findByIssueDescriptorId" ,

    query=" select new com.test.live.dto.IssuesDto (idc.id, dep.department, iss.issueName, 
               cat.issueCategory, idc.issueDescriptor, idc.description) 
            from Department dep 
            inner join dep.issues iss 
            inner join iss.category cat 
            inner join cat.issueDescriptor idc 
            where idc.id in(?1)")

И конструктор должен быть

public IssuesDto(long id, String department, String issueName, String issueCategory, String issueDescriptor,
            String description) {
        super();
        this.id = id;
        this.department = department;
        this.issueName = issueName;
        this.issueCategory = issueCategory;
        this.issueDescriptor = issueDescriptor;
        this.description = description;
    }

Ответ 7

На моем компьютере этот код работает. Он немного отличается от ответа Даймона.

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID",type=Integer.class),
                @ColumnResult(name="USER_ID",type=Integer.class)
            }
        )
    }
)

@NamedNativeQuery(name="User.getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")