use of org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchImpl 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.results.graph.entity.internal.EntityDelayedFetchImpl 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);
}
Aggregations