हाइबरनेट Query.list () से सूची <टाइप> करने के लिए "उचित" तरीका क्या है?


84

मैं हाइबरनेट के साथ नौसिखिया हूं, और मैं एक विशिष्ट फिल्टर से मेल खाने वाली वस्तुओं की सूची को वापस करने के लिए एक सरल विधि लिख रहा हूं। List<Foo>एक प्राकृतिक वापसी प्रकार लग रहा था।

मैं जो कुछ भी करता हूं, मैं कंपाइलर को खुश नहीं कर सकता, जब तक कि मैं एक बदसूरत काम नहीं करता @SuppressWarnings

import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

public class Foo {

    public Session acquireSession() {
        // All DB opening, connection etc. removed,
        // since the problem is in compilation, not at runtime.
        return null;
    }

    @SuppressWarnings("unchecked") /* <----- */

    public List<Foo> activeObjects() {
        Session s = acquireSession();
        Query   q = s.createQuery("from foo where active");
        return (List<Foo>) q.list();
    }
}

मैं उससे छुटकारा पाना चाहूंगाSuppressWarnings । लेकिन अगर मैं करता हूं, तो मुझे चेतावनी मिलती है

Warning: Unchecked cast from List to List<Foo>

(मैं इसे अनदेखा कर सकता हूं, लेकिन मैं इसे पहले स्थान पर नहीं लाना चाहूंगा), और अगर मैं .list()वापसी प्रकार के अनुरूप सामान्य हटा देता हूं, तो मुझे चेतावनी मिलती है

Warning: List is a raw type. References to generic type List<E>
should be parameterized.

मैंने देखा कि घोषणा org.hibernate.mapping करता है एक List; लेकिन यह पूरी तरह से एक अलग प्रकार है - Queryएक java.util.List, एक कच्चे प्रकार के रूप में। मुझे यह अजीब लगता है कि हाल ही में हाइबरनेट (4.0.x) पैरामीटर प्रकारों को लागू नहीं करेगा, इसलिए मुझे संदेह है कि यह मेरे बजाय कुछ गलत है।

यह बहुत हद तक कास्ट हाइबरनेट परिणाम वस्तुओं की एक सूची की तरह दिखता है , लेकिन यहां मेरे पास कोई "कठिन" त्रुटियां नहीं हैं (सिस्टम प्रकार फू जानता है, और मैं एसक्यूएलक्यूआई का उपयोग नहीं कर रहा हूं लेकिन एक सीधी क्वेरी)। तो कोई आनंद नहीं।

मैंने हाइबरनेट क्लास कास्ट एक्सेप्शन को भी देखा है क्योंकि यह बहुत अच्छा लग रहा था, लेकिन तब मुझे एहसास हुआ कि मुझे वास्तव में कोई नहीं मिलता Exception... मेरी समस्या सिर्फ एक चेतावनी की है - एक कोडिंग स्टाइल, अगर आप करेंगे।

Jboss.org, हाइबरनेट मैनुअल और कई ट्यूटोरियल पर दस्तावेज़ीकरण इस तरह के विस्तार से विषय को कवर नहीं करते हैं (या मैंने सही स्थानों पर खोज नहीं की?)। जब वे विस्तार से प्रवेश करते हैं, तो वे ऑन-द-फ्लाई कास्टिंग का उपयोग करते हैं - और यह उन ट्यूटोरियल पर है जो आधिकारिक jboss.org साइट पर नहीं थे, इसलिए मैं थोड़ा सावधान हूं।

कोड, एक बार संकलित, कोई स्पष्ट समस्या के साथ चलता है ... कि मुझे पता है ... अभी तक; और परिणाम अपेक्षित हैं।

तो: क्या मैं यह सही कर रहा हूं? क्या मुझसे साफ़ - साफ़ कुछ चीज़ चूक रही है? क्या कोई "आधिकारिक" या "अनुशंसित" तरीका है इसे करने के लिए ?

जवाबों:


101

संक्षिप्त उत्तर @SuppressWarningsसही रास्ता है।

लंबा जवाब, हाइबरनेट विधि Listसे एक कच्चा रिटर्न देता है Query.list, यहां देखें । यह हाइबरनेट के साथ एक बग नहीं है या कुछ हल किया जा सकता है, क्वेरी द्वारा लौटाए गए प्रकार को संकलन समय पर नहीं जाना जाता है

इसलिए जब आप लिखते हैं

final List<MyObject> list = query.list();

आप से एक असुरक्षित डाली कर रहे हैं Listकरने के लिए List<MyObject>- यह टाला नहीं जा सकता।

कोई रास्ता नहीं है कि आप सुरक्षित रूप से कलाकारों को बाहर ले List जा सकते हैं इसमें कुछ भी है।

त्रुटि को दूर करने का एकमात्र तरीका और भी अधिक बदसूरत है

final List<MyObject> list = new LinkedList<>();
for(final Object o : query.list()) {
    list.add((MyObject)o);
}

4
मैं केवल आपके उत्तर को बढ़ाता जा रहा था, उम्मीद करता हूं कि बेहतर होगा। इसके बजाय मुझे पता चला इस समस्या "बदसूरत डाली" दोनों ब्रूस एकेल (जावा में सोच) और रॉबर्ट सेजविक द्वारा के रूप में भेजा - Sedgewick। मुझे stackoverflow.com/questions/509076/… भी मिली । आह।
एलसर्नी

6
मुझे आपके उपयोग की शैली पसंद हैfinal
पावेल

9
यदि हाइबरनेट लोग प्रकार Class<?>में तर्क जोड़ते हैं list(), तो समस्या हल हो सकती है। ऐसे बदसूरत एपीआई का उपयोग करना शर्म की बात है।
बिन वांग

@BinWang तो असुरक्षित कास्ट कहीं और होगा, यह समस्या को हल नहीं करता है - यह इसे स्थानांतरित करता है। कहने की जरूरत नहीं है कि HQL एपीआई को अब प्रभावी रूप से वर्षों के लिए पदावनत कर दिया गया है । JPA में एक प्रकार का सुरक्षित क्वेरी API है जिसे मानदंड क्वेरी API कहा जाता है
बोरिस स्पाइडर

2
@PeteyPabPro जबकि मैं मानता हूं कि कच्चे माल से बचा जाना चाहिए, मैं असहमत हूं कि एक क्वेरी के परिणामों को एक के रूप में माना जाना चाहिए List<Object>। परिणामों को अपेक्षित प्रकार के लिए डाला जाना चाहिए और यह सुनिश्चित करने के लिए इकाई परीक्षण जोड़े जाने चाहिए कि क्वेरी सही परिणाम देती है। " बाद में कोड में " प्रश्नों को चालू करने में त्रुटियां होना अस्वीकार्य है । आपका उदाहरण कोडिंग प्रथाओं के खिलाफ एक तर्क है जो 21 वीं शताब्दी में एंथम होना चाहिए। यह है, मैं सुझाव है, कभी नहीं स्वीकार्य होगा List<Object>
बोरिस द स्पाइडर

26

इसके स्थान पर TypedQuery का उपयोग करने का संकल्प है। इसके बजाय EntityManager से एक क्वेरी बनाते समय इसे इस तरह से कॉल करें:

TypedQuery<[YourClass]> query = entityManager.createQuery("[your sql]", [YourClass].class);
List<[YourClass]> list = query.getResultList(); //no type warning

यह नामित प्रश्नों, देशी नामित प्रश्नों आदि के लिए भी काम करता है। संबंधित विधियों में समान नाम हैं जो वेनिला क्वेरी को लौटाएंगे। जब भी आपको रिटर्न प्रकार पता हो, तो क्वेरी के बजाय इसका उपयोग करें।


1
एक साइड नोट के रूप में, यह विशुद्ध रूप से देशी प्रश्नों के लिए काम नहीं करता है जो आप इनलाइन बना रहे हैं। लौटाया गया प्रश्न केवल एक प्रश्न है, टाइपकेडरी नहीं :(
Taugenichts

अब, त्रुटि संदेश चला गया है और परिणामी सूची ऑब्जेक्ट ऑब्जेक्ट के बजाय वास्तविक वस्तुओं का उपयोग करती है .. महान जवाब!
जोहान्स

यह हाइबरनेट 5.0 में जारी किया गया प्रतीत होता है। मैं इसे ४.३.११ में नहीं देखता, लेकिन निम्न लेख ५.३ को दर्शाता है। wiki.openbravo.com/wiki/… मैं एक जनवरी 2014 स्टैकओवरफ़्लो पोस्ट भी देख रहा हूँ, जिसका उल्लेख है: stackoverflow.com/a/21354639/854342
कर्टिस येलोप

यह हाइबरनेट-जपा-2.0-एपी का हिस्सा है। आप इसे हाइबरनेट 4.3 के साथ उपयोग कर सकते हैं, जैसा कि मैं वर्तमान में हाइबरनेट 4.3 पर इसका उपयोग कर रहा हूं।
ताउगेनिचट्स

6

आप इस तरह से वर्कअराउंड के साथ संकलक चेतावनी से बच सकते हैं:

List<?> resultRaw = query.list();
List<MyObj> result = new ArrayList<MyObj>(resultRaw.size());
for (Object o : resultRaw) {
    result.add((MyObj) o);
}

लेकिन इस कोड के साथ कुछ समस्याएं हैं:

  • सुपरफ्लिश अर्रेलिस्ट बनाया
  • क्वेरी से लौटे सभी तत्वों पर अनावश्यक लूप
  • अब कोड।

और अंतर केवल कॉस्मेटिक है, इसलिए इस तरह के वर्कअराउंड का उपयोग करना - मेरी राय में - व्यर्थ है।

आपको इन चेतावनियों के साथ रहना होगा या उन्हें दबाना होगा।


1
मैं व्यर्थ की बातों से सहमत हूं। मैं दबा दूंगा, भले ही वह मेरे अनाज के खिलाफ हो जाए। सभी को समान रूप से धन्यवाद, और +1।
LSerni

2
> आपको इन चेतावनियों के साथ रहना होगा या उन्हें दबाना होगा। यह दबाने चेतावनी है कि गलत हैं करने के लिए हमेशा बेहतर है, या आप unsupressed गलत चेतावनी की एक स्पैम में सही चेतावनी याद कर सकते हैं
SpongeBobFan

6

आपके प्रश्न का उत्तर देने के लिए, ऐसा करने का कोई "उचित तरीका" नहीं है। अब अगर यह सिर्फ चेतावनी है कि आपको परेशान करता है, तो इसके प्रसार से बचने का सबसे अच्छा तरीका है कि Query.list()विधि को डीएओ में लपेट दिया जाए :

public class MyDAO {

    @SuppressWarnings("unchecked")
    public static <T> List<T> list(Query q){
        return q.list();
    }
}

इस तरह से आपको @SuppressWarnings("unchecked")केवल एक बार उपयोग करना है ।


ढेर अतिप्रवाह में आपका स्वागत है ! वैसे भी, मत भूलना लेने के लिए दौरे
Sнаđошƒаӽ

3

केवल उस तरह से मेरे लिए काम एक Iterator के साथ था।

Iterator iterator= query.list().iterator();
Destination dest;
ArrayList<Destination> destinations= new ArrayList<>();
Iterator iterator= query.list().iterator();
    while(iterator.hasNext()){
        Object[] tuple= (Object[]) iterator.next();
        dest= new Destination();
        dest.setId((String)tuple[0]);
        dest.setName((String)tuple[1]);
        dest.setLat((String)tuple[2]);
        dest.setLng((String)tuple[3]);
        destinations.add(dest);
    }

अन्य तरीकों के साथ जो मैंने पाया, मैंने समस्याएं डाली थीं


क्या "कलाकारों की समस्याएं"? मैंने हमेशा केवल सीधे सूची डाली है, उपरोक्त अधिक संक्षिप्त या सुरक्षित कैसे है?
गियोवन्नी बोटा

मैं सीधे नहीं जा सकता। कास्ट समस्याएं थीं क्योंकि यह ऑब्जेक्ट से डेस्टिनेशन तक नहीं बना सकता था
पोपा आंद्रेई

आप जानते हैं कि हाइबरनेट Destinstionआपके लिए सही निर्माण कर सकता है ? select newवाक्य रचना का उपयोग करना । यह निश्चित रूप से सही दृष्टिकोण नहीं है।
बोरिस स्पाइडर

मेरा भी वही अनुभव था। जैसा कि मेरी क्वेरी कई तालिकाओं से अलग-अलग फ़ील्ड लौटाती है जो एक दूसरे से जुड़ी नहीं हैं। तो मेरे लिए काम करने का एकमात्र तरीका यही था। धन्यवाद :)
चिंतन पटेल

3
List<Person> list = new ArrayList<Person>();
Criteria criteria = this.getSessionFactory().getCurrentSession().createCriteria(Person.class);
for (final Object o : criteria.list()) {
    list.add((Person) o);
}

हां, यह मूल रूप से बोरिस द्वारा सुझाए गए समान 'कुरूपता' है, जिसमें पाश के भीतर एक कलाकार है।
एलसर्नी

2

आप इस तरह एक ResultTransformer का उपयोग करें:

public List<Foo> activeObjects() {
    Session s = acquireSession();
    Query   q = s.createQuery("from foo where active");
    q.setResultTransformer(Transformers.aliasToBean(Foo.class));
    return (List<Foo>) q.list();
}

1
मैं अब इसका परीक्षण नहीं कर सकता, लेकिन ... यह क्या बदलता है? qअभी भी एक है Queryऔर इसलिए q.list()अभी भी एक कच्चा java.util.Listप्रकार है। डाली फिर भी अनियंत्रित है; वस्तु प्रकार बदलकर आंतरिक रूप से कुछ भी लाभ नहीं होना चाहिए ...
एलसर्नी

हां कास्ट अभी भी अनियंत्रित है, लेकिन आपके खेतों के उचित नामकरण के साथ, एक परिणाम ट्रान्सफ़ॉर्मर सेट करना ऑब्जेक्ट्स को आपके वांछित POJO के रूप में कास्टिंग करने का काम करता है। इस
स्टैकओवरफ़्लो

from foo where activeहै नहीं एक देशी क्वेरी। इसलिए परिणाम ट्रांसफार्मर की कोई आवश्यकता नहीं है, क्योंकि डिफ़ॉल्ट मैपिंग पर्याप्त होगी। प्रश्न POJO फ़ील्ड के कास्टिंग के बारे में नहीं है, बल्कि परिणाम ऑब्जेक्ट को कास्टिंग करने के बारे में है। एक परिणाम ट्रांसफार्मर यहाँ मदद नहीं करेगा।
टोबियास लाइपके

0

हाइबरनेट ट्रांसफार्मर का उपयोग करने का उचित तरीका है:

public class StudentDTO {
private String studentName;
private String courseDescription;

public StudentDTO() { }  
...
} 

List resultWithAliasedBean = s.createSQLQuery(
"SELECT st.name as studentName, co.description as courseDescription " +
"FROM Enrolment e " +
"INNER JOIN Student st on e.studentId=st.studentId " +
"INNER JOIN Course co on e.courseCode=co.courseCode")
.setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
.list();

StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);

इरेटिंग थ्रॉर्थ ऑब्जेक्ट [] निरर्थक है और इसमें कुछ प्रदर्शन जुर्माना होगा। ट्रांसफ़ॉर्मर्स के उपयोग के बारे में विस्तृत जानकारी आपको यहाँ मिलेगी: HQL और SQL के लिए ट्रांसफ़ॉर्मर्स

यदि आप और भी सरल समाधान ढूंढ रहे हैं, तो आप आउट-ऑफ-द-बॉक्स-मैप-ट्रांसफार्मर का उपयोग कर सकते हैं:

List iter = s.createQuery(
"select e.student.name as studentName," +
"       e.course.description as courseDescription" +
"from   Enrolment as e")
.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP )
.iterate();

String name = (Map)(iter.next()).get("studentName");

प्रश्न परिणाम परिवर्तन के बारे में नहीं था। यह Queryपरिणामों की कास्टिंग के बारे में था - जो अभी भी आपके उदाहरण में आवश्यक है। और आपके उदाहरण का मूल से कोई लेना-देना नहीं है from foo where active
टोबियास लाइपके

0

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

sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class)) नोटवर्क नहीं किया क्योंकि मुझे रिटर्न लिस्ट एलिमेंट में एरे ऑफ़ ऑब्जेक्ट मिल रहा था न कि लिस्ट में निर्धारित माइग्रेनिटी प्रकार का एलिमेंट एलिमेंट।

जब मैंने sqlQuery.addScalar(-)प्रत्येक चयनित कॉलम और उसके प्रकार को जोड़ा है और विशिष्ट स्ट्रिंग प्रकार कॉलम के लिए हमने निम्नलिखित प्रकार के परिवर्तन किए हैं, तो मेरे लिए यह काम किया है । पसंदaddScalar("langCode");

और मैं नेक्स्टऐनिटी के साथ MYEngityName ज्वाइन कर सकता हूँ select * क्वेरी में यह वापसी सूची में ऑब्जेक्ट की सरणी दे सकते हैं।

कोड नमूना नीचे:

session = ht.getSessionFactory().openSession();
                String sql = new StringBuffer("Select txnId,nft.mId,count,retryReason,langCode FROM  MYEngityName nft INNER JOIN NextEntity m on nft.mId  =  m.id where nft.txnId < ").append(lastTxnId)
                       .append(StringUtils.isNotBlank(regionalCountryOfService)? " And  m.countryOfService in ( "+ regionalCountryOfService +" )" :"")
                       .append(" order by nft.txnId desc").toString();
                SQLQuery sqlQuery = session.createSQLQuery(sql);
                sqlQuery.setResultTransformer(Transformers.aliasToBean(MYEngityName.class));
                sqlQuery.addScalar("txnId",Hibernate.LONG)
                        .addScalar("merchantId",Hibernate.INTEGER)
                        .addScalar("count",Hibernate.BYTE)
                        .addScalar("retryReason")
                        .addScalar("langCode");
                sqlQuery.setMaxResults(maxLimit);
                return sqlQuery.list();

यह कुछ मदद कर सकता है। इस तरह मेरे लिए काम करो।


-1

मुझे यहां सबसे अच्छा समाधान मिला , इस मुद्दे की कुंजी AddEntity विधि है

public static void testSimpleSQL() {
    final Session session = sessionFactory.openSession();
    SQLQuery q = session.createSQLQuery("select * from ENTITY");
    q.addEntity(Entity.class);
    List<Entity> entities = q.list();
    for (Entity entity : entities) {
        System.out.println(entity);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.