私はSpring Data JPAを使ってSpring Bootアプリケーションを開発しています。私はいくつかのフィールドでグループ化してカウントを得るためにカスタムJPQLクエリを使っています。以下は私のレポジトリメソッドです。
@Query(value = "select count(v) as cnt, v.answer from Survey v group by v.answer")
public List<?> findSurveyCount();
それは働いていて、結果は以下のように得られます:
[
[1, "a1"],
[2, "a2"]
]
私はこのようなものを手に入れたいです。
[
{ "cnt":1, "answer":"a1" },
{ "cnt":2, "answer":"a2" }
]
どうすればこれを達成できますか?
これは JPA仕様 内のJPQLクエリでサポートされています。
ステップ1:単純なBeanクラスを宣言する
package com.path.to;
public class SurveyAnswerStatistics {
private String answer;
private Long cnt;
public SurveyAnswerStatistics(String answer, Long cnt) {
this.answer = answer;
this.count = cnt;
}
}
ステップ2:リポジトリメソッドからbeanインスタンスを返す
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query("SELECT " +
" new com.path.to.SurveyAnswerStatistics(v.answer, COUNT(v)) " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
MyBean
という名前でパッケージcom.path.to
内にある場合、Beanへの完全修飾パスはcom.path.to.MyBean
になります。 MyBean
を指定しただけでは機能しません(Beanクラスがデフォルトパッケージに含まれていない場合)。new
キーワードを使用してBeanクラスコンストラクターを必ず呼び出してください。 SELECT new com.path.to.MyBean(...)
は機能しますが、SELECT com.path.to.MyBean(...)
は機能しません。@Query("SELECT ...")
、@Query(value = "SELECT ...")
、または@Query(value = "SELECT ...", nativeQuery = false)
は機能しますが、@Query(value = "SELECT ...", nativeQuery = true)
は機能しません。これは、ネイティブクエリがJPAプロバイダに変更を加えずに渡され、基になるRDBMSに対して実行されるためです。 new
とcom.path.to.MyBean
は有効なSQLキーワードではないため、RDBMSは例外をスローします。前述のように、new ...
構文はJPAでサポートされているメカニズムであり、すべてのJPAプロバイダで機能します。ただし、クエリ自体がJPAクエリではない場合、つまりネイティブクエリである場合、クエリは基礎となるRDBMSに直接渡されるため、new
キーワードを理解できないため、new ...
構文は機能しませんこれは標準SQLの一部ではないためです。
このような状況では、Beanクラスを Spring Data Projection インターフェースに置き換える必要があります。
ステップ1:プロジェクションインターフェースを宣言する
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
ステップ2:クエリから射影プロパティを返す
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query(nativeQuery = true, value =
"SELECT " +
" v.answer AS answer, COUNT(v) AS cnt " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
SQL AS
キーワードを使用して、明確なマッピングのために結果フィールドを投影プロパティにマッピングします。
このSQLクエリはList <Object []>を返します。
あなたはこうすることができます:
@RestController
@RequestMapping("/survey")
public class SurveyController {
@Autowired
private SurveyRepository surveyRepository;
@RequestMapping(value = "/find", method = RequestMethod.GET)
public Map<Long,String> findSurvey(){
List<Object[]> result = surveyRepository.findSurveyCount();
Map<Long,String> map = null;
if(result != null && !result.isEmpty()){
map = new HashMap<Long,String>();
for (Object[] object : result) {
map.put(((Long)object[0]),object[1]);
}
}
return map;
}
}
私はこれが古い質問であり、それはすでに答えられていることを知っています、しかしここにもう一つのアプローチがあります:
@Query("select new map(count(v) as cnt, v.answer) from Survey v group by v.answer")
public List<?> findSurveyCount();
インターフェースを使用すると、より簡単なコードを入手できます。コンストラクタを作成して手動で呼び出す必要はありません
ステップ1:必要なフィールドでintefraceを宣言してください。
public interface SurveyAnswerStatistics {
String getAnswer();
Long getCnt();
}
ステップ2:インタフェースのgetterと同じ名前の列を選択し、リポジトリメソッドからintefraceを返します。
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query("select v.answer as answer, count(v) as cnt " +
"from Survey v " +
"group by v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
カスタムpojoクラスを定義し、sureveyQueryAnalyticsと言い、クエリーの戻り値をカスタムpojoクラスに格納します。
@Query(value = "select new com.xxx.xxx.class.SureveyQueryAnalytics(s.answer, count(sv)) from Survey s group by s.answer")
List<SureveyQueryAnalytics> calculateSurveyCount();
私はクエリ文字列の中でJavaの型名を好まず、特定のコンストラクタでそれを処理します。 Spring JPAは、HashMapパラメータでクエリ結果を使用して暗黙的にコンストラクタを呼び出します。
@Getter
public class SurveyAnswerStatistics {
public static final String PROP_ANSWER = "answer";
public static final String PROP_CNT = "cnt";
private String answer;
private Long cnt;
public SurveyAnswerStatistics(HashMap<String, Object> values) {
this.answer = (String) values.get(PROP_ANSWER);
this.count = (Long) values.get(PROP_CNT);
}
}
@Query("SELECT v.answer as "+PROP_ANSWER+", count(v) as "+PROP_CNT+" FROM Survey v GROUP BY v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
コードは@Getterを解決するためにLombokを必要とします
私はちょうどこの問題を解決しました:
@Query(value = "SELECT ...", nativeQuery = true
)では動作しませんので、インターフェイスを使用してカスタムDTOを定義することをお勧めします。