ネイティブクエリを持つ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
にマッピングしたいと思います。
それは可能ですか?もし可能なら、例を挙げていただけますか?
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
を使用してその行動を達成することができます
これを行う最も簡単な方法は、いわゆる投影法を使用することだと思います。クエリ結果をインターフェイスにマップできます。 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
を使用します。
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文字列配列を目的のエンティティに変換できます。
ネイティブクエリまたは非ネイティブクエリを自由に記述でき、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;
}