स्प्रिंग डेटा JPA ग्रुप बाय क्वेरी से कस्टम ऑब्जेक्ट कैसे लौटाएं


115

मैं स्प्रिंग डेटा JPA के साथ स्प्रिंग बूट एप्लिकेशन विकसित कर रहा हूं। मैं किसी क्षेत्र द्वारा समूह के लिए एक कस्टम 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" }
]

इसे कैसे प्राप्त किया जा सकता है?

जवाबों:


249

JPQL प्रश्नों के लिए समाधान

यह भीतर JPQL प्रश्नों के लिए समर्थित है जेपीए विनिर्देश

चरण 1 : एक साधारण बीन वर्ग घोषित करें

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 : रिपॉजिटरी विधि से बीन के उदाहरण लौटाएं

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

महत्वपूर्ण लेख

  1. पैकेज नाम सहित बीन क्लास के लिए पूरी तरह से योग्य पथ प्रदान करना सुनिश्चित करें। उदाहरण के लिए, अगर बीन क्लास कहा जाता है MyBeanऔर यह पैकेज में है com.path.to, तो बीन के लिए पूरी तरह से योग्य रास्ता होगा com.path.to.MyBean। बस प्रदान करने MyBeanसे काम नहीं चलेगा (जब तक कि बीन क्लास डिफ़ॉल्ट पैकेज में न हो)।
  2. newकीवर्ड का उपयोग करके बीन क्लास कंस्ट्रक्टर को कॉल करना सुनिश्चित करें । SELECT new com.path.to.MyBean(...)काम करेंगे, जबकि SELECT com.path.to.MyBean(...)नहीं करेंगे।
  3. बीन कंस्ट्रक्टर में अपेक्षित रूप से उसी क्रम में विशेषताएँ पास करना सुनिश्चित करें। एक अलग क्रम में विशेषताओं को पारित करने का प्रयास एक अपवाद का कारण होगा।
  4. सुनिश्चित करें कि क्वेरी एक वैध JPA क्वेरी है, यानी यह एक देशी क्वेरी नहीं है। @Query("SELECT ..."), या @Query(value = "SELECT ..."), या @Query(value = "SELECT ...", nativeQuery = false)काम करेगा, जबकि @Query(value = "SELECT ...", nativeQuery = true)काम नहीं करेगा। इसका कारण यह है कि देशी प्रश्नों को जेपीए प्रदाता के संशोधनों के बिना पारित किया जाता है, और अंतर्निहित आरडीबीएमएस के खिलाफ निष्पादित किया जाता है। के बाद से newऔर com.path.to.MyBeanवैध एसक्यूएल कीवर्ड नहीं हैं, आरडीबीएमएस तो एक अपवाद फेंकता है।

देशी प्रश्नों का हल

जैसा कि ऊपर उल्लेख किया गया है, new ...वाक्यविन्यास एक जेपीए समर्थित तंत्र है और सभी जेपीए प्रदाताओं के साथ काम करता है। हालाँकि, यदि क्वेरी स्वयं एक JPA क्वेरी नहीं है, अर्थात, यह एक देशी क्वेरी है, तो new ...सिंटैक्स काम नहीं करेगा क्योंकि क्वेरी सीधे अंतर्निहित RDBMS पर पारित हो जाती है, जो newकीवर्ड को नहीं समझती है क्योंकि यह इसका हिस्सा नहीं है एसक्यूएल मानक।

इन जैसी स्थितियों में, बीन वर्गों को स्प्रिंग डेटा प्रोजेक्शन इंटरफेस के साथ बदलने की आवश्यकता होती है ।

चरण 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();
}

ASपरिणामी क्षेत्रों को मैप करने के लिए SQL मैप का उपयोग करें ।


1
यह काम नहीं कर रहा है, फायरिंग त्रुटि:Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate class [SurveyAnswerReport] [select new SurveyAnswerReport(v.answer,count(v.id)) from com.furniturepool.domain.Survey v group by v.answer] at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) at org.hibernate.jpa.spi.AbstractEnti..........
प्रणव सी बालन

SurveyAnswerReport आपके आउटपुट में यह क्या है । मुझे लगता है कि आपने SurveyAnswerStatistics अपनी कक्षा के साथ बदल दिया SurveyAnswerReport। आपको पूरी तरह से योग्य वर्ग नाम निर्दिष्ट करने की आवश्यकता है।
बंटी

8
बीन क्लास को पूरी तरह से योग्य होना चाहिए, यानी पूर्ण पैकेज नाम शामिल करना चाहिए। कुछ इस तरह com.domain.dto.SurveyAnswerReport
मनीष

2
मुझे 'java.lang.IllegalArgumentException: PersistentEntity शून्य नहीं होना चाहिए!' जब मैं अपने से कस्टम प्रकार वापस करने की कोशिश करता हूं JpaRepository? कुछ विन्यास मैं याद किया है?
मारिओश

1
देशी क्वेरी अपवाद का उपयोग करते समय कहता है: नेस्टेड अपवाद java.lang.IllegalArgumentException: प्रबंधित प्रकार नहीं है: वर्ग ... इसे क्यों खुश किया जाना चाहिए?
मिखाइल ज़ेगेंती

20

यह SQL क्वेरी वापसी सूची <ऑब्जेक्ट []> होगा।

आप इसे इस तरह से कर सकते हैं:

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

1
इस प्रश्न के लिए आपकी प्रतिक्रिया के लिए धन्यवाद। यह कुरकुरा और स्पष्ट था
धीरज आर।

@ मनीष धन्यवाद आपने मेरी रात की नींद बचाई, आपके तरीके ने एक आकर्षण की तरह काम किया !!!!!!!
विनील

15

मुझे पता है कि यह एक पुराना सवाल है और इसका जवाब पहले ही दिया जा चुका है, लेकिन यहां एक और तरीका है:

@Query("select new map(count(v) as cnt, v.answer) from Survey v group by v.answer")
public List<?> findSurveyCount();

मुझे आपका उत्तर पसंद है क्योंकि यह मुझे एक नया वर्ग या इंटरफ़ेस बनाने के लिए मजबूर नहीं करता है। इसने मेरे लिए काम किया।
यूरी हसल अराउजो

ठीक काम करता है, लेकिन मैं इसके बजाय जेनेरिक में मैप के उपयोग को प्राथमिकता देता हूं?, क्योंकि मैप हमें उन्हें कुंजी (0) और मूल्य (1) के रूप में एक्सेस करने देगा
समीम आफताब अहमद

10

इंटरफेस का उपयोग करके आप सरल कोड प्राप्त कर सकते हैं। कंस्ट्रक्टरों को बनाने और मैन्युअल रूप से कॉल करने की आवश्यकता नहीं है

चरण 1 : आवश्यक क्षेत्रों के साथ पूर्णांक घोषित करें:

public interface SurveyAnswerStatistics {

  String getAnswer();
  Long getCnt();

}

चरण 2 : इंटरफ़ेस में गेट्टर के समान नाम वाले कॉलम का चयन करें और रिपॉजिटरी विधि से पूर्णांक वापस करें:

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

}

दुर्भाग्य से अनुमानों को जीयूआई के दृष्टिकोण से डीटीओ ऑब्जेक्ट के रूप में उपयोग नहीं किया जा सकता है। यदि आप फॉर्म जमा करने के लिए डीटीओ का पुन: उपयोग करना चाहते हैं तो आप नहीं कर पाएंगे। आपको अभी भी गेटर्स / सेटर के साथ एक अलग नियमित बीन की आवश्यकता होगी। तो यह एक अच्छा समाधान नहीं है।
जीन b।

इसके अलावा, लापता वर्ग
मिखाइल ज़ेगेंती

6

एक कस्टम पूजो क्लास को परिभाषित करें, जो निश्चित रूप से कहती है

@Query(value = "select new com.xxx.xxx.class.SureveyQueryAnalytics(s.answer, count(sv)) from Survey s group by s.answer")
List<SureveyQueryAnalytics> calculateSurveyCount();


3

मुझे क्वेरी स्ट्रिंग्स में जावा टाइप नाम पसंद नहीं हैं और इसे एक विशिष्ट कंस्ट्रक्टर के साथ संभालना है। वसंत 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 को हल करने के लिए कोड को लोम्बोक की आवश्यकता है


@Getter कोड को चलाने से पहले एक त्रुटि दिखा रहा है क्योंकि यह ऑब्जेक्ट प्रकार के लिए नहीं है
user666

लोम्बोक की जरूरत है। कोड में केवल एक फुटनोट जोड़ा गया है।
dwe

1

मैंने अभी इस समस्या को हल किया है:

  • क्लास-आधारित अनुमान क्वेरी देशी ( @Query(value = "SELECT ...", nativeQuery = true)) के साथ काम नहीं करता है इसलिए मैं इंटरफ़ेस का उपयोग करके कस्टम डीटीओ को परिभाषित करने की सलाह देता हूं।
  • डीटीओ का उपयोग करने से पहले क्वेरी को सही ढंग से सही या नहीं सत्यापित करना चाहिए

1

मैंने एक मूल क्वेरी को मैप करने के लिए कस्टम डीटीओ (इंटरफ़ेस) का उपयोग किया - सबसे लचीला दृष्टिकोण और रीफैक्टरिंग-सुरक्षित।

मेरे पास यह समस्या थी - यह आश्चर्यजनक रूप से, इंटरफ़ेस में फ़ील्ड्स का क्रम और क्वेरी मामलों में कॉलम। मैंने इसे इंटरफ़ेस गेटर्स को वर्णानुक्रम से क्रमबद्ध करके काम किया और फिर उसी तरह से क्वेरी में कॉलम ऑर्डर किया।


0
@Repository
public interface ExpenseRepo extends JpaRepository<Expense,Long> {
    List<Expense> findByCategoryId(Long categoryId);

    @Query(value = "select category.name,SUM(expense.amount) from expense JOIN category ON expense.category_id=category.id GROUP BY expense.category_id",nativeQuery = true)
    List<?> getAmountByCategory();

}

उपरोक्त कोड ने मेरे लिए काम किया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.