अपने नियंत्रकों में SQL से बचने के लिए रणनीतियाँ ... या मुझे अपने मॉडल में कितने तरीके होने चाहिए?


17

इसलिए जब मैं अपने मॉडल में भाग लेता हूं तो ऐसी स्थिति होती है जहां मेरे मॉडल या तो शुरू होते हैं:

  • टन और तरीकों के टन के साथ राक्षसों में बढ़ो

या

  • आप उन्हें एसक्यूएल के टुकड़े पास करने की अनुमति दें, ताकि वे एक लाख अलग-अलग तरीकों की आवश्यकता न होने के लिए पर्याप्त लचीले हों

उदाहरण के लिए, मान लें कि हमारे पास "विजेट" मॉडल है। हम कुछ बुनियादी तरीकों से शुरू करते हैं:

  • मिल ($ आईडी)
  • सम्मिलित ($ रिकॉर्ड)
  • अद्यतन ($ आईडी, $ रिकॉर्ड)
  • हटाएँ ($ आईडी)
  • getList () // विजेट्स की एक सूची प्राप्त करें

यह सब ठीक है और बांका है, लेकिन फिर हमें कुछ रिपोर्टिंग की आवश्यकता है:

  • सूची बनाएंबेटन ($ start_date, $ end_date)
  • सूचीपार्टेडबेटन ($ start_date, $ end_date)
  • listOfPending ()

और फिर रिपोर्टिंग जटिल होने लगती है:

  • सूचीपसंद करेंबेट की सूची ($ start_date, $ end_date)
  • listForCustomer ($ CUSTOMER_ID)
  • सूचीकरण

आप देख सकते हैं कि यह कहाँ बढ़ रहा है ... आखिरकार हमारे पास कई विशिष्ट क्वेरी आवश्यकताएं हैं जो मुझे या तो टन और विधियों को लागू करने की आवश्यकता है, या किसी प्रकार की "क्वेरी" ऑब्जेक्ट जिसे मैं एक एकल -> क्वेरी (क्वेरी) में पारित कर सकता हूं $ क्वेरी) विधि ...

... या बस गोली को काटो, और कुछ ऐसा करना शुरू करो:

  • सूची = MyModel-> क्वेरी ("start_date> X और end_date <Y और लंबित = 1 और customer_id = Z")

50 मिलियन अन्य विशिष्ट तरीकों के बजाय सिर्फ एक ही तरीका रखने की एक निश्चित अपील है ... लेकिन यह कभी-कभी "गलत" लगता है कि मूल रूप से नियंत्रक में SQL क्या है।

क्या इस तरह की स्थितियों को संभालने का एक "सही" तरीका है? क्या यह एक सामान्य -> ​​क्वेरी () पद्धति की तरह प्रश्नों को भरा जाना स्वीकार्य लगता है?

क्या बेहतर रणनीतियाँ हैं?


मैं अभी इसी समस्या से गुजर रहा हूं, गैर-एमवीसी प्रोजेक्ट में। सवाल यह रहता है कि क्या डेटा एक्सेस लेयर एब्सट्रैक्ट हर स्टोर की गई प्रक्रिया को छोड़ दे, और बिजनेस लॉजिक लेयर डेटाबेस एग्नोस्टिक को छोड़ दे, या डेटा लेयर लेयर जेनेरिक होना चाहिए, बिजनेस लेयर की कीमत पर अंतर्निहित डेटाबेस के बारे में कुछ जानना चाहिए? शायद एक मध्यवर्ती समाधान के पास ExecuteSP (स्ट्रिंग स्पनाम, परम ऑब्जेक्ट [] मापदंडों) जैसा कुछ होना चाहिए, फिर पढ़ने के लिए व्यावसायिक परत के लिए एक कॉन्फ़िगर फ़ाइल में सभी सपा नामों को शामिल करें। मैं वास्तव में इस के लिए एक बहुत अच्छा जवाब नहीं है, यद्यपि।
ग्रेग जैक्सन

जवाबों:


10

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

क्वेरी ऑब्जेक्ट्स आपको व्यक्तिगत रूप से प्रबंधित और बनाए रखी गई रणनीति ऑब्जेक्ट्स में प्रत्येक क्वेरी के लिए तर्क को अलग करके, एकल जिम्मेदारी सिद्धांत का पालन करते हैं। या तो आपका नियंत्रक सीधे उनके उपयोग का प्रबंधन कर सकता है, या एक माध्यमिक नियंत्रक या सहायक वस्तु को सौंप सकता है।

क्या आपके पास उनमें से बहुत कुछ होगा? निश्चित रूप से। क्या कुछ सामान्य प्रश्नों में बांटा जा सकता है? हां फिर से।

क्या आप मेटाडेटा से ऑब्जेक्ट बनाने के लिए निर्भरता इंजेक्शन का उपयोग कर सकते हैं? यही सबसे ORM उपकरण करते हैं।


4

ऐसा करने का कोई सही तरीका नहीं है। कई लोग ओआरएम का उपयोग सभी जटिलता को दूर करने के लिए करते हैं। अधिक उन्नत ORM में से कुछ जटिल SQL स्टेटमेंट में कोड एक्सप्रेशन का अनुवाद करते हैं। ओआरएम में उनके डाउनसाइड भी हैं, हालांकि कई अनुप्रयोगों के लिए लाभ लागत से अधिक हैं।

यदि आप बड़े पैमाने पर डेटासेट के साथ काम नहीं कर रहे हैं, तो सबसे सरल बात यह है कि संपूर्ण तालिका को मेमोरी में चुनें और कोड में फ़िल्टर करें।

//pseudocode
List<Person> people = Sql.GetList<Person>("select * from people");
List<Person> over21 = people.Where(x => x.Age >= 21);

आंतरिक रिपोर्टिंग अनुप्रयोगों के लिए यह दृष्टिकोण संभवतः ठीक है। यदि डेटासेट वास्तव में बड़ा है, तो आपको अपनी टेबल पर बहुत सारे कस्टम तरीकों के साथ-साथ उपयुक्त इंडेक्स की भी आवश्यकता होगी।


1
+ 1 के लिए "ऐसा करने का कोई सही तरीका नहीं है"
ozz

1
दुर्भाग्य से, डेटा सेट के बाहर फ़िल्टर करना वास्तव में एक विकल्प नहीं है, यहां तक ​​कि सबसे छोटे डेटा सेट के साथ हम काम करते हैं- यह अभी बहुत धीमा है। :-( यह सुनकर अच्छा लगा कि अन्य लोग मेरी इसी समस्या में भाग लेते हैं। :-)
कीथ पामर जूनियर

@ कीथेल्मर जिज्ञासा से बाहर, आपकी टेबल कितनी बड़ी हैं?
डेन

सैकड़ों हज़ार पंक्तियाँ, अगर ज्यादा नहीं। डेटाबेस के बाहर स्वीकार्य प्रदर्शन के साथ फ़िल्टर करने के लिए बहुत सारे, ESPECIALLY एक वितरित वास्तुकला के साथ जहां डेटाबेस अनुप्रयोग के समान मशीन पर नहीं हैं।
कीथ पामर जूनियर

-1 के लिए "ऐसा करने का कोई सही तरीका नहीं है"। कई सही तरीके हैं। जब आप ओपी कर रहे थे तब एक फीचर जोड़ते हुए तरीकों की संख्या दोगुना करना एक अस्थिर दृष्टिकोण है, और यहां सुझाया गया विकल्प समान रूप से अस्थिर है, बस क्वेरी सुविधाओं की संख्या के बजाय डेटाबेस के आकार के संबंध में। स्केलेबल दृष्टिकोण मौजूद हैं, अन्य उत्तरों को देखें।
थियोडोर मर्डॉक

4

कुछ ORM आपको मूल विधियों से शुरू होने वाले जटिल प्रश्नों का निर्माण करने की अनुमति देते हैं। उदाहरण के लिए

old_purchases = (Purchase.objects
    .filter(date__lt=date.today(),type=Purchase.PRESENT).
    .excude(status=Purchase.REJECTED)
    .order_by('customer'))

Django ORM में एक पूरी तरह से मान्य क्वेरी है ।

विचार यह है कि आपके पास कुछ क्वेरी बिल्डर (इस मामले में Purchase.objects) है जिसकी आंतरिक स्थिति किसी क्वेरी के बारे में जानकारी का प्रतिनिधित्व करती है। तरह के तरीके get, filter, exclude, order_byमान्य हैं और अपडेट की गई स्थिति के साथ एक नई क्वेरी बिल्डर लौट आते हैं। ये ऑब्जेक्ट एक पुनरावृत्त इंटरफ़ेस लागू करते हैं, ताकि जब आप उन पर पुनरावृति करते हैं, तो क्वेरी निष्पादित की जाती है और आपको अब तक निर्मित क्वेरी के परिणाम मिलते हैं। यद्यपि यह उदाहरण Django से लिया गया है, आप कई अन्य ORM में समान संरचना देखेंगे।


मुझे यह नहीं दिखाई देता है कि इससे पुराने_पुरुषों = क्रय.चयन ("तिथि> तिथि.योदय () और प्रकार = खरीद.प्रक्रिया और स्थिति! = खरीद.आयात) जैसी किसी चीज का क्या फायदा है; आप SQL ANDs और ORs को विधि ANDs और ORs में बनाकर कुछ भी जटिलता या अमूर्तता को कम नहीं कर रहे हैं- आप सिर्फ ANDs और ORs का प्रतिनिधित्व बदल रहे हैं, है ना?
कीथ पामर जूनियर

4
असल में नहीं। आप एसक्यूएल को दूर कर रहे हैं, जो आपको बहुत सारे फायदे खरीदता है। सबसे पहले, आप इंजेक्शन से बचें। उसके बाद, आप SQL बोली के कुछ भिन्न संस्करणों के बारे में चिंता किए बिना अंतर्निहित डेटाबेस को बदल सकते हैं, क्योंकि ORM आपके लिए इसे संभालता है। कई मामलों में, आप बिना सूचना के भी NoSQL बैकएंड डाल सकते हैं। तीसरा, ये क्वेरी बिल्डर्स ऐसी वस्तुएं हैं जिन्हें आप किसी भी चीज की तरह पास कर सकते हैं। इसका मतलब है कि आपका मॉडल आधा क्वेरी का निर्माण कर सकता है (उदाहरण के लिए आपके पास सबसे सामान्य मामलों के लिए कुछ तरीके हो सकते हैं) और फिर इसे संभालने के लिए नियंत्रक में परिष्कृत किया जा सकता है ..
Andrea

2
... सबसे विशिष्ट मामले। एक विशिष्ट उदाहरण Django में मॉडल के लिए एक डिफ़ॉल्ट आदेश को परिभाषित कर रहा है। जब तक आप अन्यथा निर्दिष्ट नहीं करते तब तक सभी क्वेरी परिणाम उस आदेश का पालन करेंगे। चौथा, यदि आपको कभी भी अपने डेटा को प्रदर्शन के कारण से अलग करने की आवश्यकता होती है, तो आपको केवल अपने सभी प्रश्नों को लिखने के बजाय ORM को ट्विस्ट करना होगा।
एंड्रिया

+1 गतिशील क्वेरी भाषाओं के लिए जैसे कि उल्लेख किया गया है, और LINQ।
इवान प्लाइस

2

एक तीसरा तरीका है।

आपका विशिष्ट उदाहरण आवश्यक तरीकों की संख्या बढ़ने के साथ-साथ आवश्यक तरीकों की संख्या में घातीय वृद्धि को प्रदर्शित करता है: हम उन्नत क्वेरी की पेशकश करने की क्षमता चाहते हैं, प्रत्येक क्वेरी सुविधा को मिलाते हुए ... यदि हम ऐसा करते हैं कि तरीकों को जोड़कर, हमारे पास एक विधि है बुनियादी क्वेरी, दो अगर हम एक वैकल्पिक सुविधा जोड़ते हैं, चार अगर हम दो जोड़ते हैं, आठ अगर हम तीन सुविधाएँ जोड़ते हैं, तो 2 ^ n अगर हम n सुविधाएँ जोड़ते हैं।

यह स्पष्ट रूप से तीन या चार सुविधाओं से परे अकल्पनीय है, और बहुत निकटता से संबंधित कोड की एक बुरी गंध है जो विधियों के बीच लगभग कॉपी-पेस्ट है।

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

if (dataObject.getStartDate() != null) {
    query += " AND (date BETWEEN ? AND ?) "
}

... और जहां पैरामीटर क्वेरी में जोड़े जाते हैं:

if (dataObject.getStartDate() != null) {
    preparedStatement.setTime(dataObject.getStartDate());
    preparedStatement.setTime(dataObject.getEndDate());
}

यह दृष्टिकोण रैखिक कोड के विकास के लिए अनुमति देता है क्योंकि सुविधाओं को जोड़ा जाता है, बिना मनमाने ढंग से, असम्बद्ध प्रश्नों की अनुमति के।


0

मुझे लगता है कि सामान्य सहमति MVC में आपके मॉडल में जितना संभव हो उतना डेटा एक्सेस रखने के लिए है। अन्य डिज़ाइन प्रिक्यूज़र में से एक आपके कुछ अधिक सामान्य प्रश्नों (जो कि सीधे आपके मॉडल से संबंधित नहीं हैं) को उच्च, अधिक सार स्तर पर ले जाना है, जहाँ आप इसे अन्य मॉडलों द्वारा भी उपयोग करने की अनुमति दे सकते हैं। (RoR में, हमारे पास फ्रेमवर्क नाम की कोई चीज है) एक और बात यह भी है कि आपको विचार करना होगा और वह आपके कोड की मुख्यता है। जैसे-जैसे आपकी परियोजना बढ़ती है, यदि आपके पास नियंत्रकों में डेटा एक्सेस है, तो इसे नीचे ट्रैक करना तेजी से मुश्किल हो जाएगा (हम वर्तमान में एक बड़ी परियोजना में इस मुद्दे का सामना कर रहे हैं) मॉडल, हालांकि तरीकों के साथ बरबाद किसी भी नियंत्रक के लिए संपर्क का एक बिंदु प्रदान करते हैं, जो टेबलों से क्वेरिंग समाप्त हो सकती है। (इससे कोड का पुन: उपयोग भी हो सकता है जो लाभकारी है)


1
आप किस बारे में बात कर रहे हैं इसका उदाहरण ...?
कीथ पामर जूनियर

0

आपकी सेवा परत इंटरफ़ेस में कई विधियाँ हो सकती हैं, लेकिन डेटाबेस में कॉल सिर्फ एक हो सकती है।

एक डेटाबेस में 4 प्रमुख ऑपरेशन होते हैं

  • सम्मिलित करें
  • अपडेट करें
  • हटाएं
  • सवाल

एक अन्य वैकल्पिक विधि कुछ डेटाबेस ऑपरेशन को निष्पादित करने के लिए हो सकती है जो मूल DB ऑपरेशन के अंतर्गत नहीं आते हैं। चलिए उस Execute को कॉल करते हैं।

इन्सर्ट और अपडेट्स को सेव नामक एक ऑपरेशन में जोड़ा जा सकता है।

आपके तरीकों में से अधिकांश क्वेरी के हैं। तो आप तात्कालिक जरूरतों को पूरा करने के लिए एक सामान्य इंटरफ़ेस बना सकते हैं। यहाँ एक नमूना सामान्य इंटरफ़ेस है:

 public interface IDALService
    {
        DataTransferObject<T> Save<T>(DataTransferObject<T> Dto) where T : IPOCO;
        DataTransferObject<T> Search<T>(DataTransferObject<T> Dto) where T: IPOCO;
        DataTransferObject<T> Delete<T>(DataTransferObject<T> Dto) where T : IPOCO;
        DataTransferObject<T> Execute<T>(DataTransferObject<T> Dto) where T : IPOCO;
    }

डेटा ट्रांसफर ऑब्जेक्ट जेनेरिक है और इसमें आपके सभी फ़िल्टर, पैरामीटर, सॉर्टिंग आदि समाहित होंगे। डेटा लेयर को पार्स करने और निकालने के लिए जिम्मेदार होगा और संग्रहीत कार्यविधियों, पैरामीटराइज्ड sql, linq इत्यादि के माध्यम से डेटाबेस में ऑपरेशन सेट करेगा। इसलिए, SQL परतों के बीच पारित नहीं होता है। यह आमतौर पर एक ORM करता है, लेकिन आप अपना रोल कर सकते हैं और अपनी खुद की मैपिंग कर सकते हैं।

तो, आपके मामले में आपके पास विजेट्स हैं। विजेट IPOCO इंटरफ़ेस को लागू करेगा।

तो, आपकी सेवा परत मॉडल में होगी getList().

getListमें tranforming को संभालने के लिए एक मैपिंग परत की आवश्यकता होगी

Search<Widget>(DataTransferObject<Widget> Dto)

और इसके विपरीत। जैसा कि अन्य ने उल्लेख किया है, कभी-कभी यह एक ओआरएम के माध्यम से किया जाता है, लेकिन अंततः आप बहुत सारे बॉयलरप्लेट कोड के साथ समाप्त होते हैं, खासकर यदि आपके पास 100 टेबल हैं। ORM जादुई रूप से परिमाणित SQL बनाता है और डेटाबेस के विरुद्ध चलाता है। यदि स्वयं को रोल करते हैं, तो इसके अलावा डेटा लेयर में, एसपी, लाइनक आदि को सेट करने के लिए मैपर्स की आवश्यकता होगी (मूल रूप से डेटाबेस में जाने वाला एसक्यूएल)।

जैसा कि पहले उल्लेख किया गया है, डीटीओ एक रचना द्वारा बनाई गई वस्तु है। शायद इसके भीतर निहित वस्तुओं में से एक QueryParameters नामक एक वस्तु है। ये क्वेरी के लिए सभी पैरामीटर होंगे जो क्वेरी द्वारा सेट और उपयोग किए जाएंगे। एक अन्य वस्तु क्वेरी, अपडेट, एक्सट से लौटी वस्तुओं की सूची होगी। यह पेलोड है। यदि यह स्थिति पेलोड विजेट सूची की सूची होगी।

तो, मूल रणनीति है:

  • सेवा परत कॉल
  • किसी प्रकार के रिपॉजिटरी / मैपिंग का उपयोग करके डाटाबेस में ट्रांसफ़र सर्विस लेयर कॉल
  • डेटाबेस कॉल

आपके मामले में मुझे लगता है कि मॉडल के कई तरीके हो सकते हैं, लेकिन आशा है कि आप चाहते हैं कि डेटाबेस कॉल जेनेरिक हो। आप अभी भी बहुत सारे बॉयलरप्लेट मैपिंग कोड (विशेषकर एसपी के साथ) या जादुई ओआरएम कोड के साथ समाप्त होते हैं जो गतिशील रूप से आपके लिए परिमाणित एसक्यूएल बना रहा है।

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