Skip to content

Commit 865c159

Browse files
beikovvladmihalcea
authored andcommitted
HHH-11544 - Joins over type variable defined relations is non-deterministic
Fix single table inheritance issues and improve polymorphic join condition
1 parent fb0957f commit 865c159

File tree

17 files changed

+1247
-39
lines changed

17 files changed

+1247
-39
lines changed

hibernate-core/src/main/java/org/hibernate/engine/internal/JoinSequence.java

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,28 @@ public JoinSequence addJoin(
129129
String alias,
130130
JoinType joinType,
131131
String[] referencingKey) throws MappingException {
132-
joins.add( new Join( factory, associationType, alias, joinType, referencingKey ) );
132+
joins.add( new Join( factory, associationType, alias, joinType, new String[][] { referencingKey } ) );
133+
return this;
134+
}
135+
136+
/**
137+
* Add a join to this sequence
138+
*
139+
* @param associationType The type of the association representing the join
140+
* @param alias The RHS alias for the join
141+
* @param joinType The type of join (INNER, etc)
142+
* @param referencingKeys The LHS columns for the join condition
143+
*
144+
* @return The Join memento
145+
*
146+
* @throws MappingException Generally indicates a problem resolving the associationType to a {@link Joinable}
147+
*/
148+
public JoinSequence addJoin(
149+
AssociationType associationType,
150+
String alias,
151+
JoinType joinType,
152+
String[][] referencingKeys) throws MappingException {
153+
joins.add( new Join( factory, associationType, alias, joinType, referencingKeys ) );
133154
return this;
134155
}
135156

@@ -242,8 +263,7 @@ else if ( needsTableGroupJoin( joins, withClauseFragment ) ) {
242263
join.getAlias(),
243264
join.getLHSColumns(),
244265
JoinHelper.getRHSColumnNames( join.getAssociationType(), factory ),
245-
join.joinType,
246-
""
266+
join.joinType
247267
);
248268
}
249269
addSubclassJoins(
@@ -263,17 +283,28 @@ else if ( needsTableGroupJoin( joins, withClauseFragment ) ) {
263283
joinFragment.addFromFragmentString( " on " );
264284

265285
final String rhsAlias = first.getAlias();
266-
final String[] lhsColumns = first.getLHSColumns();
286+
final String[][] lhsColumns = first.getLHSColumns();
267287
final String[] rhsColumns = JoinHelper.getRHSColumnNames( first.getAssociationType(), factory );
268-
for ( int j=0; j < lhsColumns.length; j++) {
269-
joinFragment.addFromFragmentString( lhsColumns[j] );
270-
joinFragment.addFromFragmentString( "=" );
271-
joinFragment.addFromFragmentString( rhsAlias );
272-
joinFragment.addFromFragmentString( "." );
273-
joinFragment.addFromFragmentString( rhsColumns[j] );
274-
if ( j < lhsColumns.length - 1 ) {
275-
joinFragment.addFromFragmentString( " and " );
288+
if ( lhsColumns.length > 1 ) {
289+
joinFragment.addFromFragmentString( "(" );
290+
}
291+
for ( int i = 0; i < lhsColumns.length; i++ ) {
292+
for ( int j = 0; j < lhsColumns[i].length; j++ ) {
293+
joinFragment.addFromFragmentString( lhsColumns[i][j] );
294+
joinFragment.addFromFragmentString( "=" );
295+
joinFragment.addFromFragmentString( rhsAlias );
296+
joinFragment.addFromFragmentString( "." );
297+
joinFragment.addFromFragmentString( rhsColumns[j] );
298+
if ( j < lhsColumns[i].length - 1 ) {
299+
joinFragment.addFromFragmentString( " and " );
300+
}
276301
}
302+
if ( i < lhsColumns.length - 1 ) {
303+
joinFragment.addFromFragmentString( " or " );
304+
}
305+
}
306+
if ( lhsColumns.length > 1 ) {
307+
joinFragment.addFromFragmentString( ")" );
277308
}
278309

279310
joinFragment.addFromFragmentString( " and " );
@@ -568,14 +599,14 @@ public static final class Join {
568599
private final Joinable joinable;
569600
private final JoinType joinType;
570601
private final String alias;
571-
private final String[] lhsColumns;
602+
private final String[][] lhsColumns;
572603

573604
Join(
574605
SessionFactoryImplementor factory,
575606
AssociationType associationType,
576607
String alias,
577608
JoinType joinType,
578-
String[] lhsColumns) throws MappingException {
609+
String[][] lhsColumns) throws MappingException {
579610
this.associationType = associationType;
580611
this.joinable = associationType.getAssociatedJoinable( factory );
581612
this.alias = alias;
@@ -599,7 +630,7 @@ public JoinType getJoinType() {
599630
return joinType;
600631
}
601632

602-
public String[] getLHSColumns() {
633+
public String[][] getLHSColumns() {
603634
return lhsColumns;
604635
}
605636

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/DotNode.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
*/
77
package org.hibernate.hql.internal.ast.tree;
88

9+
import java.util.ArrayList;
10+
import java.util.Arrays;
11+
import java.util.List;
12+
import java.util.Set;
13+
914
import org.hibernate.QueryException;
1015
import org.hibernate.engine.internal.JoinSequence;
1116
import org.hibernate.hql.internal.CollectionProperties;
@@ -16,6 +21,7 @@
1621
import org.hibernate.internal.CoreMessageLogger;
1722
import org.hibernate.internal.log.DeprecationLogger;
1823
import org.hibernate.internal.util.StringHelper;
24+
import org.hibernate.internal.util.collections.ArrayHelper;
1925
import org.hibernate.persister.collection.QueryableCollection;
2026
import org.hibernate.persister.entity.AbstractEntityPersister;
2127
import org.hibernate.persister.entity.EntityPersister;
@@ -483,11 +489,6 @@ private void dereferenceEntityJoin(String classAlias, EntityType propertyType, b
483489
boolean useFoundFromElement = found && canReuse( classAlias, elem );
484490

485491
if ( !useFoundFromElement ) {
486-
// If this is an implied join in a from element, then use the impled join type which is part of the
487-
// tree parser's state (set by the gramamar actions).
488-
JoinSequence joinSequence = getSessionFactoryHelper()
489-
.createJoinSequence( impliedJoin, propertyType, tableAlias, joinType, joinColumns );
490-
491492
// If the lhs of the join is a "component join", we need to go back to the
492493
// first non-component-join as the origin to properly link aliases and
493494
// join columns
@@ -501,6 +502,27 @@ private void dereferenceEntityJoin(String classAlias, EntityType propertyType, b
501502

502503
String role = lhsFromElement.getClassName() + "." + propertyName;
503504

505+
JoinSequence joinSequence;
506+
507+
if ( joinColumns.length == 0 ) {
508+
// When no columns are available, this is a special join that involves multiple subtypes
509+
String lhsTableAlias = getLhs().getFromElement().getTableAlias();
510+
511+
AbstractEntityPersister persister = (AbstractEntityPersister) lhsFromElement.getEntityPersister();
512+
513+
String[][] polyJoinColumns = persister.getPolymorphicJoinColumns(lhsTableAlias, propertyPath);
514+
515+
// Special join sequence that uses the poly join columns
516+
joinSequence = getSessionFactoryHelper()
517+
.createJoinSequence( impliedJoin, propertyType, tableAlias, joinType, polyJoinColumns );
518+
}
519+
else {
520+
// If this is an implied join in a from element, then use the implied join type which is part of the
521+
// tree parser's state (set by the grammar actions).
522+
joinSequence = getSessionFactoryHelper()
523+
.createJoinSequence( impliedJoin, propertyType, tableAlias, joinType, joinColumns );
524+
}
525+
504526
FromElementFactory factory = new FromElementFactory(
505527
currentFromClause,
506528
lhsFromElement,

hibernate-core/src/main/java/org/hibernate/hql/internal/ast/util/SessionFactoryHelper.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,24 @@ public JoinSequence createJoinSequence(boolean implicit, AssociationType associa
273273
return joinSequence;
274274
}
275275

276+
/**
277+
* Generate a join sequence representing the given association type.
278+
*
279+
* @param implicit Should implicit joins (theta-style) or explicit joins (ANSI-style) be rendered
280+
* @param associationType The type representing the thing to be joined into.
281+
* @param tableAlias The table alias to use in qualifying the join conditions
282+
* @param joinType The type of join to render (inner, outer, etc); see {@link org.hibernate.sql.JoinFragment}
283+
* @param columns The columns making up the condition of the join.
284+
*
285+
* @return The generated join sequence.
286+
*/
287+
public JoinSequence createJoinSequence(boolean implicit, AssociationType associationType, String tableAlias, JoinType joinType, String[][] columns) {
288+
JoinSequence joinSequence = createJoinSequence();
289+
joinSequence.setUseThetaStyle( implicit ); // Implicit joins use theta style (WHERE pk = fk), explicit joins use JOIN (afterQuery from)
290+
joinSequence.addJoin( associationType, tableAlias, joinType, columns );
291+
return joinSequence;
292+
}
293+
276294
/**
277295
* Create a join sequence rooted at the given collection.
278296
*

hibernate-core/src/main/java/org/hibernate/loader/plan/exec/internal/LoadQueryJoinAndFetchProcessor.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.hibernate.persister.collection.CollectionPersister;
3939
import org.hibernate.persister.collection.CollectionPropertyNames;
4040
import org.hibernate.persister.collection.QueryableCollection;
41+
import org.hibernate.persister.entity.AbstractEntityPersister;
4142
import org.hibernate.persister.entity.EntityPersister;
4243
import org.hibernate.persister.entity.Joinable;
4344
import org.hibernate.persister.entity.OuterJoinLoadable;
@@ -257,14 +258,35 @@ else if ( !StringHelper.isEmpty( joinConditions ) ) {
257258
getJoinedAssociationTypeOrNull( join )
258259
);
259260

260-
joinFragment.addJoin(
261-
joinable.getTableName(),
262-
rhsTableAlias,
263-
join.resolveAliasedLeftHandSideJoinConditionColumns( lhsTableAlias ),
264-
join.resolveNonAliasedRightHandSideJoinConditionColumns(),
265-
join.isRightHandSideRequired() ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN,
266-
additionalJoinConditions
267-
);
261+
String[] joinColumns = join.resolveAliasedLeftHandSideJoinConditionColumns( lhsTableAlias );
262+
if ( joinColumns.length == 0 ) {
263+
// When no columns are available, this is a special join that involves multiple subtypes
264+
AbstractEntityPersister persister = (AbstractEntityPersister) ( (EntityQuerySpace) join.getLeftHandSide() ).getEntityPersister();
265+
266+
String[][] polyJoinColumns = persister.getPolymorphicJoinColumns(
267+
lhsTableAlias,
268+
( (JoinDefinedByMetadata) join ).getJoinedPropertyName()
269+
);
270+
271+
joinFragment.addJoin(
272+
joinable.getTableName(),
273+
rhsTableAlias,
274+
polyJoinColumns,
275+
join.resolveNonAliasedRightHandSideJoinConditionColumns(),
276+
join.isRightHandSideRequired() ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN,
277+
additionalJoinConditions
278+
);
279+
}
280+
else {
281+
joinFragment.addJoin(
282+
joinable.getTableName(),
283+
rhsTableAlias,
284+
joinColumns,
285+
join.resolveNonAliasedRightHandSideJoinConditionColumns(),
286+
join.isRightHandSideRequired() ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN,
287+
additionalJoinConditions
288+
);
289+
}
268290
joinFragment.addJoins(
269291
joinable.fromJoinFragment( rhsTableAlias, false, true ),
270292
joinable.whereJoinFragment( rhsTableAlias, false, true )

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5492,6 +5492,37 @@ public Iterable<AttributeDefinition> getAttributes() {
54925492
return attributeDefinitions;
54935493
}
54945494

5495+
public String[][] getPolymorphicJoinColumns(String lhsTableAlias, String propertyPath) {
5496+
Set<String> subclassEntityNames = (Set<String>) getEntityMetamodel()
5497+
.getSubclassEntityNames();
5498+
// We will collect all the join columns from the LHS subtypes here
5499+
List<String[]> polymorphicJoinColumns = new ArrayList<>( subclassEntityNames.size() );
5500+
5501+
String[] joinColumns = null;
5502+
5503+
OUTER:
5504+
for ( String subclassEntityName : subclassEntityNames ) {
5505+
AbstractEntityPersister subclassPersister = (AbstractEntityPersister) getFactory()
5506+
.getMetamodel()
5507+
.entityPersister( subclassEntityName );
5508+
joinColumns = subclassPersister.toColumns( lhsTableAlias, propertyPath );
5509+
5510+
if ( joinColumns.length == 0 ) {
5511+
// The subtype does not have a "concrete" mapping for the property path
5512+
continue;
5513+
}
5514+
5515+
// Check for duplicates like this since we will mostly have just a few candidates
5516+
for ( String[] existingColumns : polymorphicJoinColumns ) {
5517+
if ( Arrays.deepEquals( existingColumns, joinColumns ) ) {
5518+
continue OUTER;
5519+
}
5520+
}
5521+
polymorphicJoinColumns.add( joinColumns );
5522+
}
5523+
5524+
return ArrayHelper.to2DStringArray( polymorphicJoinColumns );
5525+
}
54955526

54965527
private void prepareEntityIdentifierDefinition() {
54975528
if ( entityIdentifierDefinition != null ) {

0 commit comments

Comments
 (0)