स्प्रिंग डेटा JPA ने गैर-इकाई POJO के लिए मूल क्वेरी परिणाम को मैप किया


91

मेरे पास एक देशी क्वेरी के साथ एक स्प्रिंग डेटा रिपॉजिटरी विधि है

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

और मैं नॉन-एंटिटी पोजो के परिणाम को मैप करना चाहूंगा GroupDetails

क्या यह संभव है और यदि हां, तो क्या आप कृपया एक उदाहरण प्रदान कर सकते हैं?

जवाबों:


64

GroupDetails को orid के जवाब के रूप में मानकर आपने 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);

स्प्रिंग डेटा जेपीए प्रलेखन के अनुसार , वसंत पहले आपके नाम से मेल खाने वाली क्वेरी का पता लगाने की कोशिश करेगा - इसलिए उपयोग करके @NamedNativeQuery, @SqlResultSetMappingऔर @ConstructorResultआपको उस व्यवहार को प्राप्त करने में सक्षम होना चाहिए


15
NADNativeQuery से मिलान करने में सक्षम होने वाले स्प्रिंग डेटा के लिए, डोमेन इकाई का वर्ग नाम एक डॉट द्वारा पीछा किया जाता है, जिसे NamedNativeQuery के नाम से उपसर्ग करने की आवश्यकता होती है। तो नाम होना चाहिए (डोमेन इकाई मान समूह है) 'Group.getGroupDetails'।
ग्रांट लेट

@GranLay आप इस सवाल पर एक नज़र डाल सकते हैं: stackoverflow.com/q/44871757/7491770 मुझे इस तरह की समस्या है।
राम

मैं ऐसी वस्तुओं की सूची कैसे लौटाऊंगा?
निखिल साहू

1
यह काम पाने के लिए, GroupDetailsके साथ चिह्नित किया जाना चाहिए @Entity? यदि संभव हो तो आप कृपया बता सकते हैं कि एनोटेशन किस वर्ग पर @NamedNativeQueryलागू किया जाना है?
मनु

3
@SqlResultSetMappingऔर @NamedNativeQueryएनोटेशन आपके स्प्रिंग डेटा रिपॉजिटरी (उदाहरण के लिए public interface CustomRepository extends CrudRepository<CustomEntity, Long>यह CustomEntityवर्ग है) में इस्तेमाल की जाने वाली इकाई पर मौजूद होना चाहिए
टॉमसज़ डब्ल्यू

111

मुझे लगता है कि ऐसा करने का सबसे आसान तरीका तथाकथित प्रक्षेपण का उपयोग करना है। यह इंटरफेस के लिए क्वेरी परिणामों को मैप कर सकता है। उपयोग करना 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();

  }
}

आप इस विधि का उपयोग अनुमानों की सूची प्राप्त करने के लिए भी कर सकते हैं।

अनुमानों के बारे में अधिक जानकारी के लिए इस स्प्रिंग डेटा जेपीए डॉक्स प्रविष्टि की जाँच करें।

नोट 1:

याद रखें कि आपकी Userइकाई को सामान्य के रूप में परिभाषित किया गया है - अनुमानित इंटरफ़ेस से फ़ील्ड इस इकाई में फ़ील्ड से मेल खाना चाहिए। अन्यथा फ़ील्ड मैपिंग को तोड़ा जा सकता है ( getFirstname()अंतिम नाम वगैरह का मान लौटा सकता है)।

नोट 2:

यदि आप SELECT table.column ...नोटेशन का उपयोग करते हैं, तो हमेशा इकाई से नाम मिलान करने वाले उपनामों को परिभाषित करें। उदाहरण के लिए यह कोड ठीक से काम नहीं करेगा (प्रक्षेपण प्रत्येक गटर के लिए नल लौटाएगा):

@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

अच्छी तरह से काम करता है! इंटरफ़ेस सार्वजनिक करना आप कहीं और इसका इस्तेमाल करना चाहते हैं तो
Tibi

यदि आप XML प्रकार (क्लोब) फ़ील्ड को निकालना चाहते हैं तो काम नहीं करता है। कोई उपाय?
आशीष

@ आशीष मैं इसके बजाय JdbcTemplate( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ) का उपयोग करूँगा । आप लौंग लाने के लिए getClobविधि का उपयोग कर सकते हैं । एक उदाहरण के लिए :। resultSetInputStreamrs.getClob("xml_column").getCharacterStream()
मिशैल स्टोकमल

यदि मैं क्वेरी में SELECT * का उपयोग करता हूं और क्वेरी एक देशी है?
सलमान काज़मी

17

मुझे लगता है कि मिशल का दृष्टिकोण बेहतर है। लेकिन, मूल प्रश्न से परिणाम प्राप्त करने का एक और तरीका है।

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

अब, आप इस 2 डी स्ट्रिंग ऐरे को अपनी इच्छित इकाई में बदल सकते हैं।


2
सरल और सुरुचिपूर्ण
जॉन

9

आप अपनी देशी या गैर-देशी क्वेरी को अपने इच्छित तरीके से लिख सकते हैं, और आप कस्टम परिणाम कक्षाओं के उदाहरणों के साथ JPQL क्वेरी परिणामों को लपेट सकते हैं। क्वेरी में दिए गए स्तंभों के समान नामों के साथ एक DTO बनाएँ और क्वेरी द्वारा लौटाए गए समान अनुक्रम और नामों के साथ सभी तर्क निर्माता बनाएँ। फिर डेटाबेस को क्वेरी करने के लिए निम्नलिखित तरीके का उपयोग करें।

@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")

डीटीओ बनाएं:

package example;

public class CountryAndCapital {
    public String countryName;
    public String capitalName;

    public CountryAndCapital(String countryName, String capitalName) {
        this.countryName = countryName;
        this.capitalName = capitalName;
    }
}

सुधार: समान नाम अनिवार्य नहीं हैं ... निर्माणकर्ता और लौटे परिणाम सेट में मापदंडों का बस एक ही क्रम।
वाकास मेमन

यह तभी काम करता है जब देश आपकी जावा इकाई वर्ग है। यह नहीं होगा यदि देश आपकी जावा इकाई वर्ग नहीं है।
यशवंत काकड़

1
आप कहते हैं कि यह देशी प्रश्नों के साथ भी काम करना चाहिए? क्या आप इसका एक उदाहरण दे सकते हैं?
रिचर्ड टिंगल

ओपी देशी क्वेरी पूछता है, लेकिन दिया गया उदाहरण एक गैर-देशी है
सीएलएस

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
प्रश्न मूल प्रश्नों के बारे में है, एचक्यूएल में लिखे गए प्रश्नों के बारे में नहीं।
DBK

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

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