Various fixes and move tests from test.jpa
* Remodel `@MapKey` support to not create subqueries in the on-clause anymore * Make sure the index table group is reused for the to-one association a `@MapKey` refers to * Consistently register collection part table groups * Implement support for FK optimization for EntityCollectionPart
This commit is contained in:
parent
38d1c122eb
commit
9a329f4991
|
@ -57,6 +57,7 @@ import org.hibernate.mapping.PersistentClass;
|
|||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.sql.Template;
|
||||
|
@ -185,6 +186,7 @@ public class MapBinder extends CollectionBinder {
|
|||
mapProperty.getValue(), map, targetPropertyName, associatedClass, targetPropertyPersistentClass, buildingContext
|
||||
);
|
||||
map.setIndex( indexValue );
|
||||
map.setMapKeyPropertyName( mapKeyPropertyName );
|
||||
}
|
||||
else {
|
||||
//this is a true Map mapping
|
||||
|
@ -411,13 +413,14 @@ public class MapBinder extends CollectionBinder {
|
|||
PersistentClass associatedClass,
|
||||
PersistentClass targetPropertyPersistentClass,
|
||||
MetadataBuildingContext buildingContext) {
|
||||
final Value element = collection.getElement();
|
||||
final String fromAndWhere = resolveFromAndWhere(
|
||||
collection,
|
||||
associatedClass,
|
||||
targetPropertyPersistentClass,
|
||||
element
|
||||
);
|
||||
final Table mapKeyTable;
|
||||
// HHH-11005 - only if we are OneToMany and location of map key property is at a different level, need to add a select
|
||||
if ( !associatedClass.equals( targetPropertyPersistentClass ) ) {
|
||||
mapKeyTable = targetPropertyPersistentClass.getTable();
|
||||
}
|
||||
else {
|
||||
mapKeyTable = associatedClass.getTable();
|
||||
}
|
||||
|
||||
if ( value instanceof Component ) {
|
||||
Component component = (Component) value;
|
||||
|
@ -452,37 +455,23 @@ public class MapBinder extends CollectionBinder {
|
|||
final BasicValue sourceValue = (BasicValue) value;
|
||||
final DependantBasicValue dependantBasicValue = new DependantBasicValue(
|
||||
getBuildingContext(),
|
||||
collection.getTable(),
|
||||
mapKeyTable,
|
||||
sourceValue,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
String formulaString;
|
||||
|
||||
final Selectable sourceValueColumn = sourceValue.getColumn();
|
||||
if ( sourceValueColumn instanceof Column ) {
|
||||
formulaString = ( (Column) sourceValueColumn ).getQuotedName();
|
||||
dependantBasicValue.addColumn( ( (Column) sourceValueColumn ).clone() );
|
||||
}
|
||||
else if ( sourceValueColumn instanceof Formula ) {
|
||||
formulaString = ( (Formula) sourceValueColumn ).getFormula();
|
||||
dependantBasicValue.addFormula( new Formula( ( (Formula) sourceValueColumn ).getFormula() ) );
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure( "Unknown element column type : " + sourceValueColumn.getClass() );
|
||||
}
|
||||
|
||||
if ( fromAndWhere != null ) {
|
||||
final Dialect dialect = buildingContext.getBootstrapContext().getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getDialect();
|
||||
formulaString = Template.renderWhereStringTemplate( formulaString, "$alias$", dialect );
|
||||
formulaString = "(select " + formulaString + fromAndWhere + ")";
|
||||
formulaString = StringHelper.replace( formulaString, "$alias$", "a987" );
|
||||
}
|
||||
|
||||
final Formula formula = new Formula( formulaString );
|
||||
dependantBasicValue.addFormula( formula );
|
||||
|
||||
return dependantBasicValue;
|
||||
}
|
||||
else if ( value instanceof SimpleValue ) {
|
||||
|
@ -490,7 +479,7 @@ public class MapBinder extends CollectionBinder {
|
|||
SimpleValue targetValue;
|
||||
if ( value instanceof ManyToOne ) {
|
||||
ManyToOne sourceManyToOne = (ManyToOne) sourceValue;
|
||||
ManyToOne targetManyToOne = new ManyToOne( getBuildingContext(), collection.getCollectionTable() );
|
||||
ManyToOne targetManyToOne = new ManyToOne( getBuildingContext(), mapKeyTable );
|
||||
targetManyToOne.setFetchMode( FetchMode.DEFAULT );
|
||||
targetManyToOne.setLazy( true );
|
||||
//targetValue.setIgnoreNotFound( ); does not make sense for a map key
|
||||
|
@ -498,39 +487,21 @@ public class MapBinder extends CollectionBinder {
|
|||
targetValue = targetManyToOne;
|
||||
}
|
||||
else {
|
||||
targetValue = new BasicValue( getBuildingContext(), collection.getCollectionTable() );
|
||||
targetValue = new BasicValue( getBuildingContext(), mapKeyTable );
|
||||
targetValue.copyTypeFrom( sourceValue );
|
||||
}
|
||||
Iterator columns = sourceValue.getColumnIterator();
|
||||
Random random = new Random();
|
||||
final Iterator<Selectable> columns = sourceValue.getColumnIterator();
|
||||
while ( columns.hasNext() ) {
|
||||
Object current = columns.next();
|
||||
Formula formula = new Formula();
|
||||
String formulaString;
|
||||
Selectable current = columns.next();
|
||||
if ( current instanceof Column ) {
|
||||
formulaString = ( (Column) current ).getQuotedName();
|
||||
targetValue.addColumn( ( (Column) current ).clone() );
|
||||
}
|
||||
else if ( current instanceof Formula ) {
|
||||
formulaString = ( (Formula) current ).getFormula();
|
||||
targetValue.addFormula( new Formula( ( (Formula) current ).getFormula() ) );
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure( "Unknown element in column iterator: " + current.getClass() );
|
||||
}
|
||||
if ( fromAndWhere != null ) {
|
||||
final Dialect dialect = buildingContext.getBootstrapContext().getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getDialect();
|
||||
formulaString = Template.renderWhereStringTemplate( formulaString, "$alias$", dialect );
|
||||
formulaString = "(select " + formulaString + fromAndWhere + ")";
|
||||
formulaString = StringHelper.replace(
|
||||
formulaString,
|
||||
"$alias$",
|
||||
"a" + random.nextInt( 16 )
|
||||
);
|
||||
}
|
||||
formula.setFormula( formulaString );
|
||||
targetValue.addFormula( formula );
|
||||
|
||||
}
|
||||
return targetValue;
|
||||
}
|
||||
|
@ -538,76 +509,4 @@ public class MapBinder extends CollectionBinder {
|
|||
throw new AssertionFailure( "Unknown type encountered for map key: " + value.getClass() );
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveFromAndWhere(
|
||||
Collection collection,
|
||||
PersistentClass associatedClass,
|
||||
PersistentClass targetPropertyPersistentClass,
|
||||
Value element) {
|
||||
if ( ! OneToMany.class.isInstance( element ) ) {
|
||||
String referencedPropertyName = null;
|
||||
if ( element instanceof ToOne ) {
|
||||
referencedPropertyName = ( (ToOne) element ).getReferencedPropertyName();
|
||||
}
|
||||
else if ( element instanceof DependantValue ) {
|
||||
//TODO this never happen I think
|
||||
if ( propertyName != null ) {
|
||||
referencedPropertyName = collection.getReferencedPropertyName();
|
||||
}
|
||||
else {
|
||||
throw new AnnotationException( "SecondaryTable JoinColumn cannot reference a non primary key" );
|
||||
}
|
||||
}
|
||||
Iterator<Selectable> referencedEntityColumns;
|
||||
if ( referencedPropertyName == null ) {
|
||||
referencedEntityColumns = associatedClass.getIdentifier().getColumnIterator();
|
||||
}
|
||||
else {
|
||||
Property referencedProperty = associatedClass.getRecursiveProperty( referencedPropertyName );
|
||||
referencedEntityColumns = referencedProperty.getColumnIterator();
|
||||
}
|
||||
return getFromAndWhereFormula(
|
||||
associatedClass.getTable().getQualifiedTableName().toString(),
|
||||
element.getColumnIterator(),
|
||||
referencedEntityColumns
|
||||
);
|
||||
}
|
||||
else {
|
||||
// HHH-11005 - only if we are OneToMany and location of map key property is at a different level, need to add a select
|
||||
if ( !associatedClass.equals( targetPropertyPersistentClass ) ) {
|
||||
return getFromAndWhereFormula(
|
||||
targetPropertyPersistentClass.getTable()
|
||||
.getQualifiedTableName()
|
||||
.toString(),
|
||||
element.getColumnIterator(),
|
||||
associatedClass.getIdentifier().getColumnIterator()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getFromAndWhereFormula(
|
||||
String tableName,
|
||||
Iterator<Selectable> collectionTableColumns,
|
||||
Iterator<Selectable> referencedEntityColumns) {
|
||||
String alias = "$alias$";
|
||||
StringBuilder fromAndWhereSb = new StringBuilder( " from " )
|
||||
.append( tableName )
|
||||
//.append(" as ") //Oracle doesn't support it in subqueries
|
||||
.append( " " )
|
||||
.append( alias ).append( " where " );
|
||||
while ( collectionTableColumns.hasNext() ) {
|
||||
Column colColumn = (Column) collectionTableColumns.next();
|
||||
Column refColumn = (Column) referencedEntityColumns.next();
|
||||
fromAndWhereSb.append( alias )
|
||||
.append( '.' )
|
||||
.append( refColumn.getQuotedName() )
|
||||
.append( '=' )
|
||||
.append( colColumn.getQuotedName() )
|
||||
.append( " and " );
|
||||
}
|
||||
return fromAndWhereSb.substring( 0, fromAndWhereSb.length() - 5 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
|||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.NonAggregatedIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.Joinable;
|
||||
|
@ -52,6 +51,7 @@ import org.hibernate.query.ComparisonOperator;
|
|||
import org.hibernate.query.EntityIdentifierNavigablePath;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
|
@ -60,6 +60,7 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
|||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||
|
@ -395,6 +396,7 @@ public class LoaderSelectBuilder {
|
|||
|
||||
rootQuerySpec.getFromClause().addRoot( rootTableGroup );
|
||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
|
||||
registerPluralTableGroupParts( sqlAstCreationState.getFromClauseAccess(), rootTableGroup );
|
||||
|
||||
if ( partsToSelect != null && !partsToSelect.isEmpty() ) {
|
||||
domainResults = new ArrayList<>( partsToSelect.size() );
|
||||
|
@ -409,11 +411,13 @@ public class LoaderSelectBuilder {
|
|||
null,
|
||||
SqlAstJoinType.LEFT,
|
||||
true,
|
||||
false,
|
||||
sqlAstCreationState
|
||||
);
|
||||
rootTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
tableGroup = tableGroupJoin.getJoinedGroup();
|
||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup );
|
||||
registerPluralTableGroupParts( sqlAstCreationState.getFromClauseAccess(), tableGroup );
|
||||
}
|
||||
else {
|
||||
tableGroup = rootTableGroup;
|
||||
|
@ -470,7 +474,7 @@ public class LoaderSelectBuilder {
|
|||
|
||||
if ( loadable instanceof PluralAttributeMapping ) {
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) loadable;
|
||||
applyFiltering( rootQuerySpec, rootTableGroup, pluralAttributeMapping );
|
||||
applyFiltering( rootQuerySpec, rootTableGroup, pluralAttributeMapping, sqlAstCreationState.getFromClauseAccess() );
|
||||
applyOrdering( rootTableGroup, pluralAttributeMapping );
|
||||
}
|
||||
else if ( loadable instanceof Joinable ) {
|
||||
|
@ -580,10 +584,10 @@ public class LoaderSelectBuilder {
|
|||
private void applyFiltering(
|
||||
QuerySpec querySpec,
|
||||
TableGroup tableGroup,
|
||||
PluralAttributeMapping pluralAttributeMapping) {
|
||||
PluralAttributeMapping pluralAttributeMapping,
|
||||
FromClauseAccess fromClauseAccess) {
|
||||
final CollectionPersister collectionPersister = pluralAttributeMapping.getCollectionDescriptor();
|
||||
final Joinable joinable = collectionPersister
|
||||
.getCollectionType()
|
||||
final Joinable joinable = collectionPersister.getCollectionType()
|
||||
.getAssociatedJoinable( creationContext.getSessionFactory() );
|
||||
final Predicate filterPredicate = FilterHelper.createFilterPredicate(
|
||||
loadQueryInfluencers,
|
||||
|
@ -601,18 +605,23 @@ public class LoaderSelectBuilder {
|
|||
tableGroup
|
||||
);
|
||||
if ( manyToManyFilterPredicate != null ) {
|
||||
TableGroupJoin elementTableGroupJoin = null;
|
||||
for ( TableGroupJoin nestedTableGroupJoin : tableGroup.getNestedTableGroupJoins() ) {
|
||||
final NavigablePath navigablePath = nestedTableGroupJoin.getNavigablePath();
|
||||
if ( navigablePath.getParent() == tableGroup.getNavigablePath()
|
||||
&& CollectionPart.Nature.ELEMENT.getName().equals( navigablePath.getUnaliasedLocalName() ) ) {
|
||||
elementTableGroupJoin = nestedTableGroupJoin;
|
||||
break;
|
||||
}
|
||||
final NavigablePath parentNavigablePath = tableGroup.getNavigablePath().getParent();
|
||||
if ( parentNavigablePath == null ) {
|
||||
querySpec.applyPredicate( manyToManyFilterPredicate );
|
||||
}
|
||||
else {
|
||||
final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( parentNavigablePath );
|
||||
TableGroupJoin pluralTableGroupJoin = null;
|
||||
for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
|
||||
if ( nestedTableGroupJoin.getNavigablePath() == tableGroup.getNavigablePath() ) {
|
||||
pluralTableGroupJoin = nestedTableGroupJoin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert elementTableGroupJoin != null;
|
||||
elementTableGroupJoin.applyPredicate( manyToManyFilterPredicate );
|
||||
assert pluralTableGroupJoin != null;
|
||||
pluralTableGroupJoin.applyPredicate( manyToManyFilterPredicate );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -852,7 +861,8 @@ public class LoaderSelectBuilder {
|
|||
applyFiltering(
|
||||
querySpec,
|
||||
joinTableGroup,
|
||||
pluralAttributeMapping
|
||||
pluralAttributeMapping,
|
||||
creationState.getFromClauseAccess()
|
||||
);
|
||||
applyOrdering(
|
||||
querySpec,
|
||||
|
@ -945,6 +955,7 @@ public class LoaderSelectBuilder {
|
|||
|
||||
rootQuerySpec.getFromClause().addRoot( rootTableGroup );
|
||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
|
||||
registerPluralTableGroupParts( sqlAstCreationState.getFromClauseAccess(), rootTableGroup );
|
||||
|
||||
// generate and apply the restriction
|
||||
applySubSelectRestriction(
|
||||
|
@ -956,7 +967,7 @@ public class LoaderSelectBuilder {
|
|||
);
|
||||
|
||||
// NOTE : no need to check - we are explicitly processing a plural-attribute
|
||||
applyFiltering( rootQuerySpec, rootTableGroup, attributeMapping );
|
||||
applyFiltering( rootQuerySpec, rootTableGroup, attributeMapping, sqlAstCreationState.getFromClauseAccess() );
|
||||
applyOrdering( rootTableGroup, attributeMapping );
|
||||
|
||||
// register the jdbc-parameters
|
||||
|
@ -1103,5 +1114,23 @@ public class LoaderSelectBuilder {
|
|||
|
||||
return subQuery;
|
||||
}
|
||||
|
||||
private void registerPluralTableGroupParts(FromClauseAccess fromClauseAccess, TableGroup tableGroup) {
|
||||
if ( tableGroup instanceof PluralTableGroup ) {
|
||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) tableGroup;
|
||||
if ( pluralTableGroup.getElementTableGroup() != null ) {
|
||||
fromClauseAccess.registerTableGroup(
|
||||
pluralTableGroup.getElementTableGroup().getNavigablePath(),
|
||||
pluralTableGroup.getElementTableGroup()
|
||||
);
|
||||
}
|
||||
if ( pluralTableGroup.getIndexTableGroup() != null ) {
|
||||
fromClauseAccess.registerTableGroup(
|
||||
pluralTableGroup.getIndexTableGroup().getNavigablePath(),
|
||||
pluralTableGroup.getIndexTableGroup()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.NotYetImplementedFor6Exception;
|
|||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
|
@ -56,6 +57,7 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver expressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
|
|
@ -54,15 +54,16 @@ public abstract class IndexedCollection extends Collection {
|
|||
pk.addColumns( getKey().getColumnIterator() );
|
||||
|
||||
// index should be last column listed
|
||||
boolean isFormula = false;
|
||||
Iterator iter = getIndex().getColumnIterator();
|
||||
boolean indexIsPartOfElement = false;
|
||||
final Iterator<Selectable> iter = getIndex().getColumnIterator();
|
||||
while ( iter.hasNext() ) {
|
||||
if ( ( (Selectable) iter.next() ).isFormula() ) {
|
||||
isFormula=true;
|
||||
final Selectable selectable = iter.next();
|
||||
if ( selectable.isFormula() || !getCollectionTable().containsColumn( (Column) selectable ) ) {
|
||||
indexIsPartOfElement = true;
|
||||
}
|
||||
}
|
||||
if (isFormula) {
|
||||
//if it is a formula index, use the element columns in the PK
|
||||
if ( indexIsPartOfElement ) {
|
||||
//if it is part of the element, use the element columns in the PK
|
||||
pk.addColumns( getElement().getColumnIterator() );
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -22,6 +22,9 @@ import org.hibernate.type.SortedMapType;
|
|||
* the key columns + index columns.
|
||||
*/
|
||||
public class Map extends IndexedCollection {
|
||||
|
||||
private String mapKeyPropertyName;
|
||||
|
||||
public Map(MetadataBuildingContext buildingContext, PersistentClass owner) {
|
||||
super( buildingContext, owner );
|
||||
}
|
||||
|
@ -30,6 +33,14 @@ public class Map extends IndexedCollection {
|
|||
return true;
|
||||
}
|
||||
|
||||
public String getMapKeyPropertyName() {
|
||||
return mapKeyPropertyName;
|
||||
}
|
||||
|
||||
public void setMapKeyPropertyName(String mapKeyPropertyName) {
|
||||
this.mapKeyPropertyName = mapKeyPropertyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionSemantics getDefaultCollectionSemantics() {
|
||||
if ( isSorted() ) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -38,7 +39,7 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
|
@ -170,8 +171,10 @@ public abstract class AbstractCompositeIdentifierMapping
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||
navigablePath,
|
||||
|
@ -182,6 +185,7 @@ public abstract class AbstractCompositeIdentifierMapping
|
|||
null,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
@ -198,8 +202,9 @@ public abstract class AbstractCompositeIdentifierMapping
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
return new CompositeTableGroup( navigablePath, this, lhs, fetched );
|
||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
|||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -196,6 +197,31 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
return superMappingType.getRootEntityDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts the table group and its table reference as well as table reference joins
|
||||
* in a way such that unnecessary tables or joins are omitted if possible,
|
||||
* based on the given treated entity names.
|
||||
*
|
||||
* The goal is to e.g. remove join inheritance "branches" or union selects that are impossible.
|
||||
*
|
||||
* Consider the following example:
|
||||
* <code>
|
||||
* class BaseEntity {}
|
||||
* class Sub1 extends BaseEntity {}
|
||||
* class Sub1Sub1 extends Sub1 {}
|
||||
* class Sub1Sub2 extends Sub1 {}
|
||||
* class Sub2 extends BaseEntity {}
|
||||
* class Sub2Sub1 extends Sub2 {}
|
||||
* class Sub2Sub2 extends Sub2 {}
|
||||
* </code>
|
||||
*
|
||||
* If the <code>treatedEntityNames</code> only contains <code>Sub1</code> or any of its subtypes,
|
||||
* this means that <code>Sub2</code> and all subtypes are impossible,
|
||||
* thus the joins/selects for these types shall be omitted in the given table group.
|
||||
*
|
||||
* @param tableGroup The table group to prune subclass tables for
|
||||
* @param treatedEntityNames The entity names for which path usages were registered
|
||||
*/
|
||||
default void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
|
||||
}
|
||||
|
||||
|
@ -297,6 +323,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
additionalPredicateCollectorAccess,
|
||||
creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ),
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationState.getFromClauseAccess(),
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -309,6 +336,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver expressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
return getEntityPersister().createRootTableGroup(
|
||||
canUseInnerJoins,
|
||||
|
@ -317,6 +345,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
|
|||
additionalPredicateCollectorAccess,
|
||||
sqlAliasBase,
|
||||
expressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ public interface PluralAttributeMapping
|
|||
interface IndexMetadata {
|
||||
CollectionPart getIndexDescriptor();
|
||||
int getListIndexBase();
|
||||
String getIndexPropertyName();
|
||||
}
|
||||
|
||||
IndexMetadata getIndexMetadata();
|
||||
|
|
|
@ -34,6 +34,20 @@ public interface Queryable extends ModelPart {
|
|||
*/
|
||||
ModelPart findSubPart(String name, EntityMappingType treatTargetType);
|
||||
|
||||
default ModelPart findByPath(String path) {
|
||||
int nextStart = 0;
|
||||
int dotIndex;
|
||||
Queryable modelPartContainer = this;
|
||||
while ( ( dotIndex = path.indexOf( '.', nextStart ) ) != -1 ) {
|
||||
modelPartContainer = (Queryable) modelPartContainer.findSubPart(
|
||||
path.substring( nextStart, dotIndex ),
|
||||
null
|
||||
);
|
||||
nextStart = dotIndex + 1;
|
||||
}
|
||||
return modelPartContainer.findSubPart( path.substring( nextStart ), null );
|
||||
}
|
||||
|
||||
default ModelPart resolveSubPart(DotIdentifierSequence path) {
|
||||
return path.resolve(
|
||||
(ModelPart) this,
|
||||
|
|
|
@ -233,7 +233,6 @@ public class BasicAttributeMapping
|
|||
getContainingTableExpression(),
|
||||
allowFkOptimization
|
||||
);
|
||||
final String tableAlias = tableReference.getIdentificationVariable();
|
||||
return expressionResolver.resolveSqlSelection(
|
||||
expressionResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
|
@ -241,7 +240,7 @@ public class BasicAttributeMapping
|
|||
mappedColumnExpression
|
||||
),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableAlias,
|
||||
tableReference,
|
||||
this,
|
||||
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
|
||||
)
|
||||
|
|
|
@ -31,7 +31,9 @@ import org.hibernate.sql.ast.Clause;
|
|||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.results.ResultsLogger;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
|
@ -129,7 +131,7 @@ public class BasicValuedCollectionPart
|
|||
TableGroup tableGroup,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
|
||||
final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, creationState );
|
||||
|
||||
//noinspection unchecked
|
||||
return new BasicResult(
|
||||
|
@ -141,17 +143,35 @@ public class BasicValuedCollectionPart
|
|||
);
|
||||
}
|
||||
|
||||
private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) {
|
||||
private SqlSelection resolveSqlSelection(
|
||||
NavigablePath navigablePath,
|
||||
TableGroup tableGroup,
|
||||
boolean allowFkOptimization,
|
||||
DomainResultCreationState creationState) {
|
||||
final SqlExpressionResolver exprResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
|
||||
|
||||
final TableGroup targetTableGroup;
|
||||
// If the index is part of the element table group, we must use that explicitly here because the index is basic
|
||||
// and thus there is no index table group registered. The logic in the PluralTableGroup prevents from looking
|
||||
// into the element table group though because the element table group navigable path is not the parent of this navigable path
|
||||
if ( nature == Nature.INDEX && collectionDescriptor.getAttributeMapping().getIndexMetadata().getIndexPropertyName() != null ) {
|
||||
targetTableGroup = ( (PluralTableGroup) tableGroup ).getElementTableGroup();
|
||||
}
|
||||
else {
|
||||
targetTableGroup = tableGroup;
|
||||
}
|
||||
final TableReference tableReference = targetTableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
getContainingTableExpression(),
|
||||
allowFkOptimization
|
||||
);
|
||||
return exprResolver.resolveSqlSelection(
|
||||
exprResolver.resolveSqlExpression(
|
||||
SqlExpressionResolver.createColumnReferenceKey(
|
||||
tableGroup.getPrimaryTableReference(),
|
||||
tableReference,
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
sqlAstProcessingState -> new ColumnReference(
|
||||
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
|
||||
tableReference,
|
||||
selectableMapping,
|
||||
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
|
||||
)
|
||||
|
@ -164,7 +184,7 @@ public class BasicValuedCollectionPart
|
|||
@Override
|
||||
public void applySqlSelections(
|
||||
NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) {
|
||||
resolveSqlSelection( tableGroup, creationState );
|
||||
resolveSqlSelection( navigablePath, tableGroup, true, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -173,7 +193,7 @@ public class BasicValuedCollectionPart
|
|||
TableGroup tableGroup,
|
||||
DomainResultCreationState creationState,
|
||||
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
|
||||
selectionConsumer.accept( resolveSqlSelection( tableGroup, creationState ), getJdbcMapping() );
|
||||
selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, creationState ), getJdbcMapping() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -223,7 +243,7 @@ public class BasicValuedCollectionPart
|
|||
final TableGroup tableGroup = creationState.getSqlAstCreationState()
|
||||
.getFromClauseAccess()
|
||||
.findTableGroup( parentNavigablePath );
|
||||
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
|
||||
final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, creationState );
|
||||
|
||||
return new BasicFetch<>(
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
|
|
|
@ -14,10 +14,8 @@ import org.hibernate.engine.FetchStyle;
|
|||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
|
@ -32,6 +30,7 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -40,7 +39,7 @@ import org.hibernate.sql.ast.spi.SqlSelection;
|
|||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
|
||||
|
@ -291,8 +290,10 @@ public class EmbeddedAttributeMapping
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||
navigablePath,
|
||||
|
@ -303,6 +304,7 @@ public class EmbeddedAttributeMapping
|
|||
null,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
@ -319,8 +321,9 @@ public class EmbeddedAttributeMapping
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
return new CompositeTableGroup(
|
||||
return new StandardVirtualTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs,
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -37,7 +38,8 @@ import org.hibernate.sql.ast.spi.SqlSelection;
|
|||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
@ -97,6 +99,8 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
TableGroup tableGroup,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
// Make sure the pre-created table group for the part is registered under its navigable path
|
||||
resolveTableGroup( navigablePath, creationState );
|
||||
return new EmbeddableResultImpl<>(
|
||||
navigablePath,
|
||||
this,
|
||||
|
@ -153,6 +157,8 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
boolean selected,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
// Make sure the pre-created table group for the part is registered under its navigable path
|
||||
resolveTableGroup( fetchablePath, creationState );
|
||||
return new EmbeddableFetchImpl(
|
||||
fetchablePath,
|
||||
this,
|
||||
|
@ -163,6 +169,24 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
);
|
||||
}
|
||||
|
||||
private TableGroup resolveTableGroup(NavigablePath fetchablePath, DomainResultCreationState creationState) {
|
||||
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
|
||||
return fromClauseAccess.resolveTableGroup(
|
||||
fetchablePath,
|
||||
np -> {
|
||||
final PluralTableGroup parentTableGroup = (PluralTableGroup) fromClauseAccess.getTableGroup( np.getParent() );
|
||||
switch ( nature ) {
|
||||
case ELEMENT:
|
||||
return parentTableGroup.getElementTableGroup();
|
||||
case INDEX:
|
||||
return parentTableGroup.getIndexTableGroup();
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "Could not find table group for: " + np );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlTuple toSqlExpression(
|
||||
TableGroup tableGroup,
|
||||
|
@ -201,8 +225,10 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||
navigablePath,
|
||||
|
@ -213,6 +239,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
null,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
@ -229,10 +256,11 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
assert lhs.getModelPart() instanceof PluralAttributeMapping;
|
||||
|
||||
return new CompositeTableGroup( navigablePath, this, lhs, fetched );
|
||||
return new StandardVirtualTableGroup( navigablePath, this, lhs, fetched );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.hibernate.metamodel.mapping.SelectableConsumer;
|
|||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
|
@ -316,6 +315,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
|
|||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
tableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.engine.FetchTiming;
|
|||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.IndexedCollection;
|
||||
import org.hibernate.mapping.Map;
|
||||
import org.hibernate.mapping.OneToMany;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
|
@ -33,12 +34,14 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
|||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Joinable;
|
||||
import org.hibernate.persister.entity.PropertyMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
|
@ -49,6 +52,7 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.OneToManyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
|
@ -95,7 +99,10 @@ public class EntityCollectionPart
|
|||
this.entityMappingType = entityMappingType;
|
||||
final String referencedPropertyName;
|
||||
if ( bootModelValue instanceof OneToMany ) {
|
||||
referencedPropertyName = null;
|
||||
final String mappedByProperty = collectionDescriptor.getMappedByProperty();
|
||||
referencedPropertyName = mappedByProperty == null || mappedByProperty.isEmpty()
|
||||
? null
|
||||
: mappedByProperty;
|
||||
}
|
||||
else {
|
||||
referencedPropertyName = ( (ToOne) bootModelValue ).getReferencedPropertyName();
|
||||
|
@ -139,7 +146,20 @@ public class EntityCollectionPart
|
|||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else if ( bootModelValue instanceof OneToMany ) {
|
||||
this.targetKeyPropertyNames = Collections.singleton( referencedPropertyName );
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
int dotIndex = -1;
|
||||
while ( ( dotIndex = referencedPropertyName.indexOf( '.', dotIndex + 1 ) ) != -1 ) {
|
||||
targetKeyPropertyNames.add( referencedPropertyName.substring( 0, dotIndex ) );
|
||||
}
|
||||
final Type propertyType = ( (PropertyMapping) entityMappingType.getEntityPersister() )
|
||||
.toType( referencedPropertyName );
|
||||
ToOneAttributeMapping.addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
referencedPropertyName,
|
||||
propertyType,
|
||||
creationProcess.getCreationContext().getSessionFactory()
|
||||
);
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else {
|
||||
final EntityMetamodel entityMetamodel = entityMappingType.getEntityPersister().getEntityMetamodel();
|
||||
|
@ -152,7 +172,7 @@ public class EntityCollectionPart
|
|||
}
|
||||
else {
|
||||
final String mapsIdAttributeName;
|
||||
if ( ( mapsIdAttributeName = ToOneAttributeMapping.mapsId( entityMappingType, referencedPropertyName ) ) != null ) {
|
||||
if ( ( mapsIdAttributeName = ToOneAttributeMapping.findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( referencedPropertyName );
|
||||
ToOneAttributeMapping.addPrefixedPropertyNames(
|
||||
|
@ -177,7 +197,32 @@ public class EntityCollectionPart
|
|||
String fkTargetModelPartName,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
if ( fkTargetModelPartName == null ) {
|
||||
fkTargetModelPart = entityMappingType.getIdentifierMapping();
|
||||
if ( nature == Nature.INDEX ) {
|
||||
final String mapKeyPropertyName = ( (Map) bootValueMapping ).getMapKeyPropertyName();
|
||||
if ( mapKeyPropertyName == null ) {
|
||||
fkTargetModelPart = entityMappingType.getIdentifierMapping();
|
||||
}
|
||||
else {
|
||||
final EntityPersister elementPersister = ( (EntityType) collectionDescriptor.getElementType() )
|
||||
.getAssociatedEntityPersister( creationProcess.getCreationContext().getSessionFactory() );
|
||||
fkTargetModelPart = elementPersister.findByPath( mapKeyPropertyName );
|
||||
if ( fkTargetModelPart == null ) {
|
||||
throw new RuntimeException( "Couldn't find model part for path [" + mapKeyPropertyName + "] on entity: " + elementPersister.getEntityName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String mappedByProperty = bootValueMapping.getMappedByProperty();
|
||||
if ( collectionDescriptor.isOneToMany() && mappedByProperty != null && !mappedByProperty.isEmpty() ) {
|
||||
fkTargetModelPart = entityMappingType.findByPath( mappedByProperty );
|
||||
if ( fkTargetModelPart == null ) {
|
||||
throw new RuntimeException( "Couldn't find model part for path [" + mappedByProperty + "] on entity: " + entityMappingType.getEntityName() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
fkTargetModelPart = entityMappingType.getIdentifierMapping();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
fkTargetModelPart = entityMappingType.findSubPart( fkTargetModelPartName, null );
|
||||
|
@ -206,15 +251,35 @@ public class EntityCollectionPart
|
|||
MappingModelCreationProcess creationProcess,
|
||||
Dialect dialect) {
|
||||
final EntityPersister associatedEntityDescriptor = creationProcess.getEntityPersister( entityType.getAssociatedEntityName() );
|
||||
final ModelPart fkTargetPart = entityType.isReferenceToPrimaryKey()
|
||||
? associatedEntityDescriptor.getIdentifierMapping()
|
||||
: associatedEntityDescriptor.findSubPart( entityType.getRHSUniqueKeyPropertyName() );
|
||||
// If this is mapped by a to-one attribute, we can use the FK of that attribute
|
||||
if ( fkTargetModelPart instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) fkTargetModelPart;
|
||||
if ( toOneAttributeMapping.getForeignKeyDescriptor() == null ) {
|
||||
throw new RuntimeException( "Not yet ready: " + toOneAttributeMapping );
|
||||
}
|
||||
return toOneAttributeMapping.getForeignKeyDescriptor();
|
||||
}
|
||||
final ModelPart fkTargetPart = fkTargetModelPart;
|
||||
|
||||
final String fkKeyTableName;
|
||||
if ( nature == Nature.INDEX ) {
|
||||
final String indexPropertyName = collectionDescriptor.getAttributeMapping()
|
||||
.getIndexMetadata()
|
||||
.getIndexPropertyName();
|
||||
if ( indexPropertyName == null ) {
|
||||
fkKeyTableName = ( (Joinable) collectionDescriptor ).getTableName();
|
||||
}
|
||||
else {
|
||||
fkKeyTableName = fkBootDescriptorSource.getTable().getQuotedName( dialect );
|
||||
}
|
||||
}
|
||||
else {
|
||||
fkKeyTableName = ( (Joinable) collectionDescriptor ).getTableName();
|
||||
}
|
||||
if ( fkTargetPart instanceof BasicValuedModelPart ) {
|
||||
final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart;
|
||||
final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor;
|
||||
final SelectableMapping keySelectableMapping = SelectableMappingImpl.from(
|
||||
collectionDescriptorAsJoinable.getTableName(),
|
||||
fkKeyTableName,
|
||||
fkBootDescriptorSource.getColumnIterator().next(),
|
||||
basicFkTargetPart.getJdbcMapping(),
|
||||
dialect,
|
||||
|
@ -284,7 +349,9 @@ public class EntityCollectionPart
|
|||
|
||||
@Override
|
||||
public ModelPart getKeyTargetMatchPart() {
|
||||
return fkTargetModelPart;
|
||||
return collectionDescriptor.isOneToMany()
|
||||
? entityMappingType.getIdentifierMapping()
|
||||
: fkTargetModelPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -308,35 +375,26 @@ public class EntityCollectionPart
|
|||
}
|
||||
|
||||
@Override
|
||||
public EntityFetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
// find or create the TableGroup associated with this `fetchablePath`
|
||||
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
|
||||
creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() );
|
||||
public ModelPart findSubPart(String name) {
|
||||
return findSubPart( name, null );
|
||||
}
|
||||
|
||||
final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup(
|
||||
fetchablePath,
|
||||
np -> {
|
||||
final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( np.getParent() );
|
||||
if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) {
|
||||
return ( (OneToManyTableGroup) parentTableGroup ).getElementTableGroup();
|
||||
}
|
||||
for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getNestedTableGroupJoins() ) {
|
||||
if ( nestedTableGroupJoin.getNavigablePath().equals( np ) ) {
|
||||
return nestedTableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException( "Could not find table group for: " + np );
|
||||
}
|
||||
);
|
||||
|
||||
return new EntityFetchJoinedImpl( fetchParent, this, partTableGroup, selected, fetchablePath, creationState );
|
||||
@Override
|
||||
public ModelPart findSubPart(String name, EntityMappingType targetType) {
|
||||
// Prefer resolving the key part of the foreign key rather than the target part if possible
|
||||
// to allow deferring the initialization of the target table group, omitting it if possible.
|
||||
// This is not possible for one-to-many associations because we need to create the target table group eagerly,
|
||||
// to preserve the cardinality. Also, the OneToManyTableGroup has no reference to the parent table group
|
||||
if ( !collectionDescriptor.isOneToMany() && targetKeyPropertyNames.contains( name ) ) {
|
||||
if ( fkDescriptor.getTargetPart() instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
return ( (ModelPartContainer) fkDescriptor.getKeyPart() ).findSubPart( name, targetType );
|
||||
}
|
||||
if ( fkTargetModelPart instanceof ToOneAttributeMapping ) {
|
||||
return fkTargetModelPart;
|
||||
}
|
||||
return fkDescriptor.getKeyPart();
|
||||
}
|
||||
return EntityValuedFetchable.super.findSubPart( name, targetType );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -355,28 +413,40 @@ public class EntityCollectionPart
|
|||
TableGroup tableGroup,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
final TableGroup partTableGroup = resolveTableGroup( navigablePath, creationState );
|
||||
return entityMappingType.createDomainResult( navigablePath, partTableGroup, resultVariable, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityFetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() );
|
||||
|
||||
final TableGroup partTableGroup = resolveTableGroup( fetchablePath, creationState );
|
||||
return new EntityFetchJoinedImpl( fetchParent, this, partTableGroup, selected, fetchablePath, creationState );
|
||||
}
|
||||
|
||||
private TableGroup resolveTableGroup(NavigablePath fetchablePath, DomainResultCreationState creationState) {
|
||||
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
|
||||
final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup(
|
||||
navigablePath,
|
||||
return fromClauseAccess.resolveTableGroup(
|
||||
fetchablePath,
|
||||
np -> {
|
||||
final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( np.getParent() );
|
||||
if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) {
|
||||
return ( (OneToManyTableGroup) parentTableGroup ).getElementTableGroup();
|
||||
final PluralTableGroup parentTableGroup = (PluralTableGroup) fromClauseAccess.getTableGroup( np.getParent() );
|
||||
switch ( nature ) {
|
||||
case ELEMENT:
|
||||
return parentTableGroup.getElementTableGroup();
|
||||
case INDEX:
|
||||
return parentTableGroup.getIndexTableGroup();
|
||||
}
|
||||
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
|
||||
navigablePath,
|
||||
parentTableGroup,
|
||||
resultVariable,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
parentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
return tableGroupJoin.getJoinedGroup();
|
||||
|
||||
throw new IllegalStateException( "Could not find table group for: " + np );
|
||||
}
|
||||
);
|
||||
|
||||
return entityMappingType.createDomainResult( navigablePath, partTableGroup, resultVariable, creationState );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -452,8 +522,10 @@ public class EntityCollectionPart
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) {
|
||||
// If this is a one-to-many, the element part is already available, so we return a TableGroupJoin "hull"
|
||||
|
@ -474,6 +546,7 @@ public class EntityCollectionPart
|
|||
null,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
final TableGroupJoin join = new TableGroupJoin(
|
||||
|
@ -483,13 +556,11 @@ public class EntityCollectionPart
|
|||
null
|
||||
);
|
||||
|
||||
final TableReference keySideTableReference = collectionTableGroup.getPrimaryTableReference();
|
||||
|
||||
lazyTableGroup.setTableGroupInitializerCallback(
|
||||
tableGroup -> join.applyPredicate(
|
||||
fkDescriptor.generateJoinPredicate(
|
||||
tableGroup.getPrimaryTableReference(),
|
||||
keySideTableReference,
|
||||
collectionTableGroup.resolveTableReference( fkDescriptor.getKeyTable() ),
|
||||
sqlAstJoinType,
|
||||
sqlExpressionResolver,
|
||||
creationContext
|
||||
|
@ -510,6 +581,7 @@ public class EntityCollectionPart
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
|
||||
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.hibernate.mapping.Component;
|
|||
import org.hibernate.mapping.IndexedCollection;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
import org.hibernate.mapping.ManyToOne;
|
||||
import org.hibernate.mapping.Map;
|
||||
import org.hibernate.mapping.OneToMany;
|
||||
import org.hibernate.mapping.OneToOne;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
|
@ -56,6 +57,7 @@ import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
|
|||
import org.hibernate.metamodel.mapping.CollectionMappingType;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.GeneratedValueResolver;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.mapping.PropertyBasedMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableMappings;
|
||||
|
@ -619,7 +621,8 @@ public class MappingModelCreationHelper {
|
|||
|
||||
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
|
||||
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
|
||||
final Dialect dialect = sessionFactory.getJdbcServices().getJdbcEnvironment().getDialect();
|
||||
final JdbcEnvironment jdbcEnvironment = sessionFactory.getJdbcServices().getJdbcEnvironment();
|
||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||
final MappingMetamodel domainModel = creationContext.getDomainModel();
|
||||
|
||||
final CollectionPersister collectionDescriptor = domainModel.findCollectionDescriptor( bootValueMapping.getRole() );
|
||||
|
@ -736,11 +739,20 @@ public class MappingModelCreationHelper {
|
|||
jtdRegistry.getDescriptor( mapJavaType ),
|
||||
collectionSemantics
|
||||
);
|
||||
|
||||
final String mapKeyTableExpression;
|
||||
if ( bootValueMapping instanceof Map && ( (Map) bootValueMapping ).getMapKeyPropertyName() != null ) {
|
||||
mapKeyTableExpression = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
|
||||
( (Map) bootValueMapping ).getIndex().getTable().getQualifiedTableName(),
|
||||
dialect
|
||||
);
|
||||
}
|
||||
else {
|
||||
mapKeyTableExpression = tableExpression;
|
||||
}
|
||||
indexDescriptor = interpretMapKey(
|
||||
bootValueMapping,
|
||||
collectionDescriptor,
|
||||
tableExpression,
|
||||
mapKeyTableExpression,
|
||||
sqlAliasStem,
|
||||
dialect,
|
||||
creationProcess
|
||||
|
@ -888,7 +900,8 @@ public class MappingModelCreationHelper {
|
|||
MappingModelCreationProcess creationProcess) {
|
||||
ModelPart attributeMappingSubPart = null;
|
||||
if ( !StringHelper.isEmpty( collectionDescriptor.getMappedByProperty() ) ) {
|
||||
attributeMappingSubPart = attributeMapping.findSubPart( collectionDescriptor.getMappedByProperty(), null );
|
||||
attributeMappingSubPart = ( (ModelPartContainer) attributeMapping.getElementDescriptor().getPartMappingType() )
|
||||
.findSubPart( collectionDescriptor.getMappedByProperty(), null );
|
||||
}
|
||||
|
||||
if ( attributeMappingSubPart instanceof ToOneAttributeMapping ) {
|
||||
|
@ -1300,13 +1313,19 @@ public class MappingModelCreationHelper {
|
|||
final Type identifierOrUniqueKeyType = entityType.getIdentifierOrUniqueKeyType(
|
||||
creationProcess.getCreationContext().getSessionFactory()
|
||||
);
|
||||
componentType = (ComponentType) identifierOrUniqueKeyType;
|
||||
if ( bootValueMapping instanceof ToOne ) {
|
||||
sorted = ( (ToOne) bootValueMapping ).isSorted();
|
||||
if ( identifierOrUniqueKeyType instanceof ComponentType ) {
|
||||
componentType = (ComponentType) identifierOrUniqueKeyType;
|
||||
if ( bootValueMapping instanceof ToOne ) {
|
||||
sorted = ( (ToOne) bootValueMapping ).isSorted();
|
||||
}
|
||||
else {
|
||||
// Assume one-to-many is sorted, because it always uses the primary key value
|
||||
sorted = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Assume one-to-many is sorted, because it always uses the primary key value
|
||||
sorted = true;
|
||||
// This happens when we have a one-to-many with a mapped-by associations that has a basic FK
|
||||
return new int[] { 0 };
|
||||
}
|
||||
}
|
||||
// Consider the reordering if available
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.hibernate.internal.util.StringHelper;
|
|||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.mapping.List;
|
||||
import org.hibernate.mapping.Map;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
|
||||
import org.hibernate.metamodel.mapping.CollectionMappingType;
|
||||
|
@ -51,6 +52,7 @@ import org.hibernate.sql.ast.spi.SqlSelection;
|
|||
import org.hibernate.sql.ast.tree.from.CollectionTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.OneToManyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
|
@ -85,6 +87,7 @@ public class PluralAttributeMappingImpl
|
|||
private final PropertyAccess propertyAccess;
|
||||
private final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess;
|
||||
private final String referencedPropertyName;
|
||||
private final String mapKeyPropertyName;
|
||||
|
||||
private final CollectionPart elementDescriptor;
|
||||
private final CollectionPart indexDescriptor;
|
||||
|
@ -135,6 +138,13 @@ public class PluralAttributeMappingImpl
|
|||
this.collectionDescriptor = collectionDescriptor;
|
||||
this.referencedPropertyName = bootDescriptor.getReferencedPropertyName();
|
||||
|
||||
if ( bootDescriptor instanceof Map ) {
|
||||
this.mapKeyPropertyName = ( (Map) bootDescriptor ).getMapKeyPropertyName();
|
||||
}
|
||||
else {
|
||||
this.mapKeyPropertyName = null;
|
||||
}
|
||||
|
||||
this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( bootDescriptor.getMappedByProperty(), '.');
|
||||
|
||||
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName );
|
||||
|
@ -146,18 +156,14 @@ public class PluralAttributeMappingImpl
|
|||
separateCollectionTable = ( (Joinable) collectionDescriptor ).getTableName();
|
||||
}
|
||||
|
||||
final int baseIndex;
|
||||
if ( bootDescriptor instanceof List ) {
|
||||
baseIndex = ( (List) bootDescriptor ).getBaseIndex();
|
||||
}
|
||||
else {
|
||||
baseIndex = -1;
|
||||
}
|
||||
indexMetadata = new IndexMetadata() {
|
||||
final int baseIndex;
|
||||
|
||||
{
|
||||
if ( bootDescriptor instanceof List ) {
|
||||
baseIndex = ( (List) bootDescriptor ).getBaseIndex();
|
||||
}
|
||||
else {
|
||||
baseIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionPart getIndexDescriptor() {
|
||||
return indexDescriptor;
|
||||
|
@ -167,6 +173,11 @@ public class PluralAttributeMappingImpl
|
|||
public int getListIndexBase() {
|
||||
return baseIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIndexPropertyName() {
|
||||
return mapKeyPropertyName;
|
||||
}
|
||||
};
|
||||
|
||||
if ( collectionDescriptor instanceof Aware ) {
|
||||
|
@ -443,6 +454,7 @@ public class PluralAttributeMappingImpl
|
|||
null,
|
||||
SqlAstJoinType.LEFT,
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
@ -496,8 +508,10 @@ public class PluralAttributeMappingImpl
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final java.util.List<Predicate> predicates = new ArrayList<>( 2 );
|
||||
final TableGroup tableGroup = createRootTableGroupJoin(
|
||||
|
@ -509,6 +523,7 @@ public class PluralAttributeMappingImpl
|
|||
predicates::add,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
|
@ -531,6 +546,7 @@ public class PluralAttributeMappingImpl
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final CollectionPersister collectionDescriptor = getCollectionDescriptor();
|
||||
final TableGroup tableGroup;
|
||||
|
@ -542,6 +558,7 @@ public class PluralAttributeMappingImpl
|
|||
explicitSourceAlias,
|
||||
aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ),
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -553,6 +570,7 @@ public class PluralAttributeMappingImpl
|
|||
explicitSourceAlias,
|
||||
aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ),
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -582,6 +600,7 @@ public class PluralAttributeMappingImpl
|
|||
String sourceAlias,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup elementTableGroup = ( (EntityCollectionPart) elementDescriptor ).createTableGroupInternal(
|
||||
canUseInnerJoins,
|
||||
|
@ -598,15 +617,17 @@ public class PluralAttributeMappingImpl
|
|||
creationContext.getSessionFactory()
|
||||
);
|
||||
|
||||
if ( indexDescriptor instanceof EntityCollectionPart ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) indexDescriptor ).createTableGroupJoin(
|
||||
if ( indexDescriptor instanceof TableGroupJoinProducer ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) indexDescriptor ).createTableGroupJoin(
|
||||
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
|
||||
elementTableGroup,
|
||||
tableGroup,
|
||||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
fetched,
|
||||
false,
|
||||
stem -> sqlAliasBase,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
tableGroup.registerIndexTableGroup( tableGroupJoin );
|
||||
|
@ -622,6 +643,7 @@ public class PluralAttributeMappingImpl
|
|||
String sourceAlias,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
assert !getCollectionDescriptor().isOneToMany();
|
||||
|
||||
|
@ -647,33 +669,38 @@ public class PluralAttributeMappingImpl
|
|||
creationContext.getSessionFactory()
|
||||
);
|
||||
|
||||
if ( indexDescriptor instanceof EntityCollectionPart ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) indexDescriptor ).createTableGroupJoin(
|
||||
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
|
||||
tableGroup,
|
||||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
fetched,
|
||||
stem -> sqlAliasBase,
|
||||
sqlExpressionResolver,
|
||||
creationContext
|
||||
);
|
||||
tableGroup.registerIndexTableGroup( tableGroupJoin );
|
||||
}
|
||||
if ( elementDescriptor instanceof EntityCollectionPart ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) elementDescriptor ).createTableGroupJoin(
|
||||
if ( elementDescriptor instanceof TableGroupJoinProducer ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) elementDescriptor ).createTableGroupJoin(
|
||||
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
|
||||
tableGroup,
|
||||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
fetched,
|
||||
false,
|
||||
stem -> sqlAliasBase,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
tableGroup.registerElementTableGroup( tableGroupJoin );
|
||||
}
|
||||
|
||||
if ( indexDescriptor instanceof TableGroupJoinProducer ) {
|
||||
final TableGroupJoin tableGroupJoin = ( (TableGroupJoinProducer) indexDescriptor ).createTableGroupJoin(
|
||||
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
|
||||
tableGroup,
|
||||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
fetched,
|
||||
false,
|
||||
stem -> sqlAliasBase,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
tableGroup.registerIndexTableGroup( tableGroupJoin );
|
||||
}
|
||||
|
||||
return tableGroup;
|
||||
}
|
||||
|
||||
|
@ -692,6 +719,7 @@ public class PluralAttributeMappingImpl
|
|||
additionalPredicateCollectorAccess,
|
||||
creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ),
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationState.getFromClauseAccess(),
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -704,6 +732,7 @@ public class PluralAttributeMappingImpl
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver expressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
if ( getCollectionDescriptor().isOneToMany() ) {
|
||||
return createOneToManyTableGroup(
|
||||
|
@ -713,6 +742,7 @@ public class PluralAttributeMappingImpl
|
|||
explicitSourceAlias,
|
||||
sqlAliasBase,
|
||||
expressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -724,6 +754,7 @@ public class PluralAttributeMappingImpl
|
|||
explicitSourceAlias,
|
||||
sqlAliasBase,
|
||||
expressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.mapping.Selectable;
|
|||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.metamodel.mapping.AssociationKey;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
|
@ -40,6 +41,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
|||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
|
@ -62,6 +64,8 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
|||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.MappedByTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
|
@ -108,7 +112,23 @@ public class ToOneAttributeMapping
|
|||
private final NavigableRole navigableRole;
|
||||
|
||||
private final String sqlAliasStem;
|
||||
// The nullability of the actual FK column
|
||||
private final boolean isNullable;
|
||||
/*
|
||||
The nullability of the table on which the FK column is located
|
||||
Note that this can be null although the FK column is not nullable e.g. in the case of a join table
|
||||
|
||||
@Entity
|
||||
public class Entity1 {
|
||||
@OneToOne
|
||||
@JoinTable(name = "key_table")
|
||||
Entity2 association;
|
||||
}
|
||||
|
||||
Here the join to "key_table" is nullable, but the FK column is not null.
|
||||
Choosing an inner join for the association would be wrong though, because of the nullability of the key table,
|
||||
hence this flag is also controlling the default join type.
|
||||
*/
|
||||
private final boolean isKeyTableNullable;
|
||||
private final boolean isConstrained;
|
||||
private final boolean isIgnoreNotFound;
|
||||
|
@ -422,7 +442,9 @@ public class ToOneAttributeMapping
|
|||
else {
|
||||
this.targetKeyPropertyName = referencedPropertyName;
|
||||
final String mapsIdAttributeName;
|
||||
if ( ( mapsIdAttributeName = mapsId( entityMappingType, referencedPropertyName ) ) != null ) {
|
||||
// If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the
|
||||
// primary key columns and if so, we add the primary key property name as target key property
|
||||
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( targetKeyPropertyName );
|
||||
addPrefixedPropertyNames(
|
||||
|
@ -487,7 +509,7 @@ public class ToOneAttributeMapping
|
|||
return true;
|
||||
}
|
||||
|
||||
static String mapsId(EntityMappingType entityMappingType, String referencedPropertyName) {
|
||||
static String findMapsIdPropertyName(EntityMappingType entityMappingType, String referencedPropertyName) {
|
||||
final AbstractEntityPersister persister = (AbstractEntityPersister) entityMappingType.getEntityPersister();
|
||||
if ( Arrays.equals( persister.getKeyColumnNames(), persister.getPropertyColumnNames( referencedPropertyName ) ) ) {
|
||||
return persister.getIdentifierPropertyName();
|
||||
|
@ -616,7 +638,20 @@ public class ToOneAttributeMapping
|
|||
// Prefer resolving the key part of the foreign key rather than the target part if possible
|
||||
// This way, we don't have to register table groups the target entity type
|
||||
if ( canUseParentTableGroup && targetKeyPropertyNames.contains( name ) ) {
|
||||
return foreignKeyDescriptor.getKeyPart();
|
||||
final ModelPart fkSideModelPart;
|
||||
final ModelPart fkTargetModelPart;
|
||||
if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
|
||||
fkTargetModelPart = foreignKeyDescriptor.getTargetPart();
|
||||
fkSideModelPart = foreignKeyDescriptor.getKeyPart();
|
||||
}
|
||||
else {
|
||||
fkTargetModelPart = foreignKeyDescriptor.getKeyPart();
|
||||
fkSideModelPart = foreignKeyDescriptor.getTargetPart();
|
||||
}
|
||||
if ( fkTargetModelPart instanceof NonAggregatedIdentifierMappingImpl ) {
|
||||
return ( (ModelPartContainer) fkSideModelPart ).findSubPart( name, targetType );
|
||||
}
|
||||
return fkSideModelPart;
|
||||
}
|
||||
return EntityValuedFetchable.super.findSubPart( name, targetType );
|
||||
}
|
||||
|
@ -953,6 +988,7 @@ public class ToOneAttributeMapping
|
|||
resultVariable,
|
||||
getJoinType( fetchablePath, parentTableGroup ),
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
parentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
@ -969,6 +1005,7 @@ public class ToOneAttributeMapping
|
|||
resultVariable,
|
||||
getDefaultSqlAstJoinType( parentTableGroup ),
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
parentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
@ -1063,7 +1100,7 @@ public class ToOneAttributeMapping
|
|||
private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) {
|
||||
if ( side == ForeignKeyDescriptor.Nature.KEY ) {
|
||||
// case 1.2
|
||||
return !getKeyTargetMatchPart().getNavigableRole()
|
||||
return !foreignKeyDescriptor.getNavigableRole()
|
||||
.equals( entityMappingType.getIdentifierMapping().getNavigableRole() );
|
||||
}
|
||||
else {
|
||||
|
@ -1093,6 +1130,7 @@ public class ToOneAttributeMapping
|
|||
null,
|
||||
getDefaultSqlAstJoinType( tableGroup ),
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
tableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
@ -1150,10 +1188,88 @@ public class ToOneAttributeMapping
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
// Make sure the lhs is never a plural table group directly, but always a table group for a part
|
||||
// This is vital for the map key property check that comes next
|
||||
assert !( lhs instanceof PluralTableGroup );
|
||||
|
||||
TableGroup parentTableGroup = lhs;
|
||||
ModelPartContainer parentContainer = lhs.getModelPart();
|
||||
StringBuilder embeddablePathSb = null;
|
||||
// Traverse up embeddable table groups until we find a table group for a collection part
|
||||
while ( !( parentContainer instanceof CollectionPart ) ) {
|
||||
if ( parentContainer instanceof EmbeddableValuedModelPart ) {
|
||||
if ( embeddablePathSb == null ) {
|
||||
embeddablePathSb = new StringBuilder();
|
||||
}
|
||||
embeddablePathSb.insert( 0, parentContainer.getPartName() + "." );
|
||||
parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() );
|
||||
parentContainer = parentTableGroup.getModelPart();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If a parent is a collection part, there is no custom predicate and the join is INNER or LEFT
|
||||
// we check if this attribute is the map key property to reuse the existing index table group
|
||||
if ( CollectionPart.Nature.ELEMENT.getName().equals( parentTableGroup.getNavigablePath().getUnaliasedLocalName() )
|
||||
&& !addsPredicate && ( sqlAstJoinType == SqlAstJoinType.INNER || sqlAstJoinType == SqlAstJoinType.LEFT ) ) {
|
||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) fromClauseAccess.findTableGroup(
|
||||
parentTableGroup.getNavigablePath().getParent()
|
||||
);
|
||||
final String indexPropertyName = pluralTableGroup.getModelPart()
|
||||
.getIndexMetadata()
|
||||
.getIndexPropertyName();
|
||||
final String pathName;
|
||||
if ( embeddablePathSb != null ) {
|
||||
pathName = embeddablePathSb.append( getAttributeName() ).toString();
|
||||
}
|
||||
else {
|
||||
pathName = getAttributeName();
|
||||
}
|
||||
if ( pathName.equals( indexPropertyName ) ) {
|
||||
final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup();
|
||||
// If this is the map key property, we can reuse the index table group
|
||||
initializeIfNeeded( lhs, sqlAstJoinType, indexTableGroup );
|
||||
return new TableGroupJoin(
|
||||
navigablePath,
|
||||
sqlAstJoinType,
|
||||
new MappedByTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
indexTableGroup,
|
||||
fetched,
|
||||
pluralTableGroup,
|
||||
(np, tableExpression) -> {
|
||||
if ( !canUseParentTableGroup ) {
|
||||
return false;
|
||||
}
|
||||
NavigablePath path = np.getParent();
|
||||
// Fast path
|
||||
if ( path != null && navigablePath.equals( path ) ) {
|
||||
return targetKeyPropertyNames.contains( np.getUnaliasedLocalName() )
|
||||
&& identifyingColumnsTableExpression.equals( tableExpression );
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder( np.getFullPath().length() );
|
||||
sb.append( np.getUnaliasedLocalName() );
|
||||
while ( path != null && !navigablePath.equals( path ) ) {
|
||||
sb.insert( 0, '.' );
|
||||
sb.insert( 0, path.getUnaliasedLocalName() );
|
||||
path = path.getParent();
|
||||
}
|
||||
return path != null && navigablePath.equals( path )
|
||||
&& targetKeyPropertyNames.contains( sb.toString() )
|
||||
&& identifyingColumnsTableExpression.equals( tableExpression );
|
||||
}
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
|
||||
navigablePath,
|
||||
lhs,
|
||||
|
@ -1163,6 +1279,7 @@ public class ToOneAttributeMapping
|
|||
null,
|
||||
aliasBaseGenerator,
|
||||
sqlExpressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
final TableGroupJoin join = new TableGroupJoin(
|
||||
|
@ -1186,10 +1303,7 @@ public class ToOneAttributeMapping
|
|||
)
|
||||
);
|
||||
|
||||
if ( sqlAstJoinType == SqlAstJoinType.INNER && isNullable ) {
|
||||
// Force initialization of the underlying table group join to retain cardinality
|
||||
lazyTableGroup.getPrimaryTableReference();
|
||||
}
|
||||
initializeIfNeeded( lhs, sqlAstJoinType, lazyTableGroup );
|
||||
|
||||
return join;
|
||||
}
|
||||
|
@ -1204,6 +1318,7 @@ public class ToOneAttributeMapping
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( sqlAliasStem );
|
||||
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins() && !isNullable;
|
||||
|
@ -1266,15 +1381,19 @@ public class ToOneAttributeMapping
|
|||
)
|
||||
);
|
||||
|
||||
if ( sqlAstJoinType == SqlAstJoinType.INNER && isNullable ) {
|
||||
// Force initialization of the underlying table group join to retain cardinality
|
||||
lazyTableGroup.getPrimaryTableReference();
|
||||
}
|
||||
initializeIfNeeded( lhs, sqlAstJoinType, lazyTableGroup );
|
||||
}
|
||||
|
||||
return lazyTableGroup;
|
||||
}
|
||||
|
||||
private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) {
|
||||
if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) {
|
||||
// Force initialization of the underlying table group join to retain cardinality
|
||||
tableGroup.getPrimaryTableReference();
|
||||
}
|
||||
}
|
||||
|
||||
private SqlAstJoinType getJoinType(NavigablePath navigablePath, TableGroup tableGroup) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||
if ( tableGroupJoin.getNavigablePath().equals( navigablePath ) ) {
|
||||
|
@ -1353,7 +1472,7 @@ public class ToOneAttributeMapping
|
|||
|
||||
@Override
|
||||
public ModelPart getKeyTargetMatchPart() {
|
||||
return foreignKeyDescriptor;
|
||||
return foreignKeyDescriptor.getPart( sideNature );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -82,6 +82,11 @@ public abstract class AbstractPluralAttribute<D, C, E>
|
|||
return elementPathSource.findSubPathSource( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPathSource<?> getIntermediatePathSource(SqmPathSource<?> pathSource) {
|
||||
return pathSource == elementPathSource ? null : elementPathSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionType getCollectionType() {
|
||||
return getCollectionClassification().toJpaClassification();
|
||||
|
@ -130,10 +135,15 @@ public abstract class AbstractPluralAttribute<D, C, E>
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<E> createSqmPath(SqmPath<?> lhs) {
|
||||
final NavigablePath navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
//noinspection unchecked
|
||||
return new SqmPluralValuedSimplePath(
|
||||
public SqmPath<E> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
final NavigablePath navigablePath;
|
||||
if ( intermediatePathSource == null ) {
|
||||
navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
}
|
||||
else {
|
||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||
}
|
||||
return new SqmPluralValuedSimplePath<>(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs,
|
||||
|
|
|
@ -46,9 +46,15 @@ public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
|
||||
final NavigablePath navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
return new SqmAnyValuedSimplePath( navigablePath, this, lhs, lhs.nodeBuilder() );
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
final NavigablePath navigablePath;
|
||||
if ( intermediatePathSource == null ) {
|
||||
navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
}
|
||||
else {
|
||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||
}
|
||||
return new SqmAnyValuedSimplePath<>( navigablePath, this, lhs, lhs.nodeBuilder() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,8 +40,14 @@ public class BasicSqmPathSource<J>
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
|
||||
final NavigablePath navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
final NavigablePath navigablePath;
|
||||
if ( intermediatePathSource == null ) {
|
||||
navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
}
|
||||
else {
|
||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||
}
|
||||
return new SqmBasicValuedSimplePath<>(
|
||||
navigablePath,
|
||||
this,
|
||||
|
|
|
@ -35,7 +35,7 @@ public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<D> createSqmPath(SqmPath<?> lhs) {
|
||||
public SqmPath<D> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
return new DiscriminatorSqmPath( this, lhs, entityDomainType, entityMapping, lhs.nodeBuilder() );
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.metamodel.model.domain.internal;
|
|||
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
|
@ -48,9 +49,16 @@ public class EmbeddedSqmPathSource<J>
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
final NavigablePath navigablePath;
|
||||
if ( intermediatePathSource == null ) {
|
||||
navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
}
|
||||
else {
|
||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||
}
|
||||
return new SqmEmbeddedValuedSimplePath<>(
|
||||
lhs.getNavigablePath().append( getPathName() ),
|
||||
navigablePath,
|
||||
this,
|
||||
lhs,
|
||||
lhs.nodeBuilder()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.metamodel.model.domain.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
|
@ -35,9 +36,16 @@ public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
final NavigablePath navigablePath;
|
||||
if ( intermediatePathSource == null ) {
|
||||
navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
}
|
||||
else {
|
||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||
}
|
||||
return new SqmEntityValuedSimplePath<>(
|
||||
lhs.getNavigablePath().append( getPathName() ),
|
||||
navigablePath,
|
||||
this,
|
||||
lhs,
|
||||
lhs.nodeBuilder()
|
||||
|
|
|
@ -186,8 +186,7 @@ public class EntityTypeImpl<J>
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(
|
||||
SqmPath<?> lhs) {
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
throw new UnsupportedOperationException(
|
||||
"EntityType cannot be used to create an SqmPath - that would be an SqmFrom which are created directly"
|
||||
);
|
||||
|
|
|
@ -59,6 +59,11 @@ class ListAttributeImpl<X, E> extends AbstractPluralAttribute<X, List<E>, E> imp
|
|||
return getElementPathSource().findSubPathSource( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPathSource<?> getIntermediatePathSource(SqmPathSource<?> pathSource) {
|
||||
return pathSource == getElementPathSource() || pathSource == indexPathSource ? null : getElementPathSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmAttributeJoin createSqmJoin(
|
||||
SqmFrom lhs,
|
||||
|
|
|
@ -69,6 +69,11 @@ class MapAttributeImpl<X, K, V> extends AbstractPluralAttribute<X, Map<K, V>, V>
|
|||
return getElementPathSource().findSubPathSource( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPathSource<?> getIntermediatePathSource(SqmPathSource<?> pathSource) {
|
||||
return pathSource == getElementPathSource() || pathSource == keyPathSource ? null : getElementPathSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleDomainType<K> getKeyType() {
|
||||
return (SimpleDomainType<K>) keyPathSource.getSqmPathType();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.metamodel.model.domain.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.ManagedDomainType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.tree.domain.NonAggregatedCompositeSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
|
@ -35,9 +36,16 @@ public class NonAggregatedCompositeSqmPathSource<J> extends AbstractSqmPathSourc
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
final NavigablePath navigablePath;
|
||||
if ( intermediatePathSource == null ) {
|
||||
navigablePath = lhs.getNavigablePath().append( getPathName() );
|
||||
}
|
||||
else {
|
||||
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
|
||||
}
|
||||
return new NonAggregatedCompositeSimplePath<>(
|
||||
lhs.getNavigablePath().append( getPathName() ),
|
||||
navigablePath,
|
||||
this,
|
||||
lhs,
|
||||
lhs.nodeBuilder()
|
||||
|
|
|
@ -221,8 +221,8 @@ public class SingularAttributeImpl<D,J>
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
|
||||
return sqmPathSource.createSqmPath( lhs );
|
||||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
return sqmPathSource.createSqmPath( lhs, intermediatePathSource );
|
||||
}
|
||||
|
||||
private class DelayedKeyTypeAccess implements Supplier<SimpleDomainType<J>>, Serializable {
|
||||
|
|
|
@ -465,6 +465,18 @@ public abstract class AbstractCollectionPersister
|
|||
indexFormulas[i] = indexForm.getFormula();
|
||||
hasFormula = true;
|
||||
}
|
||||
// Treat a mapped-by index like a formula to avoid trying to set it in insert/update
|
||||
// Previously this was a sub-query formula, but was changed to represent the proper mapping
|
||||
// which enables optimizations for queries. The old insert/update code wasn't adapted yet though.
|
||||
// For now, this is good enough, because the formula is never used anymore,
|
||||
// since all read paths go through the new code that can properly handle this case
|
||||
else if ( indexedCollection instanceof org.hibernate.mapping.Map
|
||||
&& ( (org.hibernate.mapping.Map) indexedCollection ).getMapKeyPropertyName() != null ) {
|
||||
Column indexCol = (Column) s;
|
||||
indexFormulaTemplates[i] = Template.TEMPLATE + indexCol.getQuotedName( dialect );
|
||||
indexFormulas[i] = indexCol.getQuotedName( dialect );
|
||||
hasFormula = true;
|
||||
}
|
||||
else {
|
||||
Column indexCol = (Column) s;
|
||||
indexColumnNames[i] = indexCol.getQuotedName( dialect );
|
||||
|
@ -1172,6 +1184,7 @@ public abstract class AbstractCollectionPersister
|
|||
() -> p -> {},
|
||||
new SqlAliasBaseConstant( alias ),
|
||||
sqlAstCreationState.getSqlExpressionResolver(),
|
||||
sqlAstCreationState.getFromClauseAccess(),
|
||||
getFactory()
|
||||
);
|
||||
|
||||
|
|
|
@ -210,6 +210,7 @@ import org.hibernate.sql.Template;
|
|||
import org.hibernate.sql.Update;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SimpleFromClauseAccessImpl;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
|
||||
|
@ -1321,6 +1322,7 @@ public abstract class AbstractEntityPersister
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableReference primaryTableReference = createPrimaryTableReference(
|
||||
sqlAliasBase,
|
||||
|
@ -1911,6 +1913,7 @@ public abstract class AbstractEntityPersister
|
|||
() -> p -> {},
|
||||
new SqlAliasBaseConstant( alias ),
|
||||
sqlAstCreationState.getSqlExpressionResolver(),
|
||||
sqlAstCreationState.getFromClauseAccess(),
|
||||
getFactory()
|
||||
);
|
||||
|
||||
|
|
|
@ -1315,6 +1315,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
|
||||
@Override
|
||||
public void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
|
||||
// If the base type is part of the treatedEntityNames this means we can't optimize this,
|
||||
// as the table group is e.g. returned through a select
|
||||
if ( treatedEntityNames.contains( getEntityName() ) ) {
|
||||
return;
|
||||
}
|
||||
|
@ -1324,29 +1326,33 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
for ( String treatedEntityName : treatedEntityNames ) {
|
||||
final JoinedSubclassEntityPersister subPersister = (JoinedSubclassEntityPersister) getSubclassMappingType( treatedEntityName );
|
||||
final String[] subclassTableNames = subPersister.getSubclassTableNames();
|
||||
if ( tableGroup.canUseInnerJoins() ) {
|
||||
if ( sharedSuperclassTables.isEmpty() ) {
|
||||
for ( int i = 0; i < subclassTableNames.length; i++ ) {
|
||||
if ( subPersister.isClassOrSuperclassTable[i] ) {
|
||||
sharedSuperclassTables.add( subclassTableNames[i] );
|
||||
}
|
||||
// For every treated entity name, we collect table names that are needed by all treated entity names
|
||||
// In mathematical terms, sharedSuperclassTables will be the "intersection" of the table names of all treated entities
|
||||
if ( sharedSuperclassTables.isEmpty() ) {
|
||||
for ( int i = 0; i < subclassTableNames.length; i++ ) {
|
||||
if ( subPersister.isClassOrSuperclassTable[i] ) {
|
||||
sharedSuperclassTables.add( subclassTableNames[i] );
|
||||
}
|
||||
}
|
||||
else {
|
||||
sharedSuperclassTables.retainAll( Arrays.asList( subclassTableNames ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
sharedSuperclassTables.retainAll( Arrays.asList( subclassTableNames ) );
|
||||
}
|
||||
// Add the table references for all table names of the treated entities as we have to retain these table references.
|
||||
// Table references not appearing in this set can later be pruned away
|
||||
// todo (6.0): no need to resolve all table references, only the ones needed for cardinality
|
||||
for ( int i = 0; i < subclassTableNames.length; i++ ) {
|
||||
retainedTableReferences.add( tableGroup.resolveTableReference( null, subclassTableNames[i], false ) );
|
||||
}
|
||||
}
|
||||
final List<TableReferenceJoin> tableReferenceJoins = tableGroup.getTableReferenceJoins();
|
||||
if ( sharedSuperclassTables.isEmpty() ) {
|
||||
tableReferenceJoins
|
||||
.removeIf( join -> !retainedTableReferences.contains( join.getJoinedTableReference() ) );
|
||||
}
|
||||
else {
|
||||
// The optimization is to remove all table reference joins that are not contained in the retainedTableReferences
|
||||
// In addition, we switch from a possible LEFT join, to an inner join for all sharedSuperclassTables
|
||||
// For now, we can only do this if the table group reports canUseInnerJoins or isRealTableGroup,
|
||||
// because the switch for table reference joins to INNER must be cardinality preserving.
|
||||
// If canUseInnerJoins is true, this is trivially given, but also if the table group is real
|
||||
// i.e. with parenthesis around, as that means the table reference joins will be isolated
|
||||
if ( tableGroup.canUseInnerJoins() || tableGroup.isRealTableGroup() ) {
|
||||
final TableReferenceJoin[] oldJoins = tableReferenceJoins.toArray( new TableReferenceJoin[0] );
|
||||
tableReferenceJoins.clear();
|
||||
for ( TableReferenceJoin oldJoin : oldJoins ) {
|
||||
|
@ -1368,6 +1374,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
tableReferenceJoins
|
||||
.removeIf( join -> !retainedTableReferences.contains( join.getJoinedTableReference() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.hibernate.query.ComparisonOperator;
|
|||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.InFragment;
|
||||
import org.hibernate.sql.Insert;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
|
@ -844,6 +845,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver expressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableGroup tableGroup = super.createRootTableGroup(
|
||||
canUseInnerJoins,
|
||||
|
@ -852,6 +854,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
additionalPredicateCollectorAccess,
|
||||
sqlAliasBase,
|
||||
expressionResolver,
|
||||
fromClauseAccess,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
@ -954,9 +957,12 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
|
||||
@Override
|
||||
public void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
|
||||
// If the base type is part of the treatedEntityNames this means we can't optimize this,
|
||||
// as the table group is e.g. returned through a select
|
||||
if ( treatedEntityNames.contains( getEntityName() ) ) {
|
||||
return;
|
||||
}
|
||||
// The optimization is to simply add the discriminator filter fragment for all treated entity names
|
||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||
tableReference.setPrunedTableExpression(
|
||||
"(select * from " + getTableName() + " t where " + discriminatorFilterFragment( "t", treatedEntityNames ) + ")"
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.hibernate.metamodel.mapping.SelectableMapping;
|
|||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
|
@ -255,6 +256,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver expressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext) {
|
||||
final TableReference tableReference = resolvePrimaryTableReference( sqlAliasBase );
|
||||
|
||||
|
@ -395,10 +397,13 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
|
||||
@Override
|
||||
public void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
|
||||
// If the base type is part of the treatedEntityNames this means we can't optimize this,
|
||||
// as the table group is e.g. returned through a select
|
||||
if ( treatedEntityNames.contains( getEntityName() ) ) {
|
||||
return;
|
||||
}
|
||||
final TableReference tableReference = tableGroup.resolveTableReference( getRootTableName() );
|
||||
// Replace the default union sub-query with a specially created one that only selects the tables for the treated entity names
|
||||
tableReference.setPrunedTableExpression( generateSubquery( treatedEntityNames ) );
|
||||
}
|
||||
|
||||
|
@ -527,6 +532,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
|
||||
final Dialect dialect = getFactory().getJdbcServices().getDialect();
|
||||
|
||||
// Collect all selectables of every entity subtype and group by selection expression as well as table name
|
||||
final LinkedHashMap<String, Map<String, SelectableMapping>> selectables = new LinkedHashMap<>();
|
||||
visitSubTypeAttributeMappings(
|
||||
attributeMapping -> attributeMapping.forEachSelectable(
|
||||
|
@ -534,6 +540,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
.put( selectable.getContainingTableExpression(), selectable )
|
||||
)
|
||||
);
|
||||
// Collect the concrete subclass table names for the treated entity names
|
||||
final Set<String> treatedTableNames = new HashSet<>( treated.size() );
|
||||
for ( String subclassName : treated ) {
|
||||
final UnionSubclassEntityPersister subPersister = (UnionSubclassEntityPersister) getSubclassMappingType( subclassName );
|
||||
|
@ -544,6 +551,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
}
|
||||
}
|
||||
|
||||
// Create a union sub-query for the table names, like generateSubquery(PersistentClass model, Mapping mapping)
|
||||
final StringBuilder buf = new StringBuilder( subquery.length() )
|
||||
.append( "( " );
|
||||
|
||||
|
@ -554,6 +562,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
for ( Map<String, SelectableMapping> selectableMappings : selectables.values() ) {
|
||||
SelectableMapping selectableMapping = selectableMappings.get( subclassTableName );
|
||||
if ( selectableMapping == null ) {
|
||||
// If there is no selectable mapping for a table name, we render a null expression
|
||||
selectableMapping = selectableMappings.values().iterator().next();
|
||||
final int sqlType = selectableMapping.getJdbcMapping().getJdbcTypeDescriptor()
|
||||
.getDefaultSqlTypeCode();
|
||||
|
|
|
@ -155,6 +155,16 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
|
|||
return identifierForTableGroup;
|
||||
}
|
||||
|
||||
public boolean isParent(NavigablePath navigablePath) {
|
||||
while ( navigablePath != null ) {
|
||||
if ( this.equals( navigablePath.getParent() ) ) {
|
||||
return true;
|
||||
}
|
||||
navigablePath = navigablePath.getParent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return fullPath;
|
||||
|
|
|
@ -40,11 +40,8 @@ import org.hibernate.query.results.implicit.ImplicitFetchBuilderEmbeddable;
|
|||
import org.hibernate.query.results.implicit.ImplicitFetchBuilderEntity;
|
||||
import org.hibernate.query.results.implicit.ImplicitFetchBuilderPlural;
|
||||
import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderEntity;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetchable;
|
||||
import org.hibernate.sql.results.graph.collection.internal.EntityCollectionPartTableGroup;
|
||||
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -277,23 +274,14 @@ public class Builders {
|
|||
|
||||
if ( fetchable instanceof EntityCollectionPart ) {
|
||||
final EntityCollectionPart entityCollectionPart = (EntityCollectionPart) fetchable;
|
||||
return (parent, fetchablePath, jdbcResultsMetadata, legacyFetchResolver, domainResultCreationState) -> {
|
||||
final FromClauseAccess fromClauseAccess = domainResultCreationState.getSqlAstCreationState()
|
||||
.getFromClauseAccess();
|
||||
final TableGroup collectionTableGroup = fromClauseAccess.findTableGroup( parent.getNavigablePath() );
|
||||
fromClauseAccess.registerTableGroup(
|
||||
fetchablePath,
|
||||
new EntityCollectionPartTableGroup( fetchablePath, collectionTableGroup, entityCollectionPart )
|
||||
);
|
||||
return parent.generateFetchableFetch(
|
||||
entityCollectionPart,
|
||||
fetchablePath,
|
||||
FetchTiming.IMMEDIATE,
|
||||
true,
|
||||
null,
|
||||
domainResultCreationState
|
||||
);
|
||||
};
|
||||
return (parent, fetchablePath, jdbcResultsMetadata, legacyFetchResolver, domainResultCreationState) -> parent.generateFetchableFetch(
|
||||
entityCollectionPart,
|
||||
fetchablePath,
|
||||
FetchTiming.IMMEDIATE,
|
||||
true,
|
||||
null,
|
||||
domainResultCreationState
|
||||
);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -118,8 +118,10 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
|
|||
tableAlias,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
false,
|
||||
s -> sqlAliasBase,
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationState.getFromClauseAccess(),
|
||||
creationState.getCreationContext()
|
||||
);
|
||||
ownerTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
|
|
@ -114,6 +114,7 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde
|
|||
null,
|
||||
new SqlAliasBaseConstant( tableAlias ),
|
||||
creationStateImpl,
|
||||
creationStateImpl.getFromClauseAccess(),
|
||||
creationStateImpl.getCreationContext()
|
||||
);
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
|
|||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
false,
|
||||
creationStateImpl
|
||||
);
|
||||
parentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
|
|
@ -70,6 +70,7 @@ public class ImplicitModelPartResultBuilderEmbeddable
|
|||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
false,
|
||||
creationStateImpl
|
||||
);
|
||||
|
||||
|
|
|
@ -41,10 +41,17 @@ public interface SqmPathSource<J> extends SqmExpressable<J>, Bindable<J>, SqmExp
|
|||
*/
|
||||
SqmPathSource<?> findSubPathSource(String name);
|
||||
|
||||
/**
|
||||
* Returns the intermediate SqmPathSource for a path source previously acquired via {@link #findSubPathSource(String)}.
|
||||
*/
|
||||
default SqmPathSource<?> getIntermediatePathSource(SqmPathSource<?> pathSource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SQM path for this source relative to the given left-hand side
|
||||
*/
|
||||
SqmPath<J> createSqmPath(SqmPath<?> lhs);
|
||||
SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource);
|
||||
|
||||
@Override
|
||||
default SqmExpressable<J> getExpressable() {
|
||||
|
|
|
@ -90,6 +90,8 @@ import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
|||
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot;
|
||||
import org.hibernate.sql.ast.tree.from.CorrelatedPluralTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
|
||||
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -373,8 +375,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
private Map<String, FilterPredicate> collectionFilterPredicates;
|
||||
private OrderByFragmentConsumer orderByFragmentConsumer;
|
||||
|
||||
private final Map<String, NavigablePath> joinPathBySqmJoinFullPath = new HashMap<>();
|
||||
|
||||
private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager();
|
||||
private final Stack<SqlAstProcessingState> processingStateStack = new StandardStack<>();
|
||||
private final Stack<FromClauseIndex> fromClauseIndexStack = new StandardStack<>();
|
||||
|
@ -1460,7 +1460,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
applyCollectionFilterPredicates( sqlQuerySpec );
|
||||
}
|
||||
|
||||
joinPathBySqmJoinFullPath.clear();
|
||||
return sqlQuerySpec;
|
||||
}
|
||||
finally {
|
||||
|
@ -1954,16 +1953,73 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
from.getCorrelationParent().getNavigablePath()
|
||||
);
|
||||
final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase( parentTableGroup.getGroupAlias() );
|
||||
tableGroup = new CorrelatedTableGroup(
|
||||
parentTableGroup,
|
||||
sqlAliasBase,
|
||||
currentQuerySpec,
|
||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
sessionFactory
|
||||
);
|
||||
if ( parentTableGroup instanceof PluralTableGroup ) {
|
||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) parentTableGroup;
|
||||
final CorrelatedPluralTableGroup correlatedPluralTableGroup = new CorrelatedPluralTableGroup(
|
||||
parentTableGroup,
|
||||
sqlAliasBase,
|
||||
currentQuerySpec,
|
||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
sessionFactory
|
||||
);
|
||||
final TableGroup elementTableGroup = pluralTableGroup.getElementTableGroup();
|
||||
if ( elementTableGroup != null ) {
|
||||
final TableGroup correlatedElementTableGroup = new CorrelatedTableGroup(
|
||||
elementTableGroup,
|
||||
sqlAliasBase,
|
||||
currentQuerySpec,
|
||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
sessionFactory
|
||||
);
|
||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
elementTableGroup.getNavigablePath(),
|
||||
SqlAstJoinType.INNER,
|
||||
correlatedElementTableGroup
|
||||
);
|
||||
correlatedPluralTableGroup.registerElementTableGroup( tableGroupJoin );
|
||||
}
|
||||
final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup();
|
||||
if ( indexTableGroup != null ) {
|
||||
final TableGroup correlatedIndexTableGroup = new CorrelatedTableGroup(
|
||||
indexTableGroup,
|
||||
sqlAliasBase,
|
||||
currentQuerySpec,
|
||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
sessionFactory
|
||||
);
|
||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
indexTableGroup.getNavigablePath(),
|
||||
SqlAstJoinType.INNER,
|
||||
correlatedIndexTableGroup
|
||||
);
|
||||
correlatedPluralTableGroup.registerIndexTableGroup( tableGroupJoin );
|
||||
}
|
||||
tableGroup = correlatedPluralTableGroup;
|
||||
}
|
||||
else {
|
||||
tableGroup = new CorrelatedTableGroup(
|
||||
parentTableGroup,
|
||||
sqlAliasBase,
|
||||
currentQuerySpec,
|
||||
predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates(
|
||||
additionalRestrictions,
|
||||
predicate
|
||||
),
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
|
||||
fromClauseIndex.register( from, tableGroup );
|
||||
registerPluralTableGroupParts( tableGroup );
|
||||
|
||||
log.tracef( "Resolved SqmRoot [%s] to correlated TableGroup [%s]", sqmRoot, tableGroup );
|
||||
consumeExplicitJoins( from, tableGroup );
|
||||
|
@ -2142,13 +2198,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
sqmFrom.visitSqmJoins(
|
||||
sqmJoin -> {
|
||||
registerUsage( (SqmFrom<?, ?>) sqmJoin.getLhs(), lhsTableGroup );
|
||||
consumeExplicitJoin( sqmJoin, lhsTableGroup, lhsTableGroup, true );
|
||||
final TableGroup actualTableGroup = findActualTableGroup( lhsTableGroup, sqmJoin );
|
||||
registerUsage( (SqmFrom<?, ?>) sqmJoin.getLhs(), actualTableGroup );
|
||||
consumeExplicitJoin( sqmJoin, actualTableGroup, actualTableGroup, true );
|
||||
}
|
||||
);
|
||||
for ( SqmFrom<?, ?> sqmTreat : sqmFrom.getSqmTreats() ) {
|
||||
registerUsage( sqmTreat, lhsTableGroup );
|
||||
consumeExplicitJoins( sqmTreat, lhsTableGroup );
|
||||
final TableGroup actualTableGroup = findActualTableGroup( lhsTableGroup, sqmTreat );
|
||||
registerUsage( sqmTreat, actualTableGroup );
|
||||
consumeExplicitJoins( sqmTreat, actualTableGroup );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2172,6 +2230,26 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
}
|
||||
|
||||
private TableGroup findActualTableGroup(TableGroup lhsTableGroup, SqmPath<?> path) {
|
||||
final SqmPathSource<?> intermediatePathSource;
|
||||
final SqmPath<?> lhs;
|
||||
if ( path instanceof SqmTreatedPath<?, ?> ) {
|
||||
lhs = ( (SqmTreatedPath<?, ?>) path ).getWrappedPath().getLhs();
|
||||
}
|
||||
else {
|
||||
lhs = path.getLhs();
|
||||
}
|
||||
intermediatePathSource = lhs == null ? null : lhs.getReferencedPathSource()
|
||||
.getIntermediatePathSource( path.getReferencedPathSource() );
|
||||
if ( intermediatePathSource == null ) {
|
||||
return lhsTableGroup;
|
||||
}
|
||||
// The only possible intermediate path source for now is the element path source for plural attributes
|
||||
assert intermediatePathSource.getPathName().equals( CollectionPart.Nature.ELEMENT.getName() );
|
||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) lhsTableGroup;
|
||||
return pluralTableGroup.getElementTableGroup();
|
||||
}
|
||||
|
||||
private TableGroup consumeAttributeJoin(
|
||||
SqmAttributeJoin<?, ?> sqmJoin,
|
||||
TableGroup lhsTableGroup,
|
||||
|
@ -2185,50 +2263,37 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final TableGroup joinedTableGroup;
|
||||
|
||||
final NavigablePath sqmJoinNavigablePath = sqmJoin.getNavigablePath();
|
||||
final NavigablePath parentNavigablePath = sqmJoinNavigablePath.getParent();
|
||||
|
||||
final ModelPart modelPart = ownerTableGroup.getModelPart().findSubPart(
|
||||
pathSource.getPathName(),
|
||||
SqmMappingModelHelper.resolveExplicitTreatTarget( sqmJoin, this )
|
||||
);
|
||||
|
||||
final NavigablePath joinPath;
|
||||
if ( pathSource instanceof PluralPersistentAttribute ) {
|
||||
assert modelPart instanceof PluralAttributeMapping;
|
||||
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) modelPart;
|
||||
|
||||
joinPath = getJoinNavigablePath(
|
||||
sqmJoinNavigablePath,
|
||||
parentNavigablePath,
|
||||
pluralAttributeMapping.getPartName()
|
||||
);
|
||||
|
||||
joinPathBySqmJoinFullPath.put(
|
||||
sqmJoin.getNavigablePath().getFullPath(),
|
||||
joinPath.append( CollectionPart.Nature.ELEMENT.getName() )
|
||||
);
|
||||
|
||||
joinedTableGroupJoin = pluralAttributeMapping.createTableGroupJoin(
|
||||
joinPath,
|
||||
sqmJoinNavigablePath,
|
||||
ownerTableGroup,
|
||||
sqmJoin.getExplicitAlias(),
|
||||
sqmJoinType.getCorrespondingSqlJoinType(),
|
||||
sqmJoin.isFetched(),
|
||||
sqmJoin.getJoinPredicate() != null,
|
||||
this
|
||||
);
|
||||
}
|
||||
else {
|
||||
assert modelPart instanceof TableGroupJoinProducer;
|
||||
|
||||
joinPath = getJoinNavigablePath( sqmJoinNavigablePath, parentNavigablePath, modelPart.getPartName() );
|
||||
|
||||
joinedTableGroupJoin = ( (TableGroupJoinProducer) modelPart ).createTableGroupJoin(
|
||||
joinPath,
|
||||
sqmJoinNavigablePath,
|
||||
ownerTableGroup,
|
||||
sqmJoin.getExplicitAlias(),
|
||||
sqmJoinType.getCorrespondingSqlJoinType(),
|
||||
sqmJoin.isFetched(),
|
||||
sqmJoin.getJoinPredicate() != null,
|
||||
this
|
||||
);
|
||||
|
||||
|
@ -2243,7 +2308,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
joinedTableGroup = joinedTableGroupJoin.getJoinedGroup();
|
||||
lhsTableGroup.addTableGroupJoin( joinedTableGroupJoin );
|
||||
|
||||
getFromClauseIndex().register( sqmJoin, joinedTableGroup, joinPath );
|
||||
getFromClauseIndex().register( sqmJoin, joinedTableGroup );
|
||||
registerPluralTableGroupParts( joinedTableGroup );
|
||||
|
||||
// add any additional join restrictions
|
||||
if ( sqmJoin.getJoinPredicate() != null ) {
|
||||
|
@ -2251,10 +2317,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
QueryLogging.QUERY_MESSAGE_LOGGER.debugf( "Join fetch [" + sqmJoinNavigablePath + "] is restricted" );
|
||||
}
|
||||
|
||||
if ( joinedTableGroupJoin == null ) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
final SqmJoin<?, ?> oldJoin = currentlyProcessingJoin;
|
||||
currentlyProcessingJoin = sqmJoin;
|
||||
joinedTableGroupJoin.applyPredicate( (Predicate) sqmJoin.getJoinPredicate().accept( this ) );
|
||||
|
@ -2267,24 +2329,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
return joinedTableGroup;
|
||||
}
|
||||
|
||||
private NavigablePath getJoinNavigablePath(
|
||||
NavigablePath sqmJoinNavigablePath,
|
||||
NavigablePath parentNavigablePath,
|
||||
String partName) {
|
||||
if ( parentNavigablePath == null ) {
|
||||
return sqmJoinNavigablePath;
|
||||
}
|
||||
else {
|
||||
final NavigablePath elementNavigablePath = joinPathBySqmJoinFullPath.get( parentNavigablePath.getFullPath() );
|
||||
if ( elementNavigablePath == null ) {
|
||||
return sqmJoinNavigablePath;
|
||||
}
|
||||
else {
|
||||
return elementNavigablePath.append( partName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TableGroup consumeCrossJoin(SqmCrossJoin<?> sqmJoin, TableGroup lhsTableGroup, boolean transitive) {
|
||||
final EntityPersister entityDescriptor = resolveEntityPersister( sqmJoin.getReferencedPathSource() );
|
||||
|
||||
|
@ -2450,9 +2494,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
|
||||
private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> joinedPath, boolean useInnerJoin) {
|
||||
final TableGroup actualParentTableGroup = findActualTableGroup( parentTableGroup, joinedPath );
|
||||
final SqmPath<?> lhsPath = joinedPath.getLhs();
|
||||
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
||||
final ModelPart subPart = parentTableGroup.getModelPart().findSubPart(
|
||||
final ModelPart subPart = actualParentTableGroup.getModelPart().findSubPart(
|
||||
joinedPath.getReferencedPathSource().getPathName(),
|
||||
lhsPath instanceof SqmTreatedPath
|
||||
? resolveEntityPersister( ( (SqmTreatedPath<?, ?>) lhsPath ).getTreatTarget() )
|
||||
|
@ -2471,14 +2516,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
defaultSqlAstJoinType = SqlAstJoinType.INNER;
|
||||
}
|
||||
else {
|
||||
defaultSqlAstJoinType = joinProducer.getDefaultSqlAstJoinType( parentTableGroup );
|
||||
defaultSqlAstJoinType = joinProducer.getDefaultSqlAstJoinType( actualParentTableGroup );
|
||||
}
|
||||
if ( fromClauseIndex.findTableGroupOnLeaf( parentTableGroup.getNavigablePath() ) == null ) {
|
||||
if ( fromClauseIndex.findTableGroupOnLeaf( actualParentTableGroup.getNavigablePath() ) == null ) {
|
||||
final QuerySpec querySpec = currentQuerySpec();
|
||||
// The parent table group is on a parent query, so we need a root table group
|
||||
tableGroup = joinProducer.createRootTableGroupJoin(
|
||||
joinedPath.getNavigablePath(),
|
||||
parentTableGroup,
|
||||
actualParentTableGroup,
|
||||
null,
|
||||
defaultSqlAstJoinType,
|
||||
false,
|
||||
|
@ -2492,10 +2537,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
else {
|
||||
final TableGroupJoin tableGroupJoin = joinProducer.createTableGroupJoin(
|
||||
joinedPath.getNavigablePath(),
|
||||
parentTableGroup,
|
||||
actualParentTableGroup,
|
||||
null,
|
||||
defaultSqlAstJoinType,
|
||||
false,
|
||||
false,
|
||||
this
|
||||
);
|
||||
// Implicit joins in the ON clause of attribute joins need to be added as nested table group joins
|
||||
|
@ -2505,15 +2551,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final boolean nested = currentClauseStack.getCurrent() == Clause.FROM
|
||||
&& currentlyProcessingJoin instanceof SqmAttributeJoin<?, ?>;
|
||||
if ( nested ) {
|
||||
parentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
||||
actualParentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
|
||||
}
|
||||
else {
|
||||
parentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
actualParentTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
}
|
||||
tableGroup = tableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
|
||||
fromClauseIndex.register( joinedPath, tableGroup );
|
||||
registerPluralTableGroupParts( tableGroup );
|
||||
}
|
||||
else {
|
||||
tableGroup = null;
|
||||
|
@ -2521,6 +2568,24 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
return tableGroup;
|
||||
}
|
||||
|
||||
private void registerPluralTableGroupParts(TableGroup tableGroup) {
|
||||
if ( tableGroup instanceof PluralTableGroup ) {
|
||||
final PluralTableGroup pluralTableGroup = (PluralTableGroup) tableGroup;
|
||||
if ( pluralTableGroup.getElementTableGroup() != null ) {
|
||||
getFromClauseAccess().registerTableGroup(
|
||||
pluralTableGroup.getElementTableGroup().getNavigablePath(),
|
||||
pluralTableGroup.getElementTableGroup()
|
||||
);
|
||||
}
|
||||
if ( pluralTableGroup.getIndexTableGroup() != null ) {
|
||||
getFromClauseAccess().registerTableGroup(
|
||||
pluralTableGroup.getIndexTableGroup().getNavigablePath(),
|
||||
pluralTableGroup.getIndexTableGroup()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// SqmPath handling
|
||||
// - Note that SqmFrom references defined in the FROM-clause are already
|
||||
|
@ -2885,11 +2950,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final TableGroup parentTableGroup = getFromClauseAccess().getTableGroup( pluralPath.getNavigablePath().getParent() );
|
||||
assert parentTableGroup != null;
|
||||
|
||||
final PluralAttributeMapping collectionPart = (PluralAttributeMapping) parentTableGroup.getModelPart().findSubPart(
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) parentTableGroup.getModelPart().findSubPart(
|
||||
pluralPath.getNavigablePath().getUnaliasedLocalName(),
|
||||
null
|
||||
);
|
||||
assert collectionPart != null;
|
||||
assert pluralAttributeMapping != null;
|
||||
|
||||
final QuerySpec subQuerySpec = new QuerySpec( false );
|
||||
pushProcessingState(
|
||||
|
@ -2901,7 +2966,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
)
|
||||
);
|
||||
try {
|
||||
final TableGroup tableGroup = collectionPart.createRootTableGroup(
|
||||
final TableGroup tableGroup = pluralAttributeMapping.createRootTableGroup(
|
||||
true,
|
||||
pluralPath.getNavigablePath(),
|
||||
null,
|
||||
|
@ -2911,6 +2976,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
);
|
||||
|
||||
getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup );
|
||||
registerPluralTableGroupParts( tableGroup );
|
||||
subQuerySpec.getFromClause().addRoot( tableGroup );
|
||||
|
||||
final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext
|
||||
|
@ -2932,7 +2998,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) );
|
||||
|
||||
subQuerySpec.applyPredicate(
|
||||
collectionPart.getKeyDescriptor().generateJoinPredicate(
|
||||
pluralAttributeMapping.getKeyDescriptor().generateJoinPredicate(
|
||||
parentTableGroup,
|
||||
tableGroup,
|
||||
SqlAstJoinType.INNER,
|
||||
|
@ -2971,8 +3037,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
false,
|
||||
false,
|
||||
sqlAliasBaseManager,
|
||||
getSqlExpressionResolver(),
|
||||
this,
|
||||
creationContext
|
||||
);
|
||||
|
||||
|
@ -3018,9 +3086,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
AbstractSqmSpecificPluralPartPath<?> pluralPartPath,
|
||||
boolean index,
|
||||
boolean max) {
|
||||
prepareReusablePath( pluralPartPath, () -> null );
|
||||
prepareReusablePath( pluralPartPath.getLhs(), () -> null );
|
||||
|
||||
final PluralAttributeMapping mappingModelExpressable = (PluralAttributeMapping) determineValueMapping(
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
|
||||
pluralPartPath.getPluralDomainPath() );
|
||||
final FromClauseAccess parentFromClauseAccess = getFromClauseAccess();
|
||||
final QuerySpec subQuerySpec = new QuerySpec( false );
|
||||
|
@ -3033,7 +3101,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
)
|
||||
);
|
||||
try {
|
||||
final TableGroup tableGroup = mappingModelExpressable.createRootTableGroup(
|
||||
final TableGroup tableGroup = pluralAttributeMapping.createRootTableGroup(
|
||||
true,
|
||||
pluralPartPath.getNavigablePath(),
|
||||
null,
|
||||
|
@ -3043,6 +3111,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
);
|
||||
|
||||
getFromClauseAccess().registerTableGroup( pluralPartPath.getNavigablePath(), tableGroup );
|
||||
registerPluralTableGroupParts( tableGroup );
|
||||
subQuerySpec.getFromClause().addRoot( tableGroup );
|
||||
|
||||
final AbstractSqmSelfRenderingFunctionDescriptor functionDescriptor = (AbstractSqmSelfRenderingFunctionDescriptor) creationContext
|
||||
|
@ -3051,8 +3120,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
.getSqmFunctionRegistry()
|
||||
.findFunctionDescriptor( max ? "max" : "min" );
|
||||
final CollectionPart collectionPart = index
|
||||
? mappingModelExpressable.getIndexDescriptor()
|
||||
: mappingModelExpressable.getElementDescriptor();
|
||||
? pluralAttributeMapping.getIndexDescriptor()
|
||||
: pluralAttributeMapping.getElementDescriptor();
|
||||
final ModelPart modelPart;
|
||||
if ( collectionPart instanceof EntityAssociationMapping ) {
|
||||
modelPart = ( (EntityAssociationMapping) collectionPart ).getKeyTargetMatchPart();
|
||||
|
@ -3098,7 +3167,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) );
|
||||
|
||||
subQuerySpec.applyPredicate(
|
||||
mappingModelExpressable.getKeyDescriptor().generateJoinPredicate(
|
||||
pluralAttributeMapping.getKeyDescriptor().generateJoinPredicate(
|
||||
parentFromClauseAccess.findTableGroup( pluralPartPath.getPluralDomainPath().getNavigablePath().getParent() ),
|
||||
tableGroup,
|
||||
SqlAstJoinType.INNER,
|
||||
|
@ -4668,19 +4737,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final SqmPath<?> pluralPath = predicate.getPluralPath();
|
||||
prepareReusablePath( pluralPath, () -> null );
|
||||
|
||||
final PluralAttributeMapping mappingModelExpressable = (PluralAttributeMapping) determineValueMapping(
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) determineValueMapping(
|
||||
pluralPath );
|
||||
|
||||
if ( mappingModelExpressable.getElementDescriptor() instanceof EntityCollectionPart ) {
|
||||
if ( pluralAttributeMapping.getElementDescriptor() instanceof EntityCollectionPart ) {
|
||||
inferrableTypeAccessStack.push(
|
||||
() -> ( (EntityCollectionPart) mappingModelExpressable.getElementDescriptor() ).getKeyTargetMatchPart() );
|
||||
() -> ( (EntityCollectionPart) pluralAttributeMapping.getElementDescriptor() ).getKeyTargetMatchPart() );
|
||||
}
|
||||
else if ( mappingModelExpressable.getElementDescriptor() instanceof EmbeddedCollectionPart ) {
|
||||
else if ( pluralAttributeMapping.getElementDescriptor() instanceof EmbeddedCollectionPart ) {
|
||||
inferrableTypeAccessStack.push(
|
||||
() -> mappingModelExpressable.getElementDescriptor() );
|
||||
() -> pluralAttributeMapping.getElementDescriptor() );
|
||||
}
|
||||
else {
|
||||
inferrableTypeAccessStack.push( () -> mappingModelExpressable );
|
||||
inferrableTypeAccessStack.push( () -> pluralAttributeMapping );
|
||||
}
|
||||
|
||||
final Expression lhs;
|
||||
|
@ -4702,7 +4771,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
)
|
||||
);
|
||||
try {
|
||||
final TableGroup tableGroup = mappingModelExpressable.createRootTableGroup(
|
||||
final TableGroup tableGroup = pluralAttributeMapping.createRootTableGroup(
|
||||
true,
|
||||
pluralPath.getNavigablePath(),
|
||||
null,
|
||||
|
@ -4712,9 +4781,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
);
|
||||
|
||||
getFromClauseAccess().registerTableGroup( pluralPath.getNavigablePath(), tableGroup );
|
||||
registerPluralTableGroupParts( tableGroup );
|
||||
subQuerySpec.getFromClause().addRoot( tableGroup );
|
||||
|
||||
final CollectionPart elementDescriptor = mappingModelExpressable.getElementDescriptor();
|
||||
final CollectionPart elementDescriptor = pluralAttributeMapping.getElementDescriptor();
|
||||
if ( elementDescriptor instanceof EntityCollectionPart ) {
|
||||
( (EntityCollectionPart) elementDescriptor ).getKeyTargetMatchPart()
|
||||
.createDomainResult(
|
||||
|
@ -4734,7 +4804,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
|
||||
subQuerySpec.applyPredicate(
|
||||
mappingModelExpressable.getKeyDescriptor().generateJoinPredicate(
|
||||
pluralAttributeMapping.getKeyDescriptor().generateJoinPredicate(
|
||||
parentFromClauseAccess.findTableGroup( pluralPath.getNavigablePath().getParent() ),
|
||||
tableGroup,
|
||||
SqlAstJoinType.INNER,
|
||||
|
@ -4821,6 +4891,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
parentNavPath,
|
||||
tableGroup
|
||||
);
|
||||
registerPluralTableGroupParts( tableGroup );
|
||||
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) visitPluralValuedPath(
|
||||
sqmPluralPath
|
||||
|
@ -4834,8 +4905,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
sqmPluralPath.getExplicitAlias(),
|
||||
SqlAstJoinType.INNER,
|
||||
false,
|
||||
false,
|
||||
sqlAliasBaseManager,
|
||||
subQueryState,
|
||||
this,
|
||||
creationContext
|
||||
)
|
||||
);
|
||||
|
@ -5213,6 +5286,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
alias,
|
||||
tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ),
|
||||
true,
|
||||
false,
|
||||
this
|
||||
);
|
||||
lhs.addTableGroupJoin( tableGroupJoin );
|
||||
|
@ -5328,18 +5402,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
tableGroup
|
||||
);
|
||||
if ( manyToManyFilterPredicate != null ) {
|
||||
TableGroupJoin elementTableGroupJoin = null;
|
||||
for ( TableGroupJoin nestedTableGroupJoin : tableGroup.getNestedTableGroupJoins() ) {
|
||||
final NavigablePath navigablePath = nestedTableGroupJoin.getNavigablePath();
|
||||
if ( navigablePath.getParent() == tableGroup.getNavigablePath()
|
||||
&& CollectionPart.Nature.ELEMENT.getName().equals( navigablePath.getUnaliasedLocalName() ) ) {
|
||||
elementTableGroupJoin = nestedTableGroupJoin;
|
||||
final TableGroup parentTableGroup = getFromClauseIndex().getTableGroup( fetchParent.getNavigablePath() );
|
||||
TableGroupJoin pluralTableGroupJoin = null;
|
||||
for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
|
||||
if ( nestedTableGroupJoin.getNavigablePath() == fetchablePath ) {
|
||||
pluralTableGroupJoin = nestedTableGroupJoin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert elementTableGroupJoin != null;
|
||||
elementTableGroupJoin.applyPredicate( manyToManyFilterPredicate );
|
||||
assert pluralTableGroupJoin != null;
|
||||
pluralTableGroupJoin.applyPredicate( manyToManyFilterPredicate );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,9 @@ import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
|||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
|
||||
|
@ -104,26 +103,21 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
}
|
||||
else if ( mapping instanceof EntityAssociationMapping ) {
|
||||
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;
|
||||
final ForeignKeyDescriptor fkDescriptor = associationMapping.getForeignKeyDescriptor();
|
||||
final String lhsTable;
|
||||
final ModelPart keyTargetMatchPart = associationMapping.getKeyTargetMatchPart();
|
||||
final ModelPart lhsPart;
|
||||
boolean useKeyPart = associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY;
|
||||
if ( useKeyPart ) {
|
||||
lhsTable = fkDescriptor.getKeyTable();
|
||||
lhsPart = fkDescriptor.getKeyPart();
|
||||
if ( keyTargetMatchPart instanceof ToOneAttributeMapping ) {
|
||||
lhsPart = ( (ToOneAttributeMapping) keyTargetMatchPart ).getKeyTargetMatchPart();
|
||||
}
|
||||
else {
|
||||
lhsTable = fkDescriptor.getTargetTable();
|
||||
lhsPart = fkDescriptor.getTargetPart();
|
||||
lhsPart = keyTargetMatchPart;
|
||||
}
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
lhsTable
|
||||
);
|
||||
|
||||
if ( fkDescriptor instanceof SimpleForeignKeyDescriptor ) {
|
||||
if ( lhsPart instanceof BasicValuedModelPart ) {
|
||||
final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) lhsPart;
|
||||
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
basicValuedModelPart.getContainingTableExpression()
|
||||
);
|
||||
sqlExpression = sqlExprResolver.resolveSqlExpression(
|
||||
createColumnReferenceKey( tableReference, basicValuedModelPart.getSelectionExpression() ),
|
||||
processingState -> new ColumnReference(
|
||||
|
@ -134,21 +128,27 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
);
|
||||
}
|
||||
else {
|
||||
final List<Expression> expressions = new ArrayList<>( fkDescriptor.getJdbcTypeCount() );
|
||||
final List<Expression> expressions = new ArrayList<>( lhsPart.getJdbcTypeCount() );
|
||||
lhsPart.forEachSelectable(
|
||||
(selectionIndex, selectableMapping) -> expressions.add(
|
||||
sqlExprResolver.resolveSqlExpression(
|
||||
createColumnReferenceKey(
|
||||
tableReference,
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
processingState -> new ColumnReference(
|
||||
tableReference,
|
||||
selectableMapping,
|
||||
sessionFactory
|
||||
)
|
||||
)
|
||||
)
|
||||
(selectionIndex, selectableMapping) -> {
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
selectableMapping.getContainingTableExpression()
|
||||
);
|
||||
expressions.add(
|
||||
sqlExprResolver.resolveSqlExpression(
|
||||
createColumnReferenceKey(
|
||||
tableReference,
|
||||
selectableMapping.getSelectionExpression()
|
||||
),
|
||||
processingState -> new ColumnReference(
|
||||
tableReference,
|
||||
selectableMapping,
|
||||
sessionFactory
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
sqlExpression = new SqlTuple( expressions, lhsPart );
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
throw UnknownPathException.unknownSubPath( this, name );
|
||||
}
|
||||
|
||||
return subSource.createSqmPath( this );
|
||||
return subSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( subSource ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
|
|||
protected <X> SqmPath<X> resolvePath(String attributeName, SqmPathSource<X> pathSource) {
|
||||
if ( reusablePaths == null ) {
|
||||
reusablePaths = new HashMap<>();
|
||||
final SqmPath<X> path = pathSource.createSqmPath( this );
|
||||
final SqmPath<X> path = pathSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( pathSource ) );
|
||||
reusablePaths.put( attributeName, path );
|
||||
return path;
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
|
|||
//noinspection unchecked
|
||||
return (SqmPath<X>) reusablePaths.computeIfAbsent(
|
||||
attributeName,
|
||||
name -> pathSource.createSqmPath( this )
|
||||
name -> pathSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( pathSource ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class NonAggregatedCompositeSimplePath<T> extends SqmEntityValuedSimplePa
|
|||
throw UnknownPathException.unknownSubPath( this, name );
|
||||
}
|
||||
|
||||
return subPathSource.createSqmPath( this );
|
||||
return subPathSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( subPathSource ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -64,7 +64,7 @@ public class SqmAnyValuedSimplePath<T> extends AbstractSqmSimplePath<T> {
|
|||
throw UnknownPathException.unknownSubPath( this, name );
|
||||
}
|
||||
|
||||
return subPathSource.createSqmPath( this );
|
||||
return subPathSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( subPathSource ) );
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ public class SqmEmbeddedValuedSimplePath<T> extends AbstractSqmSimplePath<T> imp
|
|||
throw UnknownPathException.unknownSubPath( this, name );
|
||||
}
|
||||
|
||||
return subPathSource.createSqmPath( this );
|
||||
return subPathSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( subPathSource ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -40,7 +40,7 @@ public class SqmEntityValuedSimplePath<T> extends AbstractSqmSimplePath<T> {
|
|||
.getPathRegistry()
|
||||
.findPath( getLhs().getNavigablePath() ) != null;
|
||||
|
||||
return subPathSource.createSqmPath( this );
|
||||
return subPathSource.createSqmPath( this, getReferencedPathSource().getIntermediatePathSource( subPathSource ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -52,7 +52,7 @@ public class SqmIndexedCollectionAccessPath<T> extends AbstractSqmPath<T> implem
|
|||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
final SqmPathSource<?> subPathSource = getReferencedPathSource().getElementPathSource().findSubPathSource( name );
|
||||
return subPathSource.createSqmPath( this );
|
||||
return subPathSource.createSqmPath( this, getReferencedPathSource().getElementPathSource() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SqmMaxElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
|||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
if ( getPluralAttribute().getElementPathSource().getSqmPathType() instanceof ManagedDomainType ) {
|
||||
return getPluralAttribute().getElementPathSource().createSqmPath( this );
|
||||
return getPluralAttribute().getElementPathSource().createSqmPath( this, null );
|
||||
}
|
||||
|
||||
throw new SemanticException( "Collection element cannot be de-referenced : " + getPluralDomainPath().getNavigablePath() );
|
||||
|
|
|
@ -48,7 +48,7 @@ public class SqmMaxIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
|||
String name,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
return indexPathSource.createSqmPath( this );
|
||||
return indexPathSource.createSqmPath( this, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SqmMinElementPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
|||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
if ( getPluralAttribute().getElementPathSource().getSqmPathType() instanceof ManagedDomainType ) {
|
||||
return getPluralAttribute().getElementPathSource().createSqmPath( this );
|
||||
return getPluralAttribute().getElementPathSource().createSqmPath( this, null );
|
||||
}
|
||||
|
||||
throw new SemanticException( "Collection element cannot be de-referenced : " + getPluralDomainPath().getNavigablePath() );
|
||||
|
|
|
@ -48,7 +48,7 @@ public class SqmMinIndexPath<T> extends AbstractSqmSpecificPluralPartPath<T> {
|
|||
String name,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
return indexPathSource.createSqmPath( this );
|
||||
return indexPathSource.createSqmPath( this, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,6 @@ import jakarta.persistence.metamodel.SingularAttribute;
|
|||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.PathException;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.criteria.JpaPath;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
|
|
|
@ -373,7 +373,7 @@ public class SqmPolymorphicRootDescriptor<T> implements EntityDomainType<T> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<T> createSqmPath(SqmPath<?> lhs) {
|
||||
public SqmPath<T> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,7 @@ package org.hibernate.query.sqm.tree.domain;
|
|||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.TreatedNavigablePath;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.UnknownPathException;
|
||||
|
@ -104,7 +101,7 @@ public class SqmTreatedRoot<T, S extends T> extends SqmRoot<S> implements SqmTre
|
|||
throw UnknownPathException.unknownSubPath( this, name );
|
||||
}
|
||||
|
||||
return subSource.createSqmPath( this );
|
||||
return subSource.createSqmPath( this, null );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -111,12 +111,12 @@ public class SqmCrossJoin<T> extends AbstractSqmFrom<T, T> implements SqmJoin<T,
|
|||
|
||||
@Override
|
||||
public <S extends T> SqmFrom<?, S> treatAs(Class<S> treatJavaType, String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException( "Cross join treats can not be aliased" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> SqmFrom<?, S> treatAs(EntityDomainType<S> treatTarget, String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException( "Cross join treats can not be aliased" );
|
||||
}
|
||||
|
||||
public SqmCrossJoin<T> makeCopy(SqmCreationProcessingState creationProcessingState) {
|
||||
|
|
|
@ -124,12 +124,12 @@ public class SqmEntityJoin<T> extends AbstractSqmJoin<T, T> implements SqmQualif
|
|||
|
||||
@Override
|
||||
public <S extends T> SqmFrom<?, S> treatAs(Class<S> treatJavaType, String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException( "Entity join treats can not be aliased" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> SqmFrom<?, S> treatAs(EntityDomainType<S> treatTarget, String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException( "Entity join treats can not be aliased" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -172,12 +172,12 @@ public class SqmRoot<E> extends AbstractSqmFrom<E,E> implements JpaRoot<E>, Doma
|
|||
|
||||
@Override
|
||||
public <S extends E> SqmFrom<?, S> treatAs(Class<S> treatJavaType, String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException( "Root treats can not be aliased" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends E> SqmFrom<?, S> treatAs(EntityDomainType<S> treatTarget, String alias) {
|
||||
throw new UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException( "Root treats can not be aliased" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -137,6 +137,14 @@ public class SqlTreePrinter {
|
|||
);
|
||||
}
|
||||
|
||||
final List<TableGroupJoin> nestedTableGroupJoins = tableGroup.getNestedTableGroupJoins();
|
||||
if ( ! nestedTableGroupJoins.isEmpty() ) {
|
||||
logNode(
|
||||
"NestedTableGroupJoins",
|
||||
() -> tableGroup.visitNestedTableGroupJoins( this::visitTableGroupJoin )
|
||||
);
|
||||
}
|
||||
|
||||
final List<TableGroupJoin> tableGroupJoins = tableGroup.getTableGroupJoins();
|
||||
if ( ! tableGroupJoins.isEmpty() ) {
|
||||
logNode(
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.query.IllegalQueryOperationException;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
|
||||
import org.hibernate.query.FetchClauseType;
|
||||
|
@ -3358,7 +3357,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
String separator = NO_SEPARATOR;
|
||||
for ( TableGroup root : fromClause.getRoots() ) {
|
||||
appendSql( separator );
|
||||
renderTableGroup( root );
|
||||
renderTableGroup( root, null );
|
||||
separator = COMA_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
@ -3369,26 +3368,18 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected void renderTableGroup(TableGroup tableGroup) {
|
||||
// NOTE : commented out blocks render the TableGroup as a CTE
|
||||
|
||||
// if ( tableGroup.getGroupAlias() != null ) {
|
||||
// sqlAppender.appendSql( OPEN_PARENTHESIS );
|
||||
// }
|
||||
|
||||
protected void renderTableGroup(TableGroup tableGroup, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() );
|
||||
final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode );
|
||||
|
||||
renderTableReferenceJoins( tableGroup );
|
||||
|
||||
// if ( tableGroup.getGroupAlias() != null ) {
|
||||
// sqlAppender.appendSql( CLOSE_PARENTHESIS );
|
||||
// sqlAppender.appendSql( AS_KEYWORD );
|
||||
// sqlAppender.appendSql( tableGroup.getGroupAlias() );
|
||||
// }
|
||||
|
||||
processNestedTableGroupJoins( tableGroup );
|
||||
processTableGroupJoins( tableGroup );
|
||||
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
|
||||
if ( tableGroupJoinCollector != null ) {
|
||||
tableGroupJoinCollector.addAll( tableGroup.getTableGroupJoins() );
|
||||
}
|
||||
else {
|
||||
processTableGroupJoins( tableGroup );
|
||||
}
|
||||
ModelPartContainer modelPart = tableGroup.getModelPart();
|
||||
if ( modelPart instanceof AbstractEntityPersister ) {
|
||||
String[] querySpaces = (String[]) ( (AbstractEntityPersister) modelPart ).getQuerySpaces();
|
||||
|
@ -3407,32 +3398,56 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
}
|
||||
|
||||
protected void renderTableGroup(TableGroup tableGroup, Predicate predicate) {
|
||||
protected void renderTableGroup(TableGroup tableGroup, Predicate predicate, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
// Without reference joins or nested join groups, even a real table group does not need parenthesis
|
||||
final boolean realTableGroup = tableGroup.isRealTableGroup()
|
||||
&& ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() )
|
||||
|| hasTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
|
||||
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
|
||||
if ( realTableGroup ) {
|
||||
appendSql( OPEN_PARENTHESIS );
|
||||
}
|
||||
|
||||
final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() );
|
||||
final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode );
|
||||
final List<TableGroupJoin> tableGroupJoins;
|
||||
|
||||
if ( realTableGroup ) {
|
||||
// For real table groups, we collect all normal table group joins within that table group
|
||||
// The purpose of that is to render them in-order outside of the group/parenthesis
|
||||
// This is necessary for at least Derby but is also a lot easier to read
|
||||
renderTableReferenceJoins( tableGroup );
|
||||
processNestedTableGroupJoins( tableGroup );
|
||||
if ( tableGroupJoinCollector == null ) {
|
||||
tableGroupJoins = new ArrayList<>();
|
||||
processNestedTableGroupJoins( tableGroup, tableGroupJoins );
|
||||
}
|
||||
else {
|
||||
tableGroupJoins = null;
|
||||
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
|
||||
}
|
||||
appendSql( CLOSE_PARENTHESIS );
|
||||
}
|
||||
else {
|
||||
tableGroupJoins = null;
|
||||
}
|
||||
|
||||
appendSql( " on " );
|
||||
predicate.accept( this );
|
||||
|
||||
if ( !realTableGroup ) {
|
||||
renderTableReferenceJoins( tableGroup );
|
||||
processNestedTableGroupJoins( tableGroup );
|
||||
processNestedTableGroupJoins( tableGroup, tableGroupJoinCollector );
|
||||
}
|
||||
if ( tableGroupJoinCollector != null ) {
|
||||
tableGroupJoinCollector.addAll( tableGroup.getTableGroupJoins() );
|
||||
}
|
||||
else {
|
||||
if ( tableGroupJoins != null ) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) {
|
||||
processTableGroupJoin( tableGroupJoin, null );
|
||||
}
|
||||
}
|
||||
processTableGroupJoins( tableGroup );
|
||||
}
|
||||
processTableGroupJoins( tableGroup );
|
||||
|
||||
ModelPartContainer modelPart = tableGroup.getModelPart();
|
||||
if ( modelPart instanceof AbstractEntityPersister ) {
|
||||
|
@ -3452,13 +3467,22 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
}
|
||||
|
||||
private boolean hasTableGroupsToRender(List<TableGroupJoin> nestedTableGroupJoins) {
|
||||
private boolean hasNestedTableGroupsToRender(List<TableGroupJoin> nestedTableGroupJoins) {
|
||||
for ( TableGroupJoin nestedTableGroupJoin : nestedTableGroupJoins ) {
|
||||
final TableGroup joinedGroup = nestedTableGroupJoin.getJoinedGroup();
|
||||
if ( joinedGroup instanceof VirtualTableGroup ) {
|
||||
return !joinedGroup.getTableGroupJoins().isEmpty() || !joinedGroup.getNestedTableGroupJoins().isEmpty();
|
||||
final TableGroup realTableGroup;
|
||||
if ( joinedGroup instanceof LazyTableGroup ) {
|
||||
realTableGroup = ( (LazyTableGroup) joinedGroup ).getUnderlyingTableGroup();
|
||||
}
|
||||
if ( !( joinedGroup instanceof LazyTableGroup ) || ( (LazyTableGroup) joinedGroup ).isInitialized() ) {
|
||||
else {
|
||||
realTableGroup = joinedGroup;
|
||||
}
|
||||
if ( realTableGroup instanceof VirtualTableGroup ) {
|
||||
if ( hasNestedTableGroupsToRender( realTableGroup.getNestedTableGroupJoins() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ( realTableGroup != null ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3522,31 +3546,43 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected void processTableGroupJoins(TableGroup source) {
|
||||
source.visitTableGroupJoins( this::processTableGroupJoin );
|
||||
source.visitTableGroupJoins( tableGroupJoin -> processTableGroupJoin( tableGroupJoin, null ) );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected void processNestedTableGroupJoins(TableGroup source) {
|
||||
source.visitNestedTableGroupJoins( this::processTableGroupJoin );
|
||||
protected void processNestedTableGroupJoins(TableGroup source, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
source.visitNestedTableGroupJoins( tableGroupJoin -> processTableGroupJoin( tableGroupJoin, tableGroupJoinCollector ) );
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
protected void processTableGroupJoin(TableGroupJoin tableGroupJoin) {
|
||||
protected void processTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
final TableGroup joinedGroup = tableGroupJoin.getJoinedGroup();
|
||||
|
||||
if ( joinedGroup instanceof VirtualTableGroup ) {
|
||||
processTableGroupJoins( tableGroupJoin.getJoinedGroup() );
|
||||
processNestedTableGroupJoins( tableGroupJoin.getJoinedGroup() );
|
||||
final TableGroup realTableGroup;
|
||||
if ( joinedGroup instanceof LazyTableGroup ) {
|
||||
realTableGroup = ( (LazyTableGroup) joinedGroup ).getUnderlyingTableGroup();
|
||||
}
|
||||
else if ( !( joinedGroup instanceof LazyTableGroup ) || ( (LazyTableGroup) joinedGroup ).getUnderlyingTableGroup() != null ) {
|
||||
else {
|
||||
realTableGroup = joinedGroup;
|
||||
}
|
||||
|
||||
if ( realTableGroup instanceof VirtualTableGroup ) {
|
||||
processNestedTableGroupJoins( realTableGroup, tableGroupJoinCollector );
|
||||
if ( tableGroupJoinCollector != null ) {
|
||||
tableGroupJoinCollector.addAll( realTableGroup.getTableGroupJoins() );
|
||||
}
|
||||
else {
|
||||
processTableGroupJoins( realTableGroup );
|
||||
}
|
||||
}
|
||||
else if ( realTableGroup != null ) {
|
||||
appendSql( WHITESPACE );
|
||||
renderJoinType( tableGroupJoin.getJoinType() );
|
||||
|
||||
if ( tableGroupJoin.getPredicate() != null && !tableGroupJoin.getPredicate().isEmpty() ) {
|
||||
renderTableGroup( joinedGroup, tableGroupJoin.getPredicate() );
|
||||
renderTableGroup( realTableGroup, tableGroupJoin.getPredicate(), tableGroupJoinCollector );
|
||||
}
|
||||
else {
|
||||
renderTableGroup( joinedGroup );
|
||||
renderTableGroup( realTableGroup, tableGroupJoinCollector );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
|
|||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class CollectionTableGroup extends StandardTableGroup {
|
||||
public class CollectionTableGroup extends StandardTableGroup implements PluralTableGroup {
|
||||
|
||||
private TableGroup indexTableGroup;
|
||||
private TableGroup elementTableGroup;
|
||||
|
@ -51,6 +51,21 @@ public class CollectionTableGroup extends StandardTableGroup {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluralAttributeMapping getModelPart() {
|
||||
return (PluralAttributeMapping) super.getModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroup getElementTableGroup() {
|
||||
return elementTableGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroup getIndexTableGroup() {
|
||||
return indexTableGroup;
|
||||
}
|
||||
|
||||
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
|
||||
assert this.indexTableGroup == null;
|
||||
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
|
||||
|
@ -78,7 +93,7 @@ public class CollectionTableGroup extends StandardTableGroup {
|
|||
if ( tableReference != null ) {
|
||||
return tableReference;
|
||||
}
|
||||
if ( indexTableGroup != null ) {
|
||||
if ( indexTableGroup != null && ( navigablePath == null || indexTableGroup.getNavigablePath().isParent( navigablePath ) ) ) {
|
||||
final TableReference indexTableReference = indexTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
|
@ -89,7 +104,7 @@ public class CollectionTableGroup extends StandardTableGroup {
|
|||
return indexTableReference;
|
||||
}
|
||||
}
|
||||
if ( elementTableGroup != null ) {
|
||||
if ( elementTableGroup != null && ( navigablePath == null || elementTableGroup.getNavigablePath().isParent( navigablePath ) ) ) {
|
||||
final TableReference elementTableReference = elementTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
|
|
|
@ -20,6 +20,14 @@ public interface ColumnReferenceQualifier {
|
|||
return resolveTableReference( null, tableExpression, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getTableReference(NavigablePath, String, boolean, boolean)}, but will throw an exception if no
|
||||
* table reference can be found, even after resolving possible table reference joins.
|
||||
*
|
||||
* @param navigablePath The path for which to look up the table reference, may be null
|
||||
* @param tableExpression The table expression for which to look up the table reference
|
||||
* @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side
|
||||
*/
|
||||
TableReference resolveTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
|
@ -33,6 +41,14 @@ public interface ColumnReferenceQualifier {
|
|||
return getTableReference( null, tableExpression, true, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table reference for the table expression, or null if not found.
|
||||
*
|
||||
* @param navigablePath The path for which to look up the table reference, may be null
|
||||
* @param tableExpression The table expression for which to look up the table reference
|
||||
* @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side
|
||||
* @param resolve Whether to potentially create table reference joins for this table group
|
||||
*/
|
||||
TableReference getTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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.tree.from;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
|
||||
/**
|
||||
* A table group for correlated plural attributes.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements PluralTableGroup {
|
||||
|
||||
private TableGroup indexTableGroup;
|
||||
private TableGroup elementTableGroup;
|
||||
|
||||
public CorrelatedPluralTableGroup(
|
||||
TableGroup correlatedTableGroup,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
QuerySpec querySpec,
|
||||
Consumer<Predicate> joinPredicateConsumer,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( correlatedTableGroup, sqlAliasBase, querySpec, joinPredicateConsumer, sessionFactory );
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluralAttributeMapping getModelPart() {
|
||||
return (PluralAttributeMapping) super.getModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroup getElementTableGroup() {
|
||||
return elementTableGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroup getIndexTableGroup() {
|
||||
return indexTableGroup;
|
||||
}
|
||||
|
||||
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
|
||||
assert this.indexTableGroup == null;
|
||||
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
|
||||
public void registerElementTableGroup(TableGroupJoin elementTableGroupJoin) {
|
||||
assert this.elementTableGroup == null;
|
||||
this.elementTableGroup = elementTableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableReference getTableReferenceInternal(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization,
|
||||
boolean resolve) {
|
||||
final TableReference tableReference = super.getTableReferenceInternal(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
resolve
|
||||
);
|
||||
if ( tableReference != null ) {
|
||||
return tableReference;
|
||||
}
|
||||
if ( indexTableGroup != null && ( navigablePath == null || indexTableGroup.getNavigablePath().isParent( navigablePath ) ) ) {
|
||||
final TableReference indexTableReference = indexTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
resolve
|
||||
);
|
||||
if ( indexTableReference != null ) {
|
||||
return indexTableReference;
|
||||
}
|
||||
}
|
||||
if ( elementTableGroup != null && ( navigablePath == null || elementTableGroup.getNavigablePath().isParent( navigablePath ) ) ) {
|
||||
final TableReference elementTableReference = elementTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
resolve
|
||||
);
|
||||
if ( elementTableReference != null ) {
|
||||
return elementTableReference;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -56,6 +56,15 @@ public class CorrelatedTableGroup extends AbstractTableGroup {
|
|||
super.addTableGroupJoin( join );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNestedTableGroupJoin(TableGroupJoin join) {
|
||||
assert !getTableGroupJoins().contains( join );
|
||||
assert join.getJoinType() == SqlAstJoinType.INNER;
|
||||
querySpec.getFromClause().addRoot( join.getJoinedGroup() );
|
||||
joinPredicateConsumer.accept( join.getPredicate() );
|
||||
super.addNestedTableGroupJoin( join );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableReference getTableReferenceInternal(
|
||||
NavigablePath navigablePath,
|
||||
|
|
|
@ -18,6 +18,11 @@ import org.hibernate.query.NavigablePath;
|
|||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
|
||||
/**
|
||||
* The purpose of this table group is to defer creating the actual table group until it is really needed.
|
||||
* If it is not needed, we can safely skip rendering it. This is useful for ToOneAttributeMapping and EntityCollectionPart,
|
||||
* where we need a table group for the association, but aren't sure which columns are needed yet.
|
||||
* Deferring initialization enables getting away with fewer joins in case only foreign key columns are used.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class LazyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup {
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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.tree.from;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class MappedByTableGroup implements VirtualTableGroup {
|
||||
|
||||
private final NavigablePath navigablePath;
|
||||
private final ModelPartContainer modelPart;
|
||||
private final TableGroup underlyingTableGroup;
|
||||
private final boolean fetched;
|
||||
private final TableGroup parentTableGroup;
|
||||
private final BiPredicate<NavigablePath, String> navigablePathChecker;
|
||||
|
||||
public MappedByTableGroup(
|
||||
NavigablePath navigablePath,
|
||||
ModelPartContainer modelPart,
|
||||
TableGroup underlyingTableGroup,
|
||||
boolean fetched,
|
||||
TableGroup parentTableGroup,
|
||||
BiPredicate<NavigablePath, String> navigablePathChecker) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.modelPart = modelPart;
|
||||
this.underlyingTableGroup = underlyingTableGroup;
|
||||
this.fetched = fetched;
|
||||
this.parentTableGroup = parentTableGroup;
|
||||
this.navigablePathChecker = navigablePathChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPartContainer getExpressionType() {
|
||||
return getModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupAlias() {
|
||||
// none, although we could also delegate to the underlyingTableGroup's group-alias
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched() {
|
||||
return fetched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPartContainer getModelPart() {
|
||||
return modelPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceAlias() {
|
||||
return underlyingTableGroup.getSourceAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableGroupJoin> getTableGroupJoins() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableGroupJoin> getNestedTableGroupJoins() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRealTableGroup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseInnerJoins() {
|
||||
return underlyingTableGroup.canUseInnerJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableGroupJoin(TableGroupJoin join) {
|
||||
underlyingTableGroup.addTableGroupJoin( join );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNestedTableGroupJoin(TableGroupJoin join) {
|
||||
underlyingTableGroup.addNestedTableGroupJoin( join );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAffectedTableNames(Consumer<String> nameCollector) {
|
||||
underlyingTableGroup.applyAffectedTableNames( nameCollector );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference getPrimaryTableReference() {
|
||||
return underlyingTableGroup.getPrimaryTableReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableReferenceJoin> getTableReferenceJoins() {
|
||||
return underlyingTableGroup.getTableReferenceJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference resolveTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization) {
|
||||
final TableReference tableReference = getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
true
|
||||
);
|
||||
if ( tableReference == null ) {
|
||||
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
|
||||
}
|
||||
|
||||
return tableReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference getTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization,
|
||||
boolean resolve) {
|
||||
if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) {
|
||||
final TableReference reference = parentTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
resolve
|
||||
);
|
||||
if ( reference != null ) {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
return underlyingTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
resolve
|
||||
);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
|
|||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup {
|
||||
public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup, PluralTableGroup {
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final PluralAttributeMapping pluralAttributeMapping;
|
||||
private final TableGroup elementTableGroup;
|
||||
|
@ -52,10 +52,16 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem
|
|||
return sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroup getElementTableGroup() {
|
||||
return elementTableGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableGroup getIndexTableGroup() {
|
||||
return indexTableGroup;
|
||||
}
|
||||
|
||||
public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) {
|
||||
assert this.indexTableGroup == null;
|
||||
this.indexTableGroup = indexTableGroupJoin.getJoinedGroup();
|
||||
|
@ -163,7 +169,8 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem
|
|||
allowFkOptimization,
|
||||
resolve
|
||||
);
|
||||
if ( tableReference != null || indexTableGroup == null ) {
|
||||
if ( tableReference != null || indexTableGroup == null
|
||||
|| navigablePath != null && indexTableGroup.getNavigablePath().isParent( navigablePath ) ) {
|
||||
return tableReference;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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.tree.from;
|
||||
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public interface PluralTableGroup extends TableGroup {
|
||||
|
||||
PluralAttributeMapping getModelPart();
|
||||
|
||||
TableGroup getElementTableGroup();
|
||||
|
||||
TableGroup getIndexTableGroup();
|
||||
}
|
|
@ -12,6 +12,7 @@ import java.util.function.Supplier;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -33,7 +34,8 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon
|
|||
NavigablePath navigablePath,
|
||||
String explicitSourceAlias,
|
||||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAstCreationState creationState, SqlAstCreationContext creationContext);
|
||||
SqlAstCreationState creationState,
|
||||
SqlAstCreationContext creationContext);
|
||||
|
||||
TableGroup createRootTableGroup(
|
||||
boolean canUseInnerJoins,
|
||||
|
@ -42,5 +44,6 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon
|
|||
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
SqlExpressionResolver expressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext);
|
||||
}
|
||||
|
|
|
@ -11,29 +11,28 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class CompositeTableGroup implements VirtualTableGroup {
|
||||
public class StandardVirtualTableGroup implements VirtualTableGroup {
|
||||
private final NavigablePath navigablePath;
|
||||
private final EmbeddableValuedModelPart compositionMapping;
|
||||
|
||||
private final ModelPartContainer modelPart;
|
||||
private final TableGroup underlyingTableGroup;
|
||||
private final boolean fetched;
|
||||
|
||||
private List<TableGroupJoin> tableGroupJoins;
|
||||
private List<TableGroupJoin> nestedTableGroupJoins;
|
||||
|
||||
public CompositeTableGroup(
|
||||
public StandardVirtualTableGroup(
|
||||
NavigablePath navigablePath,
|
||||
EmbeddableValuedModelPart compositionMapping,
|
||||
ModelPartContainer modelPart,
|
||||
TableGroup underlyingTableGroup,
|
||||
boolean fetched) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.compositionMapping = compositionMapping;
|
||||
this.modelPart = modelPart;
|
||||
this.underlyingTableGroup = underlyingTableGroup;
|
||||
this.fetched = fetched;
|
||||
}
|
||||
|
@ -44,7 +43,7 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableValuedModelPart getExpressionType() {
|
||||
public ModelPartContainer getExpressionType() {
|
||||
return getModelPart();
|
||||
}
|
||||
|
||||
|
@ -60,8 +59,8 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableValuedModelPart getModelPart() {
|
||||
return compositionMapping;
|
||||
public ModelPartContainer getModelPart() {
|
||||
return modelPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,25 +135,35 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
return underlyingTableGroup.getTableReferenceJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference resolveTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization) {
|
||||
final TableReference tableReference = getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
true
|
||||
);
|
||||
if ( tableReference == null ) {
|
||||
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
|
||||
}
|
||||
|
||||
return tableReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference getTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization,
|
||||
boolean resolve) {
|
||||
return underlyingTableGroup.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference resolveTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization) {
|
||||
final TableReference tableReference = underlyingTableGroup.getTableReference(
|
||||
navigablePath,
|
||||
tableExpression,
|
||||
allowFkOptimization,
|
||||
true
|
||||
resolve
|
||||
);
|
||||
if ( tableReference != null ) {
|
||||
return tableReference;
|
||||
|
@ -162,7 +171,7 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
|
||||
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup()
|
||||
.getPrimaryTableReference()
|
||||
.getTableReference( navigablePath, tableExpression, allowFkOptimization, true );
|
||||
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
|
||||
if ( primaryTableReference != null ) {
|
||||
return primaryTableReference;
|
||||
}
|
||||
|
@ -170,12 +179,12 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
|
||||
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup()
|
||||
.getPrimaryTableReference()
|
||||
.getTableReference( navigablePath, tableExpression, allowFkOptimization, true );
|
||||
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
|
||||
if ( primaryTableReference != null ) {
|
||||
return primaryTableReference;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -48,6 +48,48 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
|
|||
|
||||
void addTableGroupJoin(TableGroupJoin join);
|
||||
|
||||
/**
|
||||
* A nested table group join is a join against a table group,
|
||||
* that is ensured to be joined against the primary table reference and table reference joins in isolation,
|
||||
* prior to doing other table group joins e.g.
|
||||
*
|
||||
* <code>
|
||||
* select *
|
||||
* from entity1 e
|
||||
* left join (
|
||||
* collection_table c1
|
||||
* join association a on a.id = c1.target_id
|
||||
* ) on c1.entity_id = e.id and c1.key = 1
|
||||
* </code>
|
||||
*
|
||||
* is modeled as
|
||||
*
|
||||
* <code>
|
||||
* TableGroup(
|
||||
* primaryTableReference = TableReference(entity1, e),
|
||||
* tableGroupJoins = [
|
||||
* TableGroupJoin(
|
||||
* TableGroup(
|
||||
* primaryTableReference = TableReference(collection_table, c1),
|
||||
* nestedTableGroupJoins = [
|
||||
* TableGroupJoin(
|
||||
* TableGroup(
|
||||
* primaryTableReference = TableReference(association, a)
|
||||
* )
|
||||
* )
|
||||
* ]
|
||||
* )
|
||||
* )
|
||||
* ]
|
||||
* )
|
||||
* </code>
|
||||
*
|
||||
* This is necessary to correctly retain the cardinality of an HQL join like e.g.
|
||||
*
|
||||
* <code>
|
||||
* from Entity1 e left join e.collectionAssociation c on key(c) = 1
|
||||
* </code>
|
||||
*/
|
||||
void addNestedTableGroupJoin(TableGroupJoin join);
|
||||
|
||||
void visitTableGroupJoins(Consumer<TableGroupJoin> consumer);
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
|
@ -32,6 +33,7 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAstCreationState creationState) {
|
||||
return createTableGroupJoin(
|
||||
navigablePath,
|
||||
|
@ -39,8 +41,10 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
|||
explicitSourceAlias,
|
||||
sqlAstJoinType,
|
||||
fetched,
|
||||
addsPredicate,
|
||||
creationState.getSqlAliasBaseGenerator(),
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationState.getFromClauseAccess(),
|
||||
creationState.getCreationContext()
|
||||
);
|
||||
}
|
||||
|
@ -54,8 +58,10 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
|||
String explicitSourceAlias,
|
||||
SqlAstJoinType sqlAstJoinType,
|
||||
boolean fetched,
|
||||
boolean addsPredicate,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext);
|
||||
|
||||
/**
|
||||
|
@ -78,6 +84,7 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
|||
predicateConsumer,
|
||||
creationState.getSqlAliasBaseGenerator(),
|
||||
creationState.getSqlExpressionResolver(),
|
||||
creationState.getFromClauseAccess(),
|
||||
creationState.getCreationContext()
|
||||
);
|
||||
}
|
||||
|
@ -94,5 +101,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
|
|||
Consumer<Predicate> predicateConsumer,
|
||||
SqlAliasBaseGenerator aliasBaseGenerator,
|
||||
SqlExpressionResolver sqlExpressionResolver,
|
||||
FromClauseAccess fromClauseAccess,
|
||||
SqlAstCreationContext creationContext);
|
||||
}
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* 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.results.graph.collection.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityCollectionPartTableGroup implements TableGroup {
|
||||
private final NavigablePath collectionPartPath;
|
||||
private final TableGroup collectionTableGroup;
|
||||
private final EntityCollectionPart collectionPart;
|
||||
|
||||
public EntityCollectionPartTableGroup(
|
||||
NavigablePath collectionPartPath,
|
||||
TableGroup collectionTableGroup,
|
||||
EntityCollectionPart collectionPart) {
|
||||
this.collectionPartPath = collectionPartPath;
|
||||
this.collectionTableGroup = collectionTableGroup;
|
||||
this.collectionPart = collectionPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return collectionPartPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getExpressionType() {
|
||||
return getModelPart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupAlias() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityCollectionPart getModelPart() {
|
||||
return collectionPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceAlias() {
|
||||
return collectionTableGroup.getSourceAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableGroupJoin> getTableGroupJoins() {
|
||||
return collectionTableGroup.getTableGroupJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableGroupJoin> getNestedTableGroupJoins() {
|
||||
return collectionTableGroup.getNestedTableGroupJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseInnerJoins() {
|
||||
return collectionTableGroup.canUseInnerJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTableGroupJoin(TableGroupJoin join) {
|
||||
collectionTableGroup.addTableGroupJoin( join );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNestedTableGroupJoin(TableGroupJoin join) {
|
||||
collectionTableGroup.addNestedTableGroupJoin( join );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
|
||||
collectionTableGroup.visitTableGroupJoins( consumer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
|
||||
collectionTableGroup.visitNestedTableGroupJoins( consumer );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAffectedTableNames(Consumer<String> nameCollector) {
|
||||
collectionTableGroup.applyAffectedTableNames( nameCollector );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference getPrimaryTableReference() {
|
||||
return collectionTableGroup.getPrimaryTableReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TableReferenceJoin> getTableReferenceJoins() {
|
||||
return collectionTableGroup.getTableReferenceJoins();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference getTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization,
|
||||
boolean resolve) {
|
||||
return collectionTableGroup.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve );
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference resolveTableReference(
|
||||
NavigablePath navigablePath,
|
||||
String tableExpression,
|
||||
boolean allowFkOptimization) {
|
||||
return collectionTableGroup.resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
|
|||
null,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
|
|
@ -54,6 +54,7 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
|
|||
resultVariable,
|
||||
SqlAstJoinType.INNER,
|
||||
true,
|
||||
false,
|
||||
creationState.getSqlAstCreationState()
|
||||
);
|
||||
tableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
|
|
|
@ -57,7 +57,7 @@ public class EntityDelayedFetchImpl extends AbstractNonJoinedEntityFetch {
|
|||
() -> new EntityDelayedFetchInitializer(
|
||||
parentAccess,
|
||||
navigablePath,
|
||||
getEntityValuedModelPart(),
|
||||
(ToOneAttributeMapping) getEntityValuedModelPart(),
|
||||
selectByUniqueKey,
|
||||
keyResult.createResultAssembler( creationState )
|
||||
)
|
||||
|
|
|
@ -39,9 +39,9 @@ import org.hibernate.query.sqm.internal.QuerySqmImpl;
|
|||
import org.hibernate.query.sqm.sql.SqmTranslation;
|
||||
import org.hibernate.query.sqm.sql.internal.StandardSqmTranslator;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
|
@ -168,7 +168,7 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
|
|||
TableGroup::getClass
|
||||
) );
|
||||
Map<String, Class<? extends TableGroup> > expectedTableGroupByName = new HashMap<>();
|
||||
expectedTableGroupByName.put( "homeAddress", CompositeTableGroup.class );
|
||||
expectedTableGroupByName.put( "homeAddress", StandardVirtualTableGroup.class );
|
||||
expectedTableGroupByName.put( "company", LazyTableGroup.class );
|
||||
assertThat( tableGroupByName, is( expectedTableGroupByName ) );
|
||||
} );
|
||||
|
@ -250,14 +250,16 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
|
|||
// Check the from-clause
|
||||
assertPluralAttributeJoinedGroup( sqlAst, "shipAddresses", tableGroup -> {
|
||||
if ( graphSemantic == GraphSemantic.LOAD ) {
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
|
||||
final TableGroup compositeTableGroup = tableGroup.getTableGroupJoins()
|
||||
final TableGroup compositeTableGroup = tableGroup.getNestedTableGroupJoins()
|
||||
.iterator()
|
||||
.next()
|
||||
.getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup countryTableGroup = compositeTableGroup.getTableGroupJoins()
|
||||
.iterator()
|
||||
|
@ -266,13 +268,16 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
|
|||
assertThat( countryTableGroup.getModelPart().getPartName(), is( "country" ) );
|
||||
|
||||
assertThat( countryTableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( countryTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
else {
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
} );
|
||||
|
||||
|
@ -324,7 +329,7 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
|
|||
final TableGroup joinedGroup = tableGroup.getTableGroupJoins().iterator().next().getJoinedGroup();
|
||||
assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) );
|
||||
assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) );
|
||||
assertThat( joinedGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
}
|
||||
|
||||
// util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -33,9 +33,9 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
|||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
|
@ -164,7 +164,7 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
|
|||
TableGroup::getClass
|
||||
) );
|
||||
Map<String, Class<? extends TableGroup> > expectedTableGroupByName = new HashMap<>();
|
||||
expectedTableGroupByName.put( "homeAddress", CompositeTableGroup.class );
|
||||
expectedTableGroupByName.put( "homeAddress", StandardVirtualTableGroup.class );
|
||||
expectedTableGroupByName.put( "company", LazyTableGroup.class );
|
||||
assertThat( tableGroupByName, is( expectedTableGroupByName ) );
|
||||
} );
|
||||
|
@ -245,25 +245,30 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
|
|||
// Check the from-clause
|
||||
assertPluralAttributeJoinedGroup( sqlAst, "shipAddresses", tableGroup -> {
|
||||
if ( graphSemantic == GraphSemantic.LOAD ) {
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() )
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() )
|
||||
.getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup countryTableGroup = CollectionUtils.getOnlyElement( compositeTableGroup.getTableGroupJoins() )
|
||||
.getJoinedGroup();
|
||||
assertThat( countryTableGroup.getModelPart().getPartName(), is( "country" ) );
|
||||
|
||||
assertThat( countryTableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( countryTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
else {
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
} );
|
||||
|
||||
|
@ -315,7 +320,7 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
|
|||
final TableGroup joinedGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) );
|
||||
assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) );
|
||||
assertThat( joinedGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
}
|
||||
|
||||
// util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -37,9 +37,9 @@ import org.hibernate.query.sqm.internal.QuerySqmImpl;
|
|||
import org.hibernate.query.sqm.sql.SqmTranslation;
|
||||
import org.hibernate.query.sqm.sql.internal.StandardSqmTranslator;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
|
@ -166,7 +166,7 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
|
|||
TableGroup::getClass
|
||||
) );
|
||||
Map<String, Class<? extends TableGroup> > expectedTableGroupByName = new HashMap<>();
|
||||
expectedTableGroupByName.put( "homeAddress", CompositeTableGroup.class );
|
||||
expectedTableGroupByName.put( "homeAddress", StandardVirtualTableGroup.class );
|
||||
expectedTableGroupByName.put( "company", LazyTableGroup.class );
|
||||
assertThat( tableGroupByName, is( expectedTableGroupByName ) );
|
||||
} );
|
||||
|
@ -248,14 +248,16 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
|
|||
// Check the from-clause
|
||||
assertPluralAttributeJoinedGroup( sqlAst, "shipAddresses", tableGroup -> {
|
||||
if ( graphSemantic == GraphSemantic.LOAD ) {
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
|
||||
final TableGroup compositeTableGroup = tableGroup.getTableGroupJoins()
|
||||
final TableGroup compositeTableGroup = tableGroup.getNestedTableGroupJoins()
|
||||
.iterator()
|
||||
.next()
|
||||
.getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
|
||||
final TableGroup countryTableGroup = compositeTableGroup.getTableGroupJoins()
|
||||
.iterator()
|
||||
|
@ -264,13 +266,16 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
|
|||
assertThat( countryTableGroup.getModelPart().getPartName(), is( "country" ) );
|
||||
|
||||
assertThat( countryTableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( countryTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
else {
|
||||
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
|
||||
assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( tableGroup.getNestedTableGroupJoins(), hasSize( 1 ) );
|
||||
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
|
||||
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
|
||||
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
@ -321,7 +326,7 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
|
|||
final TableGroup joinedGroup = tableGroup.getTableGroupJoins().iterator().next().getJoinedGroup();
|
||||
assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) );
|
||||
assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) );
|
||||
assertThat( joinedGroup, instanceOf( CompositeTableGroup.class ) );
|
||||
assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) );
|
||||
}
|
||||
|
||||
// util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -6,45 +6,104 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.jpa.ql;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
|
||||
import org.hibernate.test.jpa.AbstractJPATest;
|
||||
import org.hibernate.test.jpa.MapContent;
|
||||
import org.hibernate.test.jpa.MapOwner;
|
||||
import org.hibernate.test.jpa.Relationship;
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-14279")
|
||||
public class MapIssueTest extends AbstractJPATest {
|
||||
|
||||
@Override
|
||||
public String[] getMappings() {
|
||||
return new String[] {};
|
||||
}
|
||||
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { MapOwner.class, MapContent.class, Relationship.class};
|
||||
}
|
||||
@DomainModel(
|
||||
annotatedClasses = {
|
||||
MapOwner.class, MapContent.class, Relationship.class
|
||||
})
|
||||
@SessionFactory(statementInspectorClass = SQLStatementInspector.class)
|
||||
public class MapIssueTest {
|
||||
|
||||
@Test
|
||||
@RequiresDialect(value = PostgreSQLDialect.class, comment = "Requires support for using a correlated column in a join condition which H2 apparently does not support. For simplicity just run this on PostgreSQL")
|
||||
public void testWhereSubqueryMapKeyIsEntityWhereWithKey() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "select r from Relationship r where exists (select 1 from MapOwner as o left join o.contents c with key(c) = r)" ).list();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
public void testWhereSubqueryMapKeyIsEntityWhereWithKey(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select r from Relationship r where exists (select 1 from MapOwner as o left join o.contents c with key(c) = r)" ).list();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeyIsEntityWhereWithKey() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "select 1 from MapOwner as o left join o.contents c where c.id is not null" ).list();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
public void testOnlyCollectionTableJoined(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select 1 from MapOwner as o left join o.contents c where c.id is not null" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
// Assert only the collection table is joined
|
||||
statementInspector.assertNumberOfJoins( 0, 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeyJoinIsOmitted(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
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();
|
||||
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 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeyJoinIsReused(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select key(c), c from MapOwner as o left join o.contents c join c.relationship r where r.name is not null" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
// Assert 3 joins, collection table, collection element and relationship
|
||||
statementInspector.assertNumberOfJoins( 0, 3 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeyJoinIsReusedForFurtherJoin(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select key(c), c from MapOwner as o left join o.contents c join c.relationship r join r.self s where s.name is not null" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
// Assert 3 joins, collection table, collection element, relationship and self
|
||||
statementInspector.assertNumberOfJoins( 0, 4 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeyJoinIsReusedForFurtherJoinAndElementJoinIsProperlyOrdered(SessionFactoryScope scope) {
|
||||
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
|
||||
statementInspector.clear();
|
||||
scope.inTransaction(
|
||||
s -> {
|
||||
s.createQuery( "select key(c), c from MapOwner as o left join o.contents c join c.relationship r join r.self s join c.relationship2 where s.name is not null" ).list();
|
||||
statementInspector.assertExecutedCount( 1 );
|
||||
// Assert 3 joins, collection table, collection element, relationship, relationship2 and self
|
||||
statementInspector.assertNumberOfJoins( 0, 5 );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,6 @@ public class TreatKeywordTest extends BaseCoreFunctionalTestCase {
|
|||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-9862" )
|
||||
// @FailureExpected( jiraKey = "HHH-9862" )
|
||||
public void testRestrictionsOnJoinedSubclasses() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
|
|
@ -11,6 +11,8 @@ public class MapContent {
|
|||
private Long id;
|
||||
@ManyToOne(optional = false)
|
||||
private Relationship relationship;
|
||||
@ManyToOne
|
||||
private Relationship relationship2;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.hibernate.test.jpa;
|
|||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
|
||||
@Entity
|
||||
public class Relationship {
|
||||
|
@ -9,6 +10,8 @@ public class Relationship {
|
|||
@Id
|
||||
private Long id;
|
||||
private String name;
|
||||
@ManyToOne
|
||||
private Relationship self;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
|
|
@ -43,7 +43,7 @@ public final class DialectContext {
|
|||
}
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
throw new HibernateException( "Dialect class not found: " + dialectName );
|
||||
throw new HibernateException( "Dialect class not found: " + dialectName, cnfe );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
|
||||
|
|
Loading…
Reference in New Issue