Extra LEFT JOIN generated with @ManyToOne and @JoinTable when projecting on main entity id
Activity

Jan-Willem Gmelig Meyling November 21, 2019 at 9:39 PM
Using the ground work from , I was able to finish my patch in https://github.com/hibernate/hibernate-orm/pull/3109

Jan-Willem Gmelig Meyling August 9, 2018 at 3:12 PM
Definitely willing to do so, but I just don't think it can be done without severe changes to 5.x. But what's your opinion about the current behaviour of the JoinedSubclassEntityPersister in this? Because I do have some worries about the discrepancy between the two persister implementations regarding this aspect.
Steve Ebersole August 9, 2018 at 2:17 PM
IMO this is something that should be targeted for 6. As mentions previous and current versions of Hibernate are simply not set up to easily support this. So unless someone else (?) is willing to work on it, the effort to make this work is simply not justifiable for 5.

Jan-Willem Gmelig Meyling August 9, 2018 at 1:42 AM
Christian and I had a brief chat about this issue. Christian remembered that secondary tables etc. are always joined, I remembered observering something different for my queries, in particular with joined inheritance. So I adjusted his test case to use joined inheritance and added a subclass to the hierarchy so it wouldn't fallback to using single table inheritance anyway. And as a matter of fact: the test then passes. When using the JoinedSubclassPersister, the join is not generated. Which lead to the question why this happened, and whether this "optimisation" could be ported to the SingleTableEntityPersister too.
The primary difference turned out to be in the implementation of the isClassOrSuperclassTable methods in the two entity persisters. For the SingleTableEntityPersister this method returns true for the JoinTable of the association, yet for the JoinedSubclassEntityPersister it returns false. This is caused by how the value for isClassOrSuperclassTable is set in the constructor.
For the SingleTableEntityPersister we find:
For the JoinedSubclassEntityPersister we find:
This value is then used in AbstractEntityPersister's determineSubclassTableJoinType, where it then creates a join for the SingleTableEntityPersister and omits a join for the JoinedSubclassEntityPersister.
In general I have the following findings:
The implementation of isClassOrSuperclassTable is correct for the JoinedSubclassEntityPersister, but not for the SingleTableEntityPersister (from a naming perspective)
(Most) uses of isClassOrSuperclassTable should however probably be replaced with a currently non-existent isClassOrSuperclassJoin (from a functional perspective), as all properties from the concrete and its supertypes are currently expected to be present in the tables in the from clause created by EntityPersister#fromJoinFragment
While isClassOrSuperclassTable is false for isClassOrSuperclassJoin properties in the Join JoinedSubclassEntityPersister, this is usually not a problem, because getSubclassPropertyDeclarer will return Declarer.SUBCLASS for this property, eventually causing the subtables (including the joined tables) to be joined in the join fragment anyway.
Although it is perfectly valid for the JoinedSubclassEntityPersister to not join the subclass tables (there is no need to), I find it very peculiar that it doesn't join the JoinTable by default and that we haven't seen issues with this.
Indeed applying the same approach for isClassOrSuperclassJoin from the JoinedSubclassEntityPErsister to the SingleTableEntityPersister (making isClassOrSuperclassTable return false instead, effectively marking the property as a subclass property which is then only joined only when any "subclass" property or the entire entity is selected) , the test suite then fails with a couple of test failures (referencing a non present column from ommitting the join)
This makes me worry whether these failing tests are actually valid scenarios for joined inheritance as well (which in that case would be broken rather than "optimized").
I don't deem it possible to filter the joined tables in this from clause based on the projected columns, and thus properly fix/optimize this, for at least 5.x. I have no idea what the situation would be in 6.0.
Would love to hear some feedback on my findings. Maybe I am overlooking something . My work in progress can be found at: https://github.com/JWGmeligMeyling/hibernate-orm/commit/78b02ec25c8d7f80d945040300b6fdbdfa40389b

Christian Beikov August 8, 2018 at 8:56 PM
Let's see what comes up with. He just found out that the joined persister behaves differently. Maybe it's just a small configuration issue.
I have a simple Hibernate @ManyToOne mapping between two entities, using an association table thanks to annotation @JoinTable. Here is my mapping:
and
When I query on Customer entity, I always get an extra left join to the join table. For example, a HQL query such as SELECT c.id from Customer c generates the following SQL query:
I understand the left join is required when the full Customer entity is loaded, to know whether or not Address is null, but the LEFT JOIN seems totally useless in case of a projection, when just the id is selected in my example. This can be a performance issues for large tables.
Full source code to reproduce is available here: https://github.com/ndionisi/hibernate-extra-left-join
I reproduce it with Hibernate 5.1, 5.2 and 5.3.