web-dev-qa-db-ja.com

Spring Data JPAはネイティブクエリ結果を非エンティティPOJOにマッピングします

ネイティブクエリを持つSpring Dataリポジトリメソッドがあります

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

そして、結果を非エンティティPOJO GroupDetailsにマッピングしたいと思います。

それは可能ですか?もし可能なら、例を挙げていただけますか?

62
alexanoid

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

Spring Data JPA documentation によると、springは最初にメソッド名に一致する名前付きクエリを見つけようとします-したがって、@NamedNativeQuery@SqlResultSetMappingおよび@ConstructorResultを使用してその行動を達成することができます

53
Daimon

これを行う最も簡単な方法は、いわゆる投影法を使用することだと思います。クエリ結果をインターフェイスにマップできます。 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 ...表記を使用する場合、常にエンティティの名前に一致するエイリアスを定義します。たとえば、このコードは正しく機能しません(投影は各ゲッターに対して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を使用します。

62

Michalのアプローチの方が優れていると思います。ただし、ネイティブクエリから結果を取得する方法がもう1つあります。

@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文字列配列を目的のエンティティに変換できます。

5
Ashish

ネイティブクエリまたは非ネイティブクエリを自由に記述でき、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;
    }
}
3
Waqas Memon

次のようなことができます

@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;
    }
1
Chandan Gawri