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


91

ネイティブクエリを使用したSpringDataリポジトリメソッドがあります

@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ます。

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

回答:


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
SpringデータをNamedNativeQueryと一致させるには、ドメインエンティティのクラス名の後にドットを付けて、NamedNativeQueryの名前の前に付ける必要があります。したがって、名前は(ドメインエンティティがグループであると仮定して) 'Group.getGroupDetails'である必要があります。
グラントレイ

@GrantLayこの質問をご覧ください:stackoverflow.com/q/44871757/7491770まさにこの種の問題があります。
ram 2017

そのようなオブジェクトのリストを返すにはどうすればよいですか?
Nikhil Sahu 2017

1
それを機能させるにGroupDetailsは、@Entity?でマークする必要があります 可能であれば、注釈@NamedNativeQueryを適用する必要があるクラスを教えてください。
マヌー

3
@SqlResultSetMappingそして、@NamedNativeQuery注釈が(例えば、あなたのために春データリポジトリで使用されるエンティティ上に存在しなければならないpublic interface CustomRepository extends CrudRepository<CustomEntity, Long>ことがあるCustomEntityクラス)
トマシュW

111

そのための最も簡単な方法は、いわゆるプロジェクションを使用することだと思います。クエリ結果をインターフェイスにマップできます。使用するのSqlResultSetMappingは不便で、コードが醜くなります:)。

Spring Data 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:注1:

Userエンティティを通常として定義することを忘れないでください。投影されたインターフェイスのフィールドは、このエンティティのフィールドと一致する必要があります。そうしないと、フィールドマッピングが破損する可能性があります(getFirstname()姓などの値を返す可能性があります)。

注2:注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代わりにカスタムリポジトリを使用したいと思います。


それはよりクリーンなソリューションです。確認しましたが、パフォーマンスはSqlResultSetMappingを使用するよりもはるかに劣っています(約30〜40%遅くなります:()
kidnan1991 2018年

うまく機能します!他の場所で使用する場合は、インターフェイスを公開します
tibi 2018年

XMLタイプ(clob)フィールドを抽出する場合は機能しません。なにか提案を?
Ashish 2018

@Ashish代わりにJdbcTemplatedocs.spring.io/spring-framework/docs/current/javadoc-api/org/…)を使用したいと思います。clobをフェッチするためにgetClobメソッドonを使用できます。例:。resultSetInputStreamrs.getClob("xml_column").getCharacterStream()
のMichałStochmal

クエリでSELECT *を使用し、クエリがネイティブクエリである場合はどうなりますか?
Salman Kazmi 2018

17

ミハルのアプローチの方が良いと思います。ただし、ネイティブクエリから結果を取得する方法がもう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文字列配列を目的のエンティティに変換できます。


2
シンプルでエレガント
ジョン

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

訂正:同じ名前は必須ではありません...コンストラクターと返された結果セットのパラメーターの同じシーケンスだけです。
WaqasMemon19年

これは、CountryがJavaエンティティクラスである場合にのみ機能します。国がJavaエンティティクラスでない場合、これは当てはまりません。
YeshwantKAKAD19年

1
これはネイティブクエリでも機能するはずだとおっしゃっていますか?その例を挙げていただけますか?
リチャードティングル

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 2017年

-5

私のコンピューターでは、このコードは機能します。大門の答えとは少し異なります。

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