HHH-17830 Fix rendering custom predicate for element collection joins
This commit is contained in:
parent
e3f069a5cd
commit
fcf722a9f8
|
@ -48,6 +48,7 @@ import org.hibernate.query.sqm.ComparisonOperator;
|
|||
import org.hibernate.spi.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.internal.TableGroupJoinHelper;
|
||||
import org.hibernate.sql.ast.spi.AliasCollector;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
||||
|
@ -710,13 +711,7 @@ public class LoaderSelectBuilder {
|
|||
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 );
|
||||
}
|
||||
final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin );
|
||||
|
||||
pluralAttributeMapping.applyBaseRestrictions(
|
||||
joinForPredicate::applyPredicate,
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.hibernate.persister.entity.Joinable;
|
|||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.internal.TableGroupJoinHelper;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
||||
|
@ -763,13 +764,7 @@ public class PluralAttributeMappingImpl
|
|||
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 );
|
||||
}
|
||||
final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( tableGroupJoin );
|
||||
joinForPredicate.applyPredicate( predicateCollector.getPredicate() );
|
||||
}
|
||||
return tableGroupJoin;
|
||||
|
|
|
@ -268,6 +268,7 @@ import org.hibernate.sql.ast.Clause;
|
|||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlTreeCreationException;
|
||||
import org.hibernate.sql.ast.SqlTreeCreationLogger;
|
||||
import org.hibernate.sql.ast.internal.TableGroupJoinHelper;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
|
||||
|
@ -3381,13 +3382,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
}
|
||||
|
||||
// Implicit joins in the predicate might alter the nested table group joins,
|
||||
// so defer determination of the join for predicate until after the predicate was visited
|
||||
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 ) {
|
||||
|
@ -3398,6 +3395,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final SqmJoin<?, ?> oldJoin = currentlyProcessingJoin;
|
||||
currentlyProcessingJoin = sqmJoin;
|
||||
final Predicate predicate = visitNestedTopLevelPredicate( sqmJoin.getJoinPredicate() );
|
||||
joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( joinedTableGroupJoin );
|
||||
// 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() ) {
|
||||
|
@ -3408,6 +3406,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
currentlyProcessingJoin = oldJoin;
|
||||
}
|
||||
else {
|
||||
joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( joinedTableGroupJoin );
|
||||
}
|
||||
// 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();
|
||||
|
@ -8338,13 +8339,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
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 );
|
||||
}
|
||||
final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin );
|
||||
joinForPredicate.applyPredicate( predicate );
|
||||
},
|
||||
tableGroup,
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.sql.ast.internal;
|
||||
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedCollectionPart;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.VirtualTableGroup;
|
||||
|
||||
public class TableGroupJoinHelper {
|
||||
|
||||
/**
|
||||
* Determine the {@link TableGroupJoin} to which a custom {@code ON} clause predicate should be applied to.
|
||||
* This is supposed to be called right after construction of a {@link TableGroupJoin}.
|
||||
* This should also be called after a {@link org.hibernate.query.sqm.tree.predicate.SqmPredicate} is translated to a
|
||||
* {@link org.hibernate.sql.ast.tree.predicate.Predicate}, because that translation might cause nested joins to be
|
||||
* added to the table group of the join.
|
||||
*/
|
||||
public static TableGroupJoin determineJoinForPredicateApply(TableGroupJoin mainTableGroupJoin) {
|
||||
final TableGroup mainTableGroup = mainTableGroupJoin.getJoinedGroup();
|
||||
if ( !mainTableGroup.getNestedTableGroupJoins().isEmpty() || mainTableGroup.getTableGroupJoins().isEmpty() ) {
|
||||
// Always apply a predicate on the main table group join if it has nested table group joins or no joins
|
||||
return mainTableGroupJoin;
|
||||
}
|
||||
else {
|
||||
// If the main table group has just regular table group joins,
|
||||
// prefer to apply predicates on the last table group join
|
||||
final TableGroupJoin lastTableGroupJoin = mainTableGroup.getTableGroupJoins()
|
||||
.get( mainTableGroup.getTableGroupJoins().size() - 1 );
|
||||
if ( lastTableGroupJoin.getJoinedGroup().getModelPart() instanceof EmbeddedCollectionPart ) {
|
||||
// If the table group join refers to an embedded collection part,
|
||||
// then the underlying table group *is* the main table group.
|
||||
// Applying predicates on the join referring to the virtual table group would be a problem though,
|
||||
// because these predicates will never be rendered. So use the main table group join in that case
|
||||
assert lastTableGroupJoin.getJoinedGroup() instanceof VirtualTableGroup
|
||||
&& ( (VirtualTableGroup) lastTableGroupJoin.getJoinedGroup() ).getUnderlyingTableGroup() == mainTableGroup;
|
||||
return mainTableGroupJoin;
|
||||
}
|
||||
return lastTableGroupJoin;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,11 +11,15 @@ import java.util.Set;
|
|||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
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.CollectionTable;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinTable;
|
||||
|
@ -123,6 +127,26 @@ public class JoinTableOptimizationTest {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@JiraKey("HHH-17830")
|
||||
public void testElementCollectionJoinCustomOnClause(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select p.text from Document d join d.pages p on p.text is not null" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
Assertions.assertEquals(
|
||||
"select p1_0.text " +
|
||||
"from Document d1_0 " +
|
||||
"join document_pages p1_0 on d1_0.id=p1_0.Document_id and p1_0.text is not null",
|
||||
statementInspector.getSqlQueries().get( 0 ),
|
||||
"Join condition was wrongly removed"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Document")
|
||||
public static class Document {
|
||||
@Id
|
||||
|
@ -131,6 +155,9 @@ public class JoinTableOptimizationTest {
|
|||
@OneToMany
|
||||
@JoinTable(name = "people")
|
||||
Set<Person> people;
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "document_pages")
|
||||
Set<Page> pages;
|
||||
}
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
@ -138,4 +165,8 @@ public class JoinTableOptimizationTest {
|
|||
Long id;
|
||||
String name;
|
||||
}
|
||||
@Embeddable
|
||||
public static class Page {
|
||||
String text;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue