2010-11-12 5 views
8

का उपयोग नहीं करेगा मेरे पास Car इकाई है जो एक इकाई Owner के साथ कई से एक रिश्ते के साथ है। यदि मैं सभी कारों का चयन करता हूं, तो सिद्धांत Car तालिका पर एक प्रश्न पूछता है, और बाद में प्रत्येक कार के लिए Owner तालिका पर एक क्वेरी करता है। तो Car और Owner तालिकाओं के बीच एक एकल जॉइन क्वेरी के बजाय एन कारें एन + 1 क्वेरी प्राप्त करती हैं।Doctrine2 कई-से-एक एसोसिएशन जॉइन क्वेरी

/** @Entity */ 
class Car { 

    /** @Id @Column(type="smallint") */ 
    private $id; 

    /** @ManyToOne(targetEntity="Owner", fetch="EAGER") 
     @JoinColumn(name="owner", referencedColumnName="id") */ 
    private $owner; 

    public function getId() { return $this->id; } 
    public function getOwner() { return $this->owner; } 
} 

/** @Entity */ 
class Owner { 

    /** @Id @Column(type="smallint") */ 
    private $id; 

    /** @Column(type="string") */ 
    private $name; 

    public function getName() { return $this->name; } 
} 

मैं उनके मालिकों के साथ कारों की सूची चाहते हैं, मुझे क्या करना:

$repo = $em->getRepository('Car'); 
$cars = $repo->findAll(); 

foreach($cars as $car) 
    echo 'Car no. ' . $car->getId() . 
     ' owned by ' . $car->getOwner()->getName() . '\n'; 

अब यह सब बहुत अच्छी तरह से तथ्य यह है कि से काम करता है, के अलावा

मेरे संस्थाओं इस प्रकार हैं सिद्धांत प्रत्येक कार के लिए एक प्रश्न जारी करता है।

SELECT * FROM Car; 
SELECT * FROM Owner WHERE id = 1; 
SELECT * FROM Owner WHERE id = 2; 
SELECT * FROM Owner WHERE id = 3; 
.... 
बेशक

मैं इस तरह देखने के लिए मेरी क्वेरी लॉग चाहते हैं:

SELECT * FROM Car JOIN Owner ON Car.owner = Owner.id; 

चाहे मैं fetch="EAGER" या fetch="LAZY" कोई फर्क नहीं पड़ता है, और मुझे शामिल हों के साथ एक कस्टम DQL क्वेरी बनाने भले ही दो इकाइयों के बीच, $car->getOwner() अभी भी सिद्धांत को डेटाबेस से पूछताछ करने का कारण बनता है (जब तक कि मैं ईगर का उपयोग नहीं करता, उस स्थिति में $repo->findAll() उन सभी का कारण बनता है)।

क्या मैं यहां बहुत थक गया हूं, और यह जिस तरह से काम करना है - या क्या इसके बजाय जॉइन क्वेरी करने के लिए सिद्धांत को मजबूर करने का एक चालाक तरीका है?

उत्तर

5

कम से कम 1.x सिद्धांत में यदि आप संबंधित वस्तुओं के लिए पूछना चाहते हैं, तो आपको डीक्यूएल का उपयोग करना होगा। आपके मामले के लिए, DQL क्वेरी कुछ इस तरह दिखेगा:

//Assuming $em is EntityManager 
$query = $em->createQuery('SELECT c, o FROM Car c JOIN c.owner o'); 
$cars = $query->execute(); 
+1

ऐसा लगता है कि वह भी 2. x में मामला है मैंने पूछताछ की कोशिश की थी 'कार सी जॉइन सी। जॉइनर ओ' से चुनें, जो अभी भी एन + 1 प्रश्न बना है, लेकिन आपका काम ठीक है। बहुत धन्यवाद! मुझे लगता है कि मुझे findXxx विधियों का उपयोग न करने के साथ बस रहना होगा। – Frode

+4

इस तरह की सुविधा विधियों के लिए, मैं एक "सेवा परत" या "भंडार" शैली वर्ग बनाने का सुझाव दूंगा जो मॉडलों को लोड करने जैसे कार्यों को संभालता है। इस तरह आप पूरे स्थान पर डीक्यूएल तर्क को डुप्लिकेट किए बिना क्वेरी को आसानी से पुन: उपयोग कर सकते हैं। –

1

आपकी क्वेरी ...

$car->getOwner() // "go and fetch this car's owner" 

... तो यह निश्चित रूप से क्वेरी कई बार जारी करेगा एक foreach पाश में है।

यदि आप इस से निपटने के लिए कस्टम डीक्यूएल लिख रहे हैं, तो $car->getOwner() इसमें बिल्कुल शामिल नहीं होना चाहिए। यह कार वर्ग का एक कार्य है। आपके द्वारा लिखे गए कस्टम डीक्यूएल आपके द्वारा बताए गए सटीक SQL क्वेरी की नकल करेंगे और आपके सहयोग को कुशलता से प्राप्त करेंगे।

+0

+1 आपके उत्तर के लिए धन्यवाद। जब तक मालिक के सदस्य को ईमानदारी से लोड किया जाता है, तब तक लूप के अंदर '$ कार-> getOwner() 'करने में कोई समस्या नहीं है। अगर यह आलसी fetching के साथ हुआ, मैं समझ जाएगा, लेकिन उत्सुक लोडिंग के साथ यह आदर्श नहीं है। डीक्यूएल के बारे में जानी हार्टिकैनन को मेरा जवाब देखें :-) – Frode

+0

@ फ्रोड: वास्तव में, अब अपने प्रश्न को बेहतर समझें। इस पर अभी तक नहीं आया है और आप के रूप में उत्सुक लोडिंग से एक ही दक्षता की उम्मीद होगी। ऐसा लगता है कि डीक्यूएल की आवश्यकता है। – Tom

4

पहले एक डीक्यूएल क्वेरी चलाएं जहां आप मालिक के साथ सभी कारों (डीक्यूएल जॉइन) में शामिल होते हैं। मालिक को select() में रखें।

// preload cars 
$qb = $em->createQueryBuilder() 
     ->select('car, owner') 
     ->from('\Entity\Car', 'car') 
     ->leftJoin('c.owner', 'owner'); 

    $query = $qb->getQuery(); 

    // the following seems not needed, but I think it depends on the conf 
    $query->setFetchMode("\Entity\Car", "owner", "EAGER"); 

    $query->execute(); //you don't have to use this result here, Doctrine will keep it 

सिद्धांत 2 फिर एक (तेज सामान्य रूप से के रूप में यह अभिलेखों की संख्या पर निर्भर करता है कम डाटाबेस प्रश्नों की आवश्यकता है) शामिल हों प्रदर्शन करेंगे। अब अपना foreach लॉन्च करें, सिद्धांत आंतरिक रूप से संस्थाएं पायेगा और जब आपको owner की आवश्यकता होगी तो यह एकल प्रश्न नहीं चलाएगा।

प्रश्नों की संख्या पर निगरानी पहले/प्रत्येक परिवर्तन के बाद (उदाहरण के लिए। सामान्य लॉग MySQL)