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


91

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

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

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

답변:


64

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당신은 그 동작을 달성 할 수 있어야한다


15
스프링 데이터가 NamedNativeQuery와 일치 할 수 있으려면 도메인 엔터티의 클래스 이름 뒤에 점이 오는 NamedNativeQuery의 이름 앞에 접두사가 있어야합니다. 따라서 이름은 (도메인 엔티티가 그룹이라고 가정) 'Group.getGroupDetails'가되어야합니다.
Grant Lay

@GrantLay는이 질문을 볼 수 있습니까? stackoverflow.com/q/44871757/7491770 정확히 이런 종류의 문제가 있습니다.

그러한 객체 목록을 어떻게 반환합니까?
Nikhil Sahu

1
작동하려면 ? GroupDetails로 표시 해야합니다 @Entity. 가능하다면 어떤 클래스에 주석 @NamedNativeQuery을 적용 해야하는지 말씀해 주 시겠습니까?
Manu

3
@SqlResultSetMapping@NamedNativeQuery(예 : 주석은 봄의 데이터 저장소에 사용되는 개체에 있어야합니다 public interface CustomRepository extends CrudRepository<CustomEntity, Long>그것은이다 CustomEntity클래스)
토마스 W

111

그렇게하는 가장 쉬운 방법은 소위 프로젝션을 사용하는 것입니다. 쿼리 결과를 인터페이스에 매핑 할 수 있습니다. 사용 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대신 사용자 지정 저장소와 함께 사용하고 싶습니다 .


더 깨끗한 솔루션입니다. 확인했지만 성능은 SqlResultSetMapping을 사용하는 것보다 훨씬 나쁩니다 (약 30-40 % 느립니다. :()
kidnan1991

잘 작동합니다! 당신이 다른 곳에서 사용하려면 인터페이스를 공개
티비

XML 유형 (clob) 필드를 추출하려는 경우 작동하지 않습니다. 어떠한 제안?
Ashish

@Ashish 대신 JdbcTemplate( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… )를 사용하고 싶습니다 . clob을 가져 오기 위해 getClobon 메소드를 사용할 수 있습니다 . 예 : . resultSetInputStreamrs.getClob("xml_column").getCharacterStream()
Michał Stochmal 2018

쿼리에서 SELECT *를 사용하고 쿼리가 기본 쿼리이면 어떻게됩니까?
Salman Kazmi

17

나는 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 문자열 배열을 원하는 엔티티로 변환 할 수 있습니다.


2
간단하고 우아한
john

9

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

수정 : 동일한 이름은 필수가 아닙니다. 생성자에있는 동일한 매개 변수 시퀀스와 반환 된 결과 집합입니다.
Waqas Memon

이것은 Country가 Java 엔티티 클래스 인 경우에만 작동합니다. Country가 Java 엔티티 클래스가 아닌 경우에는 그렇지 않습니다.
Yeshwant KAKAD

1
이것이 네이티브 쿼리에서도 작동해야한다고 말씀 하셨나요? 그 예를 들어 주시겠습니까?
Richard Tingle

OP는 네이티브 쿼리를 요청하지만 제공된 예제는 네이티브가 아닌 것입니다
CLS

0

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

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

13
문제는 HQL로 작성된 쿼리가 아니라 네이티브 쿼리에 관한 것입니다.
DBK

-5

내 컴퓨터에서이 코드가 작동하는 것을 확인했는데 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")

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.