[spring] SpringData JPA는 네이티브 쿼리 결과를 Non-Entity POJO에 매핑합니다.

네이티브 쿼리가있는 스프링 데이터 저장소 메서드가 있습니다.

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

가능합니까? 그렇다면 예를 들어 주시겠습니까?



답변

orid의 답변에서 GroupDetails를 가정하면 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);

스프링 데이터 JPA의에 따르면 문서 , 봄 먼저 메소드 이름과 일치하는 이름 쿼리를 찾기 위해 노력할 것입니다 – 그래서 사용하여 @NamedNativeQuery, @SqlResultSetMapping그리고 @ConstructorResult당신은 그 동작을 달성 할 수 있어야한다


답변

그렇게하는 가장 쉬운 방법은 소위 프로젝션을 사용하는 것입니다. 쿼리 결과를 인터페이스에 매핑 할 수 있습니다. 사용 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 ...표기법 을 사용하는 경우 항상 엔티티의 이름과 일치하는 별칭을 정의하십시오. 예를 들어이 코드는 제대로 작동하지 않습니다 (프로젝션은 각 getter에 대해 null을 반환합니다).

@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대신 사용자 지정 저장소와 함께 사용하고 싶습니다 .


답변

나는 Michal의 접근 방식이 더 낫다고 생각합니다. 그러나 네이티브 쿼리에서 결과를 가져 오는 또 다른 방법이 있습니다.

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

이제이 2D 문자열 배열을 원하는 엔티티로 변환 할 수 있습니다.


답변

원하는 방식으로 네이티브 또는 네이티브가 아닌 쿼리를 작성할 수 있으며 사용자 지정 결과 클래스의 인스턴스로 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;
    }
}


답변

다음과 같이 할 수 있습니다.

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


답변

내 컴퓨터에서이 코드가 작동하는 것을 확인했는데 Daimon의 대답과는 약간 다릅니다.

@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")


답변