HHH-16595 Optimize away nested table group joins when possible
This commit is contained in:
parent
627608e65c
commit
5ba12a66e6
|
@ -708,18 +708,19 @@ public class LoaderSelectBuilder {
|
|||
else {
|
||||
final TableGroup parentTableGroup = astCreationState.getFromClauseAccess().getTableGroup(
|
||||
parentNavigablePath );
|
||||
TableGroupJoin pluralTableGroupJoin = null;
|
||||
for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
|
||||
if ( nestedTableGroupJoin.getNavigablePath() == tableGroup.getNavigablePath() ) {
|
||||
pluralTableGroupJoin = nestedTableGroupJoin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup );
|
||||
assert pluralTableGroupJoin != null;
|
||||
|
||||
final TableGroupJoin joinForPredicate;
|
||||
if ( !tableGroup.getNestedTableGroupJoins().isEmpty() || tableGroup.getTableGroupJoins().isEmpty() ) {
|
||||
joinForPredicate = pluralTableGroupJoin;
|
||||
}
|
||||
else {
|
||||
joinForPredicate = tableGroup.getTableGroupJoins().get( tableGroup.getTableGroupJoins().size() - 1 );
|
||||
}
|
||||
|
||||
pluralAttributeMapping.applyBaseRestrictions(
|
||||
pluralTableGroupJoin::applyPredicate,
|
||||
joinForPredicate::applyPredicate,
|
||||
tableGroup,
|
||||
true,
|
||||
loadQueryInfluencers.getEnabledFilters(),
|
||||
|
@ -727,7 +728,7 @@ public class LoaderSelectBuilder {
|
|||
astCreationState
|
||||
);
|
||||
pluralAttributeMapping.applyBaseManyToManyRestrictions(
|
||||
pluralTableGroupJoin::applyPredicate,
|
||||
joinForPredicate::applyPredicate,
|
||||
tableGroup,
|
||||
true,
|
||||
loadQueryInfluencers.getEnabledFilters(),
|
||||
|
|
|
@ -637,6 +637,11 @@ public interface EntityMappingType
|
|||
getEntityPersister().applyBaseRestrictions( predicateConsumer, tableGroup, useQualifier, enabledFilters, treatAsDeclarations, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasWhereRestrictions() {
|
||||
return getEntityPersister().hasWhereRestrictions();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void applyWhereRestrictions(
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
|
|
|
@ -181,6 +181,11 @@ public interface PluralAttributeMapping
|
|||
getCollectionDescriptor().applyBaseManyToManyRestrictions( predicateConsumer, tableGroup, useQualifier, enabledFilters, treatAsDeclarations, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasWhereRestrictions() {
|
||||
return getCollectionDescriptor().hasWhereRestrictions();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void applyWhereRestrictions(
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
|
|
|
@ -19,6 +19,12 @@ import org.hibernate.sql.ast.tree.predicate.Predicate;
|
|||
* @see FilterRestrictable
|
||||
*/
|
||||
public interface WhereRestrictable {
|
||||
|
||||
/**
|
||||
* Does this restrictable have a where restriction?
|
||||
*/
|
||||
boolean hasWhereRestrictions();
|
||||
|
||||
/**
|
||||
* Apply the {@link org.hibernate.annotations.Where} restrictions
|
||||
*/
|
||||
|
|
|
@ -82,8 +82,6 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
|
|||
LazyTableGroup.ParentTableGroupUseChecker {
|
||||
private ForeignKeyDescriptor foreignKey;
|
||||
private ValuedModelPart fkTargetModelPart;
|
||||
private boolean[] isInsertable;
|
||||
private boolean[] isUpdatable;
|
||||
|
||||
public ManyToManyCollectionPart(
|
||||
Nature nature,
|
||||
|
|
|
@ -700,8 +700,7 @@ public class PluralAttributeMappingImpl
|
|||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAstCreationState creationState) {
|
||||
final PredicateCollector predicateCollector = new PredicateCollector();
|
||||
|
||||
final PredicateCollector collectionPredicateCollector = new PredicateCollector();
|
||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||
navigablePath,
|
||||
lhs,
|
||||
|
@ -709,9 +708,18 @@ public class PluralAttributeMappingImpl
|
|||
explicitSqlAliasBase,
|
||||
requestedJoinType,
|
||||
fetched,
|
||||
predicateCollector::applyPredicate,
|
||||
addsPredicate,
|
||||
collectionPredicateCollector::applyPredicate,
|
||||
creationState
|
||||
);
|
||||
final PredicateCollector predicateCollector;
|
||||
if ( tableGroup.getNestedTableGroupJoins().isEmpty() ) {
|
||||
// No nested table group joins means that the predicate has to be pushed to the last join
|
||||
predicateCollector = new PredicateCollector();
|
||||
}
|
||||
else {
|
||||
predicateCollector = collectionPredicateCollector;
|
||||
}
|
||||
|
||||
getCollectionDescriptor().applyBaseRestrictions(
|
||||
predicateCollector::applyPredicate,
|
||||
|
@ -737,12 +745,23 @@ public class PluralAttributeMappingImpl
|
|||
creationState
|
||||
);
|
||||
|
||||
return new TableGroupJoin(
|
||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
navigablePath,
|
||||
determineSqlJoinType( lhs, requestedJoinType, fetched ),
|
||||
tableGroup,
|
||||
predicateCollector.getPredicate()
|
||||
collectionPredicateCollector.getPredicate()
|
||||
);
|
||||
if ( predicateCollector != collectionPredicateCollector ) {
|
||||
final TableGroupJoin joinForPredicate;
|
||||
if ( !tableGroup.getNestedTableGroupJoins().isEmpty() || tableGroup.getTableGroupJoins().isEmpty() ) {
|
||||
joinForPredicate = tableGroupJoin;
|
||||
}
|
||||
else {
|
||||
joinForPredicate = tableGroup.getTableGroupJoins().get( tableGroup.getTableGroupJoins().size() - 1 );
|
||||
}
|
||||
joinForPredicate.applyPredicate( predicateCollector.getPredicate() );
|
||||
}
|
||||
return tableGroupJoin;
|
||||
}
|
||||
|
||||
private boolean hasSoftDelete() {
|
||||
|
@ -827,6 +846,29 @@ public class PluralAttributeMappingImpl
|
|||
boolean fetched,
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAstCreationState creationState) {
|
||||
return createRootTableGroupJoin(
|
||||
navigablePath,
|
||||
lhs,
|
||||
explicitSourceAlias,
|
||||
explicitSqlAliasBase,
|
||||
requestedJoinType,
|
||||
fetched,
|
||||
false,
|
||||
predicateConsumer,
|
||||
creationState
|
||||
);
|
||||
}
|
||||
|
||||
private TableGroup createRootTableGroupJoin(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup lhs,
|
||||
String explicitSourceAlias,
|
||||
SqlAliasBase explicitSqlAliasBase,
|
||||
SqlAstJoinType requestedJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAstCreationState creationState) {
|
||||
final CollectionPersister collectionDescriptor = getCollectionDescriptor();
|
||||
final SqlAstJoinType joinType = determineSqlJoinType( lhs, requestedJoinType, fetched );
|
||||
final SqlAliasBase sqlAliasBase = creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() );
|
||||
|
@ -845,8 +887,10 @@ public class PluralAttributeMappingImpl
|
|||
else {
|
||||
tableGroup = createCollectionTableGroup(
|
||||
lhs.canUseInnerJoins() && joinType == SqlAstJoinType.INNER,
|
||||
joinType,
|
||||
navigablePath,
|
||||
fetched,
|
||||
addsPredicate,
|
||||
explicitSourceAlias,
|
||||
sqlAliasBase,
|
||||
creationState
|
||||
|
@ -912,8 +956,10 @@ public class PluralAttributeMappingImpl
|
|||
|
||||
private TableGroup createCollectionTableGroup(
|
||||
boolean canUseInnerJoins,
|
||||
SqlAstJoinType joinType,
|
||||
NavigablePath navigablePath,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
String sourceAlias,
|
||||
SqlAliasBase explicitSqlAliasBase,
|
||||
SqlAstCreationState creationState) {
|
||||
|
@ -944,6 +990,12 @@ public class PluralAttributeMappingImpl
|
|||
null,
|
||||
creationState.getCreationContext().getSessionFactory()
|
||||
);
|
||||
// For inner joins we never need join nesting
|
||||
final boolean nestedJoin = joinType != SqlAstJoinType.INNER
|
||||
// For outer joins we need nesting if there might be an on-condition that refers to the element table
|
||||
&& ( addsPredicate
|
||||
|| isAffectedByEnabledFilters( creationState.getLoadQueryInfluencers() )
|
||||
|| collectionDescriptor.hasWhereRestrictions() );
|
||||
|
||||
if ( elementDescriptor instanceof TableGroupJoinProducer ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) elementDescriptor ).createTableGroupJoin(
|
||||
|
@ -951,12 +1003,12 @@ public class PluralAttributeMappingImpl
|
|||
tableGroup,
|
||||
null,
|
||||
sqlAliasBase,
|
||||
SqlAstJoinType.INNER,
|
||||
nestedJoin ? SqlAstJoinType.INNER : joinType,
|
||||
fetched,
|
||||
false,
|
||||
creationState
|
||||
);
|
||||
tableGroup.registerElementTableGroup( tableGroupJoin );
|
||||
tableGroup.registerElementTableGroup( tableGroupJoin, nestedJoin );
|
||||
}
|
||||
|
||||
if ( indexDescriptor instanceof TableGroupJoinProducer ) {
|
||||
|
@ -965,12 +1017,12 @@ public class PluralAttributeMappingImpl
|
|||
tableGroup,
|
||||
null,
|
||||
sqlAliasBase,
|
||||
SqlAstJoinType.INNER,
|
||||
nestedJoin ? SqlAstJoinType.INNER : joinType,
|
||||
fetched,
|
||||
false,
|
||||
creationState
|
||||
);
|
||||
tableGroup.registerIndexTableGroup( tableGroupJoin );
|
||||
tableGroup.registerIndexTableGroup( tableGroupJoin, nestedJoin );
|
||||
}
|
||||
|
||||
return tableGroup;
|
||||
|
@ -996,8 +1048,10 @@ public class PluralAttributeMappingImpl
|
|||
else {
|
||||
return createCollectionTableGroup(
|
||||
canUseInnerJoins,
|
||||
SqlAstJoinType.INNER,
|
||||
navigablePath,
|
||||
false,
|
||||
false,
|
||||
explicitSourceAlias,
|
||||
explicitSqlAliasBase,
|
||||
creationState
|
||||
|
|
|
@ -1149,6 +1149,11 @@ public abstract class AbstractCollectionPersister
|
|||
applyWhereRestrictions( predicateConsumer, tableGroup, useQualifier, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWhereRestrictions() {
|
||||
return hasWhere() || manyToManyWhereTemplate != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyWhereRestrictions(
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
|
|
|
@ -3315,6 +3315,11 @@ public abstract class AbstractEntityPersister
|
|||
applyWhereRestrictions( predicateConsumer, tableGroup, useQualifier, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWhereRestrictions() {
|
||||
return sqlWhereStringTemplate != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyWhereRestrictions(
|
||||
Consumer<Predicate> predicateConsumer,
|
||||
|
|
|
@ -3350,6 +3350,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
}
|
||||
|
||||
final TableGroupJoin joinForPredicate;
|
||||
if ( !joinedTableGroup.getNestedTableGroupJoins().isEmpty() || joinedTableGroup.getTableGroupJoins().isEmpty() ) {
|
||||
joinForPredicate = joinedTableGroupJoin;
|
||||
}
|
||||
else {
|
||||
joinForPredicate = joinedTableGroup.getTableGroupJoins().get( joinedTableGroup.getTableGroupJoins().size() - 1 );
|
||||
}
|
||||
|
||||
// add any additional join restrictions
|
||||
if ( sqmJoin.getJoinPredicate() != null ) {
|
||||
if ( sqmJoin.isFetched() ) {
|
||||
|
@ -3358,13 +3366,21 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
|
||||
final SqmJoin<?, ?> oldJoin = currentlyProcessingJoin;
|
||||
currentlyProcessingJoin = sqmJoin;
|
||||
joinedTableGroupJoin.applyPredicate( visitNestedTopLevelPredicate( sqmJoin.getJoinPredicate() ) );
|
||||
final Predicate predicate = visitNestedTopLevelPredicate( sqmJoin.getJoinPredicate() );
|
||||
// If translating the join predicate didn't initialize the table group,
|
||||
// we can safely apply it on the collection table group instead
|
||||
if ( joinForPredicate.getJoinedGroup().isInitialized() ) {
|
||||
joinForPredicate.applyPredicate( predicate );
|
||||
}
|
||||
else {
|
||||
joinedTableGroupJoin.applyPredicate( predicate );
|
||||
}
|
||||
currentlyProcessingJoin = oldJoin;
|
||||
}
|
||||
// Since joins on treated paths will never cause table pruning, we need to add a join condition for the treat
|
||||
if ( sqmJoin.getLhs() instanceof SqmTreatedPath<?, ?> ) {
|
||||
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmJoin.getLhs();
|
||||
joinedTableGroupJoin.applyPredicate(
|
||||
joinForPredicate.applyPredicate(
|
||||
createTreatTypeRestriction(
|
||||
treatedPath.getWrappedPath(),
|
||||
treatedPath.getTreatTarget()
|
||||
|
@ -8171,16 +8187,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
pluralAttributeMapping.applyBaseManyToManyRestrictions(
|
||||
(predicate) -> {
|
||||
final TableGroup parentTableGroup = getFromClauseIndex().getTableGroup( collectionFetch.getFetchParent().getNavigablePath() );
|
||||
TableGroupJoin pluralTableGroupJoin = null;
|
||||
for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
|
||||
if ( nestedTableGroupJoin.getNavigablePath() == fetchablePath ) {
|
||||
pluralTableGroupJoin = nestedTableGroupJoin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup );
|
||||
assert pluralTableGroupJoin != null;
|
||||
pluralTableGroupJoin.applyPredicate( predicate );
|
||||
|
||||
final TableGroupJoin joinForPredicate;
|
||||
if ( !tableGroup.getNestedTableGroupJoins().isEmpty() || tableGroup.getTableGroupJoins().isEmpty() ) {
|
||||
joinForPredicate = pluralTableGroupJoin;
|
||||
}
|
||||
else {
|
||||
joinForPredicate = tableGroup.getTableGroupJoins().get( tableGroup.getTableGroupJoins().size() - 1 );
|
||||
}
|
||||
joinForPredicate.applyPredicate( predicate );
|
||||
},
|
||||
tableGroup,
|
||||
true,
|
||||
|
|
|
@ -67,15 +67,33 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa
|
|||
}
|
||||
|
||||
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
|
||||
registerIndexTableGroup( indexTableGroupJoin, true );
|
||||
}
|
||||
|
||||
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin, boolean nested) {
|
||||
assert this.indexTableGroup == null;
|
||||
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
|
||||
addNestedTableGroupJoin( indexTableGroupJoin );
|
||||
if ( nested ) {
|
||||
addNestedTableGroupJoin( indexTableGroupJoin );
|
||||
}
|
||||
else {
|
||||
addTableGroupJoin( indexTableGroupJoin );
|
||||
}
|
||||
}
|
||||
|
||||
public void registerElementTableGroup(TableGroupJoin elementTableGroupJoin) {
|
||||
registerElementTableGroup( elementTableGroupJoin, true );
|
||||
}
|
||||
|
||||
public void registerElementTableGroup(TableGroupJoin elementTableGroupJoin, boolean nested) {
|
||||
assert this.elementTableGroup == null;
|
||||
this.elementTableGroup = elementTableGroupJoin.getJoinedGroup();
|
||||
addNestedTableGroupJoin( elementTableGroupJoin );
|
||||
if ( nested ) {
|
||||
addNestedTableGroupJoin( elementTableGroupJoin );
|
||||
}
|
||||
else {
|
||||
addTableGroupJoin( elementTableGroupJoin );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -72,6 +72,11 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
throw new HibernateException( "Illegal null value for array index encountered while reading: "
|
||||
+ getCollectionAttributeMapping().getNavigableRole() );
|
||||
}
|
||||
final Object element = elementAssembler.assemble( rowProcessingState );
|
||||
if ( element == null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
return;
|
||||
}
|
||||
int index = indexValue;
|
||||
|
||||
if ( indexBase != 0 ) {
|
||||
|
@ -82,7 +87,7 @@ public class ArrayInitializer extends AbstractImmediateCollectionInitializer {
|
|||
loadingState.add( i, null );
|
||||
}
|
||||
|
||||
loadingState.set( index, elementAssembler.assemble( rowProcessingState ) );
|
||||
loadingState.set( index, element );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,14 +63,24 @@ public class BagInitializer extends AbstractImmediateCollectionInitializer {
|
|||
List<Object> loadingState,
|
||||
RowProcessingState rowProcessingState) {
|
||||
if ( collectionIdAssembler != null ) {
|
||||
final Object[] row = new Object[2];
|
||||
row[0] = collectionIdAssembler.assemble( rowProcessingState );
|
||||
row[1] = elementAssembler.assemble( rowProcessingState );
|
||||
final Object collectionId = collectionIdAssembler.assemble( rowProcessingState );
|
||||
if ( collectionId == null ) {
|
||||
return;
|
||||
}
|
||||
final Object element = elementAssembler.assemble( rowProcessingState );
|
||||
if ( element == null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
return;
|
||||
}
|
||||
|
||||
loadingState.add( row );
|
||||
loadingState.add( new Object[]{ collectionId, element } );
|
||||
}
|
||||
else {
|
||||
loadingState.add( elementAssembler.assemble( rowProcessingState ) );
|
||||
final Object element = elementAssembler.assemble( rowProcessingState );
|
||||
if ( element != null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
loadingState.add( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,11 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
throw new HibernateException( "Illegal null value for list index encountered while reading: "
|
||||
+ getCollectionAttributeMapping().getNavigableRole() );
|
||||
}
|
||||
final Object element = elementAssembler.assemble( rowProcessingState );
|
||||
if ( element == null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
return;
|
||||
}
|
||||
int index = indexValue;
|
||||
|
||||
if ( listIndexBase != 0 ) {
|
||||
|
@ -84,7 +89,7 @@ public class ListInitializer extends AbstractImmediateCollectionInitializer {
|
|||
loadingState.add( i, null );
|
||||
}
|
||||
|
||||
loadingState.set( index, elementAssembler.assemble( rowProcessingState ) );
|
||||
loadingState.set( index, element );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,10 +9,12 @@ package org.hibernate.sql.results.graph.collection.internal;
|
|||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
import org.hibernate.collection.spi.PersistentMap;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.internal.log.LoggingHelper;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.results.graph.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
|
@ -61,12 +63,17 @@ public class MapInitializer extends AbstractImmediateCollectionInitializer {
|
|||
CollectionKey collectionKey,
|
||||
List<Object> loadingState,
|
||||
RowProcessingState rowProcessingState) {
|
||||
loadingState.add(
|
||||
new Object[] {
|
||||
mapKeyAssembler.assemble( rowProcessingState ),
|
||||
mapValueAssembler.assemble( rowProcessingState )
|
||||
}
|
||||
);
|
||||
final Object key = mapKeyAssembler.assemble( rowProcessingState );
|
||||
if ( key == null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
return;
|
||||
}
|
||||
final Object value = mapValueAssembler.assemble( rowProcessingState );
|
||||
if ( value == null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
return;
|
||||
}
|
||||
loadingState.add( new Object[] { key, value } );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,7 +53,12 @@ public class SetInitializer extends AbstractImmediateCollectionInitializer {
|
|||
CollectionKey collectionKey,
|
||||
List<Object> loadingState,
|
||||
RowProcessingState rowProcessingState) {
|
||||
loadingState.add( elementAssembler.assemble( rowProcessingState ) );
|
||||
final Object element = elementAssembler.assemble( rowProcessingState );
|
||||
if ( element == null ) {
|
||||
// If element is null, then NotFoundAction must be IGNORE
|
||||
return;
|
||||
}
|
||||
loadingState.add( element );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -65,7 +65,7 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch {
|
|||
NavigablePath navigablePath,
|
||||
DomainResultCreationState creationState) {
|
||||
super( fetchParent, collectionPart, navigablePath );
|
||||
this.notFoundAction = null;
|
||||
this.notFoundAction = collectionPart.getNotFoundAction();
|
||||
this.keyResult = null;
|
||||
this.sourceAlias = tableGroup.getSourceAlias();
|
||||
|
||||
|
|
|
@ -1023,6 +1023,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWhereRestrictions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyWhereRestrictions(Consumer<Predicate> predicateConsumer, TableGroup tableGroup, boolean useQualifier, SqlAstCreationState creationState) {
|
||||
|
||||
|
|
|
@ -280,10 +280,10 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
|
|||
// Check the from-clause
|
||||
assertPluralAttributeJoinedGroup( sqlAst, "shipAddresses", tableGroup -> {
|
||||
if ( graphSemantic == GraphSemantic.LOAD ) {
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup compositeTableGroup = tableGroup.getNestedTableGroupJoins()
|
||||
final TableGroup compositeTableGroup = tableGroup.getTableGroupJoins()
|
||||
.iterator()
|
||||
.next()
|
||||
.getJoinedGroup();
|
||||
|
@ -295,10 +295,10 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
|
|||
assertThat( joinedGroup.isInitialized(), is( false ) );
|
||||
}
|
||||
else {
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
|
|
|
@ -248,10 +248,10 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
|
|||
// Check the from-clause
|
||||
assertPluralAttributeJoinedGroup( sqlAst, "shipAddresses", tableGroup -> {
|
||||
if ( graphSemantic == GraphSemantic.LOAD ) {
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() )
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() )
|
||||
.getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
|
@ -265,10 +265,10 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
|
|||
assertThat( countryTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
else {
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
|
|
|
@ -278,10 +278,10 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
|
|||
// Check the from-clause
|
||||
assertPluralAttributeJoinedGroup( sqlAst, "shipAddresses", tableGroup -> {
|
||||
if ( graphSemantic == GraphSemantic.LOAD ) {
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup compositeTableGroup = tableGroup.getNestedTableGroupJoins()
|
||||
final TableGroup compositeTableGroup = tableGroup.getTableGroupJoins()
|
||||
.iterator()
|
||||
.next()
|
||||
.getJoinedGroup();
|
||||
|
@ -293,10 +293,10 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
|
|||
assertThat( joinedGroup.isInitialized(), is( false ) );
|
||||
}
|
||||
else {
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.testing.jdbc.SQLStatementInspector;
|
|||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
|
@ -42,6 +43,86 @@ public class JoinTableOptimizationTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInnerJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select p.name from Document d join d.people p" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
Assertions.assertEquals(
|
||||
"select p1_1.name " +
|
||||
"from Document d1_0 " +
|
||||
"join people p1_0 on d1_0.id=p1_0.Document_id " +
|
||||
"join Person p1_1 on p1_1.id=p1_0.people_id",
|
||||
statementInspector.getSqlQueries().get( 0 ),
|
||||
"Nested join was not optimized away"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeftJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select p.name from Document d left join d.people p" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
Assertions.assertEquals(
|
||||
"select p1_1.name " +
|
||||
"from Document d1_0 " +
|
||||
"left join people p1_0 on d1_0.id=p1_0.Document_id " +
|
||||
"left join Person p1_1 on p1_1.id=p1_0.people_id",
|
||||
statementInspector.getSqlQueries().get( 0 ),
|
||||
"Nested join was not optimized away"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInnerJoinCustomOnClause(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select p.name from Document d join d.people p on p.id > 1" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
Assertions.assertEquals(
|
||||
"select p1_1.name " +
|
||||
"from Document d1_0 " +
|
||||
"join people p1_0 on d1_0.id=p1_0.Document_id and p1_0.people_id>1 " +
|
||||
"join Person p1_1 on p1_1.id=p1_0.people_id",
|
||||
statementInspector.getSqlQueries().get( 0 ),
|
||||
"Nested join was not optimized away"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeftJoinCustomOnClause(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select p.name from Document d left join d.people p on p.id > 1" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
Assertions.assertEquals(
|
||||
"select p1_1.name " +
|
||||
"from Document d1_0 " +
|
||||
"left join (people p1_0 " +
|
||||
"join Person p1_1 on p1_1.id=p1_0.people_id) on d1_0.id=p1_0.Document_id and p1_0.people_id>1",
|
||||
statementInspector.getSqlQueries().get( 0 ),
|
||||
"Nested join was wrongly optimized away"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Document")
|
||||
public static class Document {
|
||||
@Id
|
||||
|
|
|
@ -57,7 +57,7 @@ public class MapIssueTest {
|
|||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select c from MapOwner as o left join o.contents c join c.relationship r where r.id is not null" ).list();
|
||||
s.createQuery( "select c from MapOwner as o join o.contents c join c.relationship r where r.id is not null" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
// Assert 2 joins, collection table and collection element. No need to join the relationship because it is not nullable
|
||||
statementInspector.assertNumberOfJoins( 0, 2 );
|
||||
|
|
|
@ -249,7 +249,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"join (children_uks cu1_0 join PERSON_TABLE cu1_1 on cu1_1.uk=cu1_0.child_uk) on p1_0.uk=cu1_0.owner_uk " +
|
||||
"join children_uks cu1_0 on p1_0.uk=cu1_0.owner_uk join PERSON_TABLE cu1_1 on cu1_1.uk=cu1_0.child_uk " +
|
||||
"join PERSON_TABLE_PERSON_TABLE c1_0 on p1_0.id=c1_0.Person_id " +
|
||||
"where cu1_1.id=c1_0.children_id",
|
||||
statementInspector.getSqlQueries().get( 0 )
|
||||
|
@ -272,7 +272,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"join PERSON_TABLE_PERSON_TABLE c1_0 on p1_0.id=c1_0.Person_id " +
|
||||
"join (children_uks cu1_0 join PERSON_TABLE cu1_1 on cu1_1.uk=cu1_0.child_uk) on p1_0.uk=cu1_0.owner_uk " +
|
||||
"join children_uks cu1_0 on p1_0.uk=cu1_0.owner_uk join PERSON_TABLE cu1_1 on cu1_1.uk=cu1_0.child_uk " +
|
||||
"where c1_0.children_id=cu1_1.id",
|
||||
statementInspector.getSqlQueries().get( 0 )
|
||||
);
|
||||
|
@ -293,7 +293,7 @@ public class CompareEntityValuedPathsTest {
|
|||
"select " +
|
||||
"1 " +
|
||||
"from PERSON_TABLE p1_0 " +
|
||||
"join (children_uks cu1_0 join PERSON_TABLE cu1_1 on cu1_1.uk=cu1_0.child_uk) on p1_0.uk=cu1_0.owner_uk " +
|
||||
"join children_uks cu1_0 on p1_0.uk=cu1_0.owner_uk join PERSON_TABLE cu1_1 on cu1_1.uk=cu1_0.child_uk " +
|
||||
"where cu1_1.id in (select c1_0.children_id from PERSON_TABLE_PERSON_TABLE c1_0 where p1_0.id=c1_0.Person_id)",
|
||||
statementInspector.getSqlQueries().get( 0 )
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue