use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class ToOneAttributeMapping method resolveCircularFetch.
@Override
public Fetch resolveCircularFetch(NavigablePath fetchablePath, FetchParent fetchParent, FetchTiming fetchTiming, DomainResultCreationState creationState) {
final AssociationKey associationKey = foreignKeyDescriptor.getAssociationKey();
if (creationState.isAssociationKeyVisited(associationKey) || bidirectionalAttributeName != null && !creationState.isRegisteringVisitedAssociationKeys()) {
NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals(fetchParent.getNavigablePath());
/*
@Entity
public class Card {
@Id
private String id;
@ManyToOne
private CardField field;
}
@Entity
public class CardField {
@EmbeddedId
private PrimaryKey primaryKey;
}
@Embeddable
public class PrimaryKey {
@ManyToOne(optional = false)
private Card card;
@ManyToOne(optional = false)
private Key key;
}
*/
if (parentNavigablePath.getLocalName().equals(ForeignKeyDescriptor.PART_NAME)) {
// todo (6.0): maybe it's better to have a flag in creation state that marks if we are building a circular fetch domain result already to skip this?
return null;
}
ModelPart parentModelPart = creationState.resolveModelPart(parentNavigablePath);
if (parentModelPart instanceof EmbeddedIdentifierMappingImpl) {
while (parentNavigablePath instanceof EntityIdentifierNavigablePath) {
parentNavigablePath = parentNavigablePath.getParent();
assert parentNavigablePath != null;
parentModelPart = creationState.resolveModelPart(parentNavigablePath);
}
}
while (parentModelPart instanceof EmbeddableValuedFetchable) {
parentNavigablePath = parentNavigablePath.getParent();
assert parentNavigablePath != null;
parentModelPart = creationState.resolveModelPart(parentNavigablePath);
}
if (isBidirectionalAttributeName(parentNavigablePath, parentModelPart, fetchablePath, creationState)) {
return createCircularBiDirectionalFetch(fetchablePath, fetchParent, parentNavigablePath, creationState);
}
/*
class Child {
@OneToOne
private Mother mother;
}
class Mother {
@OneToOne
private Child stepMother;
}
We have a circularity but it is not bidirectional
*/
final TableGroup parentTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().getTableGroup(fetchParent.getNavigablePath());
final DomainResult<?> foreignKeyDomainResult;
assert !creationState.isResolvingCircularFetch();
try {
creationState.setResolvingCircularFetch(true);
foreignKeyDomainResult = foreignKeyDescriptor.createDomainResult(fetchablePath, parentTableGroup, sideNature, creationState);
} finally {
creationState.setResolvingCircularFetch(false);
}
return new CircularFetchImpl(this, getEntityMappingType(), fetchTiming, fetchablePath, fetchParent, this, isSelectByUniqueKey(sideNature), fetchablePath, foreignKeyDomainResult);
}
return null;
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class ToOneAttributeMapping method createCircularBiDirectionalFetch.
private Fetch createCircularBiDirectionalFetch(NavigablePath fetchablePath, FetchParent fetchParent, NavigablePath parentNavigablePath, DomainResultCreationState creationState) {
final NavigablePath referencedNavigablePath;
final boolean hasBidirectionalFetchParent;
FetchParent realFetchParent = fetchParent;
// Traverse up the embeddable fetches
while (realFetchParent.getNavigablePath() != parentNavigablePath) {
realFetchParent = ((Fetch) fetchParent).getFetchParent();
}
if (parentNavigablePath.getParent() == null) {
referencedNavigablePath = parentNavigablePath;
hasBidirectionalFetchParent = true;
} else if (CollectionPart.Nature.fromNameExact(parentNavigablePath.getUnaliasedLocalName()) != null) {
referencedNavigablePath = parentNavigablePath.getParent().getParent();
hasBidirectionalFetchParent = fetchParent instanceof Fetch && ((Fetch) fetchParent).getFetchParent() instanceof Fetch;
} else {
referencedNavigablePath = parentNavigablePath.getParent();
hasBidirectionalFetchParent = fetchParent instanceof Fetch;
}
// The referencedNavigablePath can be null if this is a collection initialization
if (referencedNavigablePath != null) {
if (hasBidirectionalFetchParent) {
// If this is the key side, we must ensure that the key is not null, so we create a domain result for it
// In the CircularBiDirectionalFetchImpl we return null if the key is null instead of the bidirectional value
final DomainResult<?> keyDomainResult;
// For now, we don't do this if the key table is nullable to avoid an additional join
if (sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable) {
keyDomainResult = foreignKeyDescriptor.createKeyDomainResult(fetchablePath, creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup(realFetchParent.getNavigablePath()), creationState);
} else {
keyDomainResult = null;
}
return new CircularBiDirectionalFetchImpl(FetchTiming.IMMEDIATE, fetchablePath, fetchParent, this, LockMode.READ, referencedNavigablePath, keyDomainResult);
} else {
// A query like `select ch from Phone p join p.callHistory ch` returns collection element domain results
// but detects that Call#phone is bidirectional in the query.
// The problem with a bidirectional fetch though is that we can't find an initializer
// because there is none, as we don't fetch the data of the parent node.
// To avoid creating another join, we create a special join fetch that uses the existing joined data
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
final TableGroup tableGroup = fromClauseAccess.getTableGroup(referencedNavigablePath);
fromClauseAccess.registerTableGroup(fetchablePath, tableGroup);
return new EntityFetchJoinedImpl(fetchParent, this, tableGroup, fetchablePath, creationState);
}
} else {
// We get here is this is a lazy collection initialization for which we know the owner is in the PC
// So we create a delayed fetch, as we are sure to find the entity in the PC
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
final NavigablePath realParent;
if (CollectionPart.Nature.fromNameExact(parentNavigablePath.getUnaliasedLocalName()) != null) {
realParent = parentNavigablePath.getParent();
} else {
realParent = parentNavigablePath;
}
final TableGroup tableGroup = fromClauseAccess.getTableGroup(realParent);
return new EntityDelayedFetchImpl(fetchParent, this, fetchablePath, foreignKeyDescriptor.createDomainResult(fetchablePath, tableGroup, sideNature, creationState), isSelectByUniqueKey(sideNature));
}
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class ToOneAttributeMapping method createRootTableGroupJoin.
@Override
public LazyTableGroup createRootTableGroupJoin(NavigablePath navigablePath, TableGroup lhs, String explicitSourceAlias, SqlAstJoinType requestedJoinType, boolean fetched, Consumer<Predicate> predicateConsumer, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, FromClauseAccess fromClauseAccess, SqlAstCreationContext creationContext) {
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase(sqlAliasStem);
final SqlAstJoinType joinType;
if (requestedJoinType == null) {
joinType = SqlAstJoinType.INNER;
} else {
joinType = requestedJoinType;
}
final boolean canUseInnerJoin = joinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins() && !isNullable;
TableGroup realParentTableGroup = lhs;
while (realParentTableGroup.getModelPart() instanceof EmbeddableValuedModelPart) {
realParentTableGroup = fromClauseAccess.findTableGroup(realParentTableGroup.getNavigablePath().getParent());
}
final TableGroupProducer tableGroupProducer;
if (realParentTableGroup instanceof CorrelatedTableGroup) {
// If the parent is a correlated table group, we can't refer to columns of the table in the outer query,
// because the context in which a column is used could be an aggregate function.
// Using a parent column in such a case would lead to an error if the parent query lacks a proper group by
tableGroupProducer = entityMappingType;
} else {
tableGroupProducer = this;
}
final LazyTableGroup lazyTableGroup = new LazyTableGroup(canUseInnerJoin, navigablePath, fetched, () -> createTableGroupInternal(canUseInnerJoin, navigablePath, fetched, null, sqlAliasBase, sqlExpressionResolver, creationContext), (np, tableExpression) -> {
if (!canUseParentTableGroup || tableGroupProducer != ToOneAttributeMapping.this) {
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);
}, tableGroupProducer, explicitSourceAlias, sqlAliasBase, creationContext.getSessionFactory(), lhs);
if (predicateConsumer != null) {
final TableReference lhsTableReference = lhs.resolveTableReference(navigablePath, identifyingColumnsTableExpression);
lazyTableGroup.setTableGroupInitializerCallback(tableGroup -> predicateConsumer.accept(foreignKeyDescriptor.generateJoinPredicate(sideNature == ForeignKeyDescriptor.Nature.TARGET ? lhsTableReference : tableGroup.getPrimaryTableReference(), sideNature == ForeignKeyDescriptor.Nature.TARGET ? tableGroup.getPrimaryTableReference() : lhsTableReference, sqlExpressionResolver, creationContext)));
}
if (realParentTableGroup instanceof CorrelatedTableGroup) {
// Force initialization of the underlying table group join to retain cardinality
lazyTableGroup.getPrimaryTableReference();
} else {
initializeIfNeeded(lhs, requestedJoinType, lazyTableGroup);
}
return lazyTableGroup;
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class ToOneAttributeMapping method generateFetch.
@Override
public EntityFetch generateFetch(FetchParent fetchParent, NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, String resultVariable, DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
final TableGroup parentTableGroup = fromClauseAccess.getTableGroup(fetchParent.getNavigablePath());
final NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals(fetchParent.getNavigablePath()) || fetchParent.getNavigablePath() instanceof TreatedNavigablePath && parentNavigablePath.equals(fetchParent.getNavigablePath().getRealParent());
if (fetchTiming == FetchTiming.IMMEDIATE && selected) {
final TableGroup tableGroup;
if (fetchParent instanceof EntityResultJoinedSubclassImpl && ((EntityPersister) fetchParent.getReferencedModePart()).findDeclaredAttributeMapping(getPartName()) == null) {
final TableGroupJoin tableGroupJoin = createTableGroupJoin(fetchablePath, parentTableGroup, resultVariable, getJoinType(fetchablePath, parentTableGroup), true, false, creationState.getSqlAstCreationState());
parentTableGroup.addTableGroupJoin(tableGroupJoin);
tableGroup = tableGroupJoin.getJoinedGroup();
fromClauseAccess.registerTableGroup(fetchablePath, tableGroup);
} else {
tableGroup = fromClauseAccess.resolveTableGroup(fetchablePath, np -> {
final TableGroupJoin tableGroupJoin = createTableGroupJoin(fetchablePath, parentTableGroup, resultVariable, getDefaultSqlAstJoinType(parentTableGroup), true, false, creationState.getSqlAstCreationState());
parentTableGroup.addTableGroupJoin(tableGroupJoin);
return tableGroupJoin.getJoinedGroup();
});
}
final boolean added = creationState.registerVisitedAssociationKey(foreignKeyDescriptor.getAssociationKey());
AssociationKey additionalAssociationKey = null;
if (cardinality == Cardinality.LOGICAL_ONE_TO_ONE && bidirectionalAttributeName != null) {
final ModelPart bidirectionalModelPart = entityMappingType.findSubPart(bidirectionalAttributeName);
// Add the inverse association key side as well to be able to resolve to a CircularFetch
if (bidirectionalModelPart instanceof ToOneAttributeMapping) {
assert bidirectionalModelPart.getPartMappingType() == declaringTableGroupProducer;
final ToOneAttributeMapping bidirectionalAttribute = (ToOneAttributeMapping) bidirectionalModelPart;
final AssociationKey secondKey = bidirectionalAttribute.getForeignKeyDescriptor().getAssociationKey();
if (creationState.registerVisitedAssociationKey(secondKey)) {
additionalAssociationKey = secondKey;
}
}
}
final EntityFetchJoinedImpl entityFetchJoined = new EntityFetchJoinedImpl(fetchParent, this, tableGroup, fetchablePath, creationState);
if (added) {
creationState.removeVisitedAssociationKey(foreignKeyDescriptor.getAssociationKey());
}
if (additionalAssociationKey != null) {
creationState.removeVisitedAssociationKey(additionalAssociationKey);
}
return entityFetchJoined;
}
/*
1. No JoinTable
Model:
EntityA{
@ManyToOne
EntityB b
}
EntityB{
@ManyToOne
EntityA a
}
Relational:
ENTITY_A( id )
ENTITY_B( id, entity_a_id)
1.1 EntityA -> EntityB : as keyResult we need ENTITY_B.id
1.2 EntityB -> EntityA : as keyResult we need ENTITY_B.entity_a_id (FK referring column)
2. JoinTable
*/
final ForeignKeyDescriptor.Nature resolvingKeySideOfForeignKey = creationState.getCurrentlyResolvingForeignKeyPart();
final ForeignKeyDescriptor.Nature side;
if (resolvingKeySideOfForeignKey == ForeignKeyDescriptor.Nature.KEY && this.sideNature == ForeignKeyDescriptor.Nature.TARGET) {
// If we are currently resolving the key part of a foreign key we do not want to add joins.
// So if the lhs of this association is the target of the FK, we have to use the KEY part to avoid a join
side = ForeignKeyDescriptor.Nature.KEY;
} else {
side = this.sideNature;
}
final DomainResult<?> keyResult = foreignKeyDescriptor.createDomainResult(fetchablePath, parentTableGroup, side, creationState);
final boolean selectByUniqueKey = isSelectByUniqueKey(side);
if (fetchTiming == FetchTiming.IMMEDIATE) {
return new EntityFetchSelectImpl(fetchParent, this, fetchablePath, keyResult, selectByUniqueKey, creationState);
}
return new EntityDelayedFetchImpl(fetchParent, this, fetchablePath, keyResult, selectByUniqueKey);
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class ToOneAttributeMapping method createTableGroupJoin.
@Override
public TableGroupJoin createTableGroupJoin(NavigablePath navigablePath, TableGroup lhs, String explicitSourceAlias, SqlAstJoinType requestedJoinType, boolean fetched, boolean addsPredicate, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, FromClauseAccess fromClauseAccess, SqlAstCreationContext creationContext) {
// 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;
}
}
final SqlAstJoinType joinType;
if (requestedJoinType == null) {
joinType = SqlAstJoinType.INNER;
} else {
joinType = requestedJoinType;
}
// 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 && (joinType == SqlAstJoinType.INNER || joinType == 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, requestedJoinType, indexTableGroup);
return new TableGroupJoin(navigablePath, joinType, 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, explicitSourceAlias, requestedJoinType, fetched, null, aliasBaseGenerator, sqlExpressionResolver, fromClauseAccess, creationContext);
final TableGroupJoin join = new TableGroupJoin(navigablePath, joinType, lazyTableGroup, null);
final TableReference lhsTableReference = lhs.resolveTableReference(navigablePath, identifyingColumnsTableExpression);
lazyTableGroup.setTableGroupInitializerCallback(tableGroup -> join.applyPredicate(foreignKeyDescriptor.generateJoinPredicate(sideNature == ForeignKeyDescriptor.Nature.TARGET ? lhsTableReference : tableGroup.getPrimaryTableReference(), sideNature == ForeignKeyDescriptor.Nature.TARGET ? tableGroup.getPrimaryTableReference() : lhsTableReference, sqlExpressionResolver, creationContext)));
return join;
}
Aggregations