Primary key of owning side as a join column

后端 未结 4 697
借酒劲吻你
借酒劲吻你 2020-12-02 15:06

NOTE: Topic is lengthy but detailed and may come in handy if you use Doctrine2 and oneToOne relationships.

Recently I came across a problem in Doctrine:

相关标签:
4条回答
  • 2020-12-02 15:08

    My workaround was to turn the OneToOne relationship into a ManyToOne (with an associated ArrayCollection), with a custom getter and setter which only sets and returns the 0-th element of the collection:

    /**
     * Set pref
     *
     * @param \MyBundle\Entity\Pref $pref
     * @return Employee
     */
    public function setPref(\MyBundle\Entity\Pref $pref = null) {
        $this->pref[0] = $pref;
    
        return $this;
    }
    
    /**
     * Get pref
     *
     * @return \MyBundle\Entity\Pref 
     */
    public function getPref() {
        return $this->pref[0];
    }
    

    These methods can (seemingly) be used the same as the ones created by a OneToOne relationship.

    doctrine:generate:entities still creates the normal "add" and "remove" methods, but they can be ignored.

    Note: I only recently started using these and currently only to read from the database. I don't know of any side effects from this workaround. I'll update this answer if I notice any.

    This is admittedly an ugly hack. I hope Doctrine fixes this soon so I can go back to using proper OneToOne relationships.

    0 讨论(0)
  • 2020-12-02 15:16

    This is a known issue for OneToOne associations. There is a github discussion about this that is worth reading. A solution (pull request) was proposed and rejected.

    Recommendation

    Your question suggests the same solution proposed by the contributors to Doctrine: change the owning side of the relationship.

    Other Options Explored

    I had a similar problem with an entity called Version that had a OneToOne bidirectional relationship with Settings. Every time I queried Version (say for 10 specific version records), Doctrine would do additional queries for the joined Settings (as if it was Lazy Loading these entities). This happened, even though I did not reference Settings anywhere, e.g. $Version->getSettings()->getSomeProperty().

    Manual JOIN

    The only "solution" (hack) that works for me is to manually included a JOIN for this Settings entity every time I did a query on Version. But since I don't need the extra entity (in this case), that would still be a single extra unnecessary query, every time I query this table in different ways.

    Extra Lazy

    Based on other suggestions, I thought that if I explicitly specified this relationship as "extra lazy" it would work, e.g.

    /**
     * @ManyToMany(targetEntity="Settings", mappedBy="version", fetch="EXTRA_LAZY")
     */
    

    But this doesn't affect hydration. See the Doctrine Docs for more details about what EXTRA_LAZY does.

    Hydration Type: HYDRATE_ARRAY

    What helped in my case (when I didn't need an entity), was to specify that I wanted to fetch as an array (rather than object).

    $query = $queryBuilder->getQuery();
    $query->getResult(Query:HYDRATE_ARRAY);
    

    This returns an array, and as a result it doesn't lazy load the OneToOne associations. However, in other contexts where I need the entity object, I had to explicitly JOIN the entity (despite not wanting it).

    0 讨论(0)
  • 2020-12-02 15:20

    You could also force partial objects, to get rid off lazy-loading:

    use Doctrine\ORM\Query;
    
    //...
    $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
    
    0 讨论(0)
  • 2020-12-02 15:35

    I came across this same problem and remember that the symblog tutorial gave an example of how to reduce the lazy loading by explicitly add left joins on the tables that you do not need. It seems strange to include tables on a join when you do not even want that data at all, but this way you will reduce all of those extra queries down to 1 and it does run faster.

    Search for lazy loading - about 1/5 of the way down http://tutorial.symblog.co.uk/docs/customising-the-view-more-with-twig.html

    To fix this for the user/userdata issue try adding this to the user repository and use to whenever you need to get all users even if you do not want userdata. It can be further enhanced by selecting partial: ->select('partial p.{user_id,name,}')

       public function getAll($limit = 500) {
           $qb = $this->createQueryBuilder('u')
                ->select('u', 'd')
                ->leftJoin('p.userdata', 'd')
           if (false === is_null($limit))
               $qb->setMaxResults($limit);
         return $qb->getQuery()->getResult();
        }
    
    0 讨论(0)
提交回复
热议问题