[PR #970] [CLOSED] [DDC-357] Effective toOne joins #8977

Open
opened 2026-01-22 16:02:35 +01:00 by admin · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/doctrine/orm/pull/970
Author: @fprochazka
Created: 3/5/2014
Status: Closed

Base: masterHead: feature/back-reference-proxy-joins


📝 Commits (6)

  • 240a19b Effective querying of toOne relations
  • 8112d34 fixed coding style
  • e70a070 Fixed autojoining subclass relations
  • 7ef74fe dead code
  • 1e5e3d1 Fixed hydrating association of inheritance subclasses
  • 41cc6ad Fixed syntax errors

📊 Changes

13 files changed (+374 additions, -136 deletions)

View changed files

📝 lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php (+12 -1)
📝 lib/Doctrine/ORM/Query.php (+5 -0)
📝 lib/Doctrine/ORM/Query/ResultSetMapping.php (+9 -1)
📝 lib/Doctrine/ORM/Query/SqlWalker.php (+298 -106)
📝 lib/Doctrine/ORM/UnitOfWork.php (+8 -11)
📝 tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php (+2 -2)
📝 tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php (+20 -3)
📝 tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php (+6 -4)
📝 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC448Test.php (+1 -1)
📝 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC633Test.php (+2 -2)
📝 tests/Doctrine/Tests/ORM/Query/CustomTreeWalkersTest.php (+3 -3)
📝 tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php (+5 -2)
📝 tests/Doctrine/Tests/OrmFunctionalTestCase.php (+3 -0)

📄 Description

What is this?

I've kind of rewriten querying of toOne relations. It is more data and query-count effective.

How?

Let's demonstrate it on CmsUser and CmsAddress from tests models. Let's solve behaviour for toOne relations that are not mentioned in the query.

SELECT u FROM CmsUser u

lazy + mapped by side

Already implemented, result is that CmsAddress would be proxy.

lazy + inverse side

CmsUser has CmsAddress relation that is mapped and owned by CmsAddress entity.

What has to happen? The identifier of CmsAddress cannot be loaded from users table nad has to be added automatic join for the table. Because it's lazy it will be hydrated as proxy, becase that is exactly what I've asked for.

If it would have been eagerly loaded, It would create 1+N queries problem that I'm trying to avoid with this. I have the relation as lazy, if I knew I would have needed it and wanned to optimized, I'd join it, but I didn't.

Result is therefore CmsUser entity + CmsAddress proxy

eager - both inverse and mapped by sides

The appropriate query component is generated with autojoin and auto selecting of the entity.

If it is self-referencing, the auto join is not generated becase it would cause infinite recursion.

Why?

I've given this a lot of thought and tested it on our not-so-small application. We have unfortunately lot of entitiy relations that are mapped on the inverse side than we need to select, which is effectively killing performace DDC-357

I would have to go and list all the entities as partials to save performace creating such monsters as this

$builder = $repository->createQueryBuilder("o")
    ->leftJoin("o.voucher", "vu")->addSelect("partial vu.{id}")
    ->leftJoin("o.address", "a")->addSelect("a")
    ->leftJoin("o.restaurant", "r")->addSelect("partial r.{id, name}")
    ->leftJoin("o.payment", "p")->addSelect("partial p.{id}")
    ->leftJoin("o.rating", "rat")->addSelect("partial rat.{id}")
    ->leftJoin("r.settings", "rs")->addSelect("partial rs.{id}")
    ->leftJoin("r.address", "ra")->addSelect("ra")
    ->leftJoin("r.position", "rp")->addSelect("partial rp.{id}");
# plus about five more just to make save performace

We all know that hydrating a large result set is a bottleneck and if I say the relation is lazy and I'm not joining it I really don't want it to be joined with all it's data!

Now imagine I just want to select few orders and render some data on the page.. I have tens of queries like this just because I have to. This is wrong that the ORM is tripping my feet like this.

What now?

I know I have to solve theese:

  • more refactoring?
  • more tests
  • what to do with issue tests that now have changed behaviour?

Any suggestions? Let's have a reasonable discussion, please don't just close this, I've put a lot of effort into this.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/doctrine/orm/pull/970 **Author:** [@fprochazka](https://github.com/fprochazka) **Created:** 3/5/2014 **Status:** ❌ Closed **Base:** `master` ← **Head:** `feature/back-reference-proxy-joins` --- ### 📝 Commits (6) - [`240a19b`](https://github.com/doctrine/orm/commit/240a19b2ec3f2ec5a9052f2fb58d6fc68e299487) Effective querying of toOne relations - [`8112d34`](https://github.com/doctrine/orm/commit/8112d34a8b8e4b048e4e70c8c443ff0cc55825c6) fixed coding style - [`e70a070`](https://github.com/doctrine/orm/commit/e70a07038762a687ac197653dc4df37df00bbac1) Fixed autojoining subclass relations - [`7ef74fe`](https://github.com/doctrine/orm/commit/7ef74fe7009bdfb23ebac965e36c2ec1368ee38e) dead code - [`1e5e3d1`](https://github.com/doctrine/orm/commit/1e5e3d14a3196189de72b7fc638b7197de99cf74) Fixed hydrating association of inheritance subclasses - [`41cc6ad`](https://github.com/doctrine/orm/commit/41cc6ad6f344140093423b56568024fe72bc2df5) Fixed syntax errors ### 📊 Changes **13 files changed** (+374 additions, -136 deletions) <details> <summary>View changed files</summary> 📝 `lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php` (+12 -1) 📝 `lib/Doctrine/ORM/Query.php` (+5 -0) 📝 `lib/Doctrine/ORM/Query/ResultSetMapping.php` (+9 -1) 📝 `lib/Doctrine/ORM/Query/SqlWalker.php` (+298 -106) 📝 `lib/Doctrine/ORM/UnitOfWork.php` (+8 -11) 📝 `tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php` (+2 -2) 📝 `tests/Doctrine/Tests/ORM/Functional/OneToOneBidirectionalAssociationTest.php` (+20 -3) 📝 `tests/Doctrine/Tests/ORM/Functional/PostLoadEventTest.php` (+6 -4) 📝 `tests/Doctrine/Tests/ORM/Functional/Ticket/DDC448Test.php` (+1 -1) 📝 `tests/Doctrine/Tests/ORM/Functional/Ticket/DDC633Test.php` (+2 -2) 📝 `tests/Doctrine/Tests/ORM/Query/CustomTreeWalkersTest.php` (+3 -3) 📝 `tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php` (+5 -2) 📝 `tests/Doctrine/Tests/OrmFunctionalTestCase.php` (+3 -0) </details> ### 📄 Description # What is this? I've kind of rewriten querying of toOne relations. It is more data and query-count effective. # How? Let's demonstrate it on `CmsUser` and `CmsAddress` from tests models. Let's solve behaviour for toOne relations that are not mentioned in the query. ``` sql SELECT u FROM CmsUser u ``` ## lazy + mapped by side Already implemented, result is that CmsAddress would be proxy. ## lazy + inverse side `CmsUser` has `CmsAddress` relation that is mapped and owned by `CmsAddress` entity. What has to happen? The identifier of `CmsAddress` cannot be loaded from users table nad has to be added automatic join for the table. Because it's lazy it will be hydrated as proxy, becase that is exactly what I've asked for. If it would have been eagerly loaded, It would create 1+N queries problem that I'm trying to avoid with this. I have the relation as lazy, if I knew I would have needed it and wanned to optimized, I'd join it, but I didn't. Result is therefore `CmsUser` entity + `CmsAddress` proxy ## eager - both inverse and mapped by sides The appropriate query component is generated with autojoin and auto selecting of the entity. If it is self-referencing, the auto join is not generated becase it would cause infinite recursion. # Why? I've given this a lot of thought and tested it on our not-so-small application. We have unfortunately lot of entitiy relations that are mapped on the inverse side than we need to select, which is effectively killing performace [DDC-357](http://www.doctrine-project.org/jira/browse/DDC-357) I would have to go and list all the entities as partials to save performace creating such monsters as this ``` php $builder = $repository->createQueryBuilder("o") ->leftJoin("o.voucher", "vu")->addSelect("partial vu.{id}") ->leftJoin("o.address", "a")->addSelect("a") ->leftJoin("o.restaurant", "r")->addSelect("partial r.{id, name}") ->leftJoin("o.payment", "p")->addSelect("partial p.{id}") ->leftJoin("o.rating", "rat")->addSelect("partial rat.{id}") ->leftJoin("r.settings", "rs")->addSelect("partial rs.{id}") ->leftJoin("r.address", "ra")->addSelect("ra") ->leftJoin("r.position", "rp")->addSelect("partial rp.{id}"); # plus about five more just to make save performace ``` We all know that hydrating a large result set is a bottleneck and if I say the relation is lazy and I'm not joining it I **really don't want it to be joined with all it's data**! Now imagine I just want to select few orders and render some data on the page.. I have tens of queries like this just because I have to. This is wrong that the ORM is tripping my feet like this. # What now? I know I have to solve theese: - [ ] more refactoring? - [ ] more tests - [ ] what to do with issue tests that now have changed behaviour? Any suggestions? Let's have a reasonable discussion, please don't just close this, I've put a lot of effort into this. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
admin added the pull-request label 2026-01-22 16:02:35 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#8977