HHH-15132 - Improvements for NavigablePath

- dropped `NavigablePath#fullPath` field - `#getFullPath` is now "(re)built" on demand
- adjust uses of `NavigablePath#getFullPath`
- refactor `NavigablePath` constructors
This commit is contained in:
Steve Ebersole 2022-03-21 23:39:52 -05:00
parent f474449e7d
commit e496ec45ea
27 changed files with 307 additions and 254 deletions

View File

@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
@ -662,8 +663,11 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
for ( int i = 1; i < propertyPathParts.length; i++ ) {
if ( ! ( fetchable instanceof FetchableContainer ) ) {
throw new MappingException(
"Non-terminal property path [" + navigablePath.getFullPath()
+ " did not reference FetchableContainer"
String.format(
Locale.ROOT,
"Non-terminal property path did not reference FetchableContainer - %s ",
navigablePath
)
);
}
fetchable = (Fetchable) ( (FetchableContainer) fetchable ).findSubPart( propertyPathParts[i], null );
@ -869,7 +873,11 @@ public class HbmResultSetMappingDescriptor implements NamedResultSetMappingDescr
this.tableAlias = hbmCollectionReturn.getAlias();
if ( tableAlias == null ) {
throw new MappingException(
"<return-collection/> did not specify alias [" + collectionPath.getFullPath() + "]"
String.format(
Locale.ROOT,
"<return-collection/> did not specify alias - %s",
collectionPath
)
);
}

View File

@ -407,8 +407,11 @@ public class SqlResultSetMappingDescriptor implements NamedResultSetMappingDescr
for ( int i = 1; i < propertyPathParts.length; i++ ) {
if ( ! ( subPart instanceof ModelPartContainer ) ) {
throw new MappingException(
"Non-terminal property path [" + navigablePath.getFullPath()
+ " did not reference FetchableContainer"
String.format(
Locale.ROOT,
"Non-terminal property path did not reference FetchableContainer - %s ",
navigablePath
)
);
}
navigablePath = navigablePath.append( propertyPathParts[ i ] );

View File

@ -899,8 +899,8 @@ public class ToOneAttributeMapping
final NavigablePath parentPath = grandparentNavigablePath.getParent();
// This can be null for a collection loader
if ( parentPath == null ) {
return grandparentNavigablePath.getFullPath().equals(
entityMappingType.findSubPart( bidirectionalAttributeName ).getNavigableRole().getFullPath()
return grandparentNavigablePath.equals(
entityMappingType.findSubPart( bidirectionalAttributeName ).getNavigableRole()
);
}
else {

View File

@ -78,8 +78,8 @@ public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsum
String.format(
Locale.ROOT,
"SqmQualifiedJoin predicate referred to SqmRoot [`%s`] other than the join's root [`%s`]",
pathRoot.getNavigablePath().getFullPath(),
sqmJoin.getNavigablePath().getFullPath()
pathRoot.getNavigablePath(),
sqmJoin.getNavigablePath()
)
);
}
@ -110,8 +110,8 @@ public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsum
String.format(
Locale.ROOT,
"SqmQualifiedJoin predicate referred to SqmRoot [`%s`] other than the join's root [`%s`]",
pathRoot.getNavigablePath().getFullPath(),
sqmJoin.getNavigablePath().getFullPath()
pathRoot.getNavigablePath(),
sqmJoin.getNavigablePath()
)
);
}

View File

@ -2291,7 +2291,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new NotYetImplementedFor6Exception( "Path continuation from `id()` reference not yet implemented" );
}
throw new SemanticException( "Path does not resolve to an entity type '" + sqmPath.getNavigablePath().getFullPath() + "'" );
throw new SemanticException( "Path does not resolve to an entity type '" + sqmPath.getNavigablePath() + "'" );
}
@Override
@ -2312,7 +2312,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new SemanticException(
String.format(
"Path '%s' resolved to entity type '%s' which does not define a version",
sqmPath.getNavigablePath().getFullPath(), identifiableType.getTypeName()
sqmPath.getNavigablePath(),
identifiableType.getTypeName()
)
);
}
@ -2320,7 +2321,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return sqmPath.get( versionAttribute );
}
throw new SemanticException( "Path does not resolve to an entity type '" + sqmPath.getNavigablePath().getFullPath() + "'" );
throw new SemanticException( "Path does not resolve to an entity type '" + sqmPath.getNavigablePath() + "'" );
}
@Override
@ -2341,7 +2342,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new SemanticException(
String.format(
"Path '%s' resolved to entity type '%s' which does not define a natural id",
sqmPath.getNavigablePath().getFullPath(), identifiableType.getTypeName()
sqmPath.getNavigablePath(),
identifiableType.getTypeName()
)
);
}
@ -2349,7 +2351,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new SemanticException(
String.format(
"Path '%s' resolved to entity type '%s' which defines multiple natural ids",
sqmPath.getNavigablePath().getFullPath(), identifiableType.getTypeName()
sqmPath.getNavigablePath(),
identifiableType.getTypeName()
)
);
}
@ -2360,7 +2363,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return sqmPath.get( naturalIdAttribute );
}
throw new SemanticException( "Path does not resolve to an entity type '" + sqmPath.getNavigablePath().getFullPath() + "'" );
throw new SemanticException( "Path does not resolve to an entity type '" + sqmPath.getNavigablePath() + "'" );
}
@Override
@ -2381,7 +2384,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
String.format(
Locale.ROOT,
"`%s` used in `fk()` only supported for to-one mappings, but found `%s`",
sqmPath.getNavigablePath().getFullPath(),
sqmPath.getNavigablePath(),
toOneReference
)
);

View File

@ -55,7 +55,7 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
@Override
public void register(SqmPath<?> sqmPath) {
SqmTreeCreationLogger.LOGGER.tracef( "SqmProcessingIndex#register(SqmPath) : %s", sqmPath.getNavigablePath().getFullPath() );
SqmTreeCreationLogger.LOGGER.tracef( "SqmProcessingIndex#register(SqmPath) : %s", sqmPath.getNavigablePath() );
// Generally we:
// 1) add the path to the path-by-path map

View File

@ -26,7 +26,7 @@ public class UnknownPathException extends SemanticException {
"Could not resolve path `%s` relative to %s (%s)",
name,
base.getReferencedPathSource().getSqmPathType().getTypeName(),
base.getNavigablePath().getFullPath()
base.getNavigablePath()
)
);
}

View File

@ -283,7 +283,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
processStanza(
"delete",
() -> {
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath().getFullPath() );
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath() );
visitWhereClause( statement.getWhereClause() );
}
);
@ -298,7 +298,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
processStanza(
"insert",
() -> {
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath().getFullPath() );
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath() );
processStanza(
"into",
() -> statement.getInsertionTargetPaths().forEach( sqmPath -> sqmPath.accept( this ) )
@ -317,7 +317,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
processStanza(
"insert",
() -> {
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath().getFullPath() );
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath() );
processStanza(
"into",
() -> statement.getInsertionTargetPaths().forEach( sqmPath -> sqmPath.accept( this ) )
@ -361,7 +361,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
processStanza(
statement.isVersioned() ? "update versioned" : "update",
() -> {
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath().getFullPath() );
logWithIndentation( "[target = %s]", statement.getTarget().getNavigablePath() );
visitSetClause( statement.getSetClause() );
@ -495,7 +495,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
public Object visitRootPath(SqmRoot sqmRoot) {
processStanza(
"root",
'`' + sqmRoot.getNavigablePath().getFullPath() + '`',
"`" + sqmRoot.getNavigablePath() + "`",
() -> processJoins( sqmRoot )
);
@ -517,7 +517,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
public Object visitCrossJoin(SqmCrossJoin joinedFromElement) {
processStanza(
"cross",
'`' + joinedFromElement.getNavigablePath().getFullPath() + '`',
"`" + joinedFromElement.getNavigablePath() + "`",
() -> processJoins( joinedFromElement )
);
@ -528,7 +528,7 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement) {
processStanza(
"plural-part",
'`' + joinedFromElement.getNavigablePath().getFullPath() + '`',
"`" + joinedFromElement.getNavigablePath() + "`",
() -> processJoins( joinedFromElement )
);
@ -552,12 +552,12 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
@Override
public Object visitQualifiedEntityJoin(SqmEntityJoin joinedFromElement) {
if ( inJoinPredicate ) {
logWithIndentation( "-> [joined-path] - `%s`", joinedFromElement.getNavigablePath().getFullPath() );
logWithIndentation( "-> [joined-path] - `%s`", joinedFromElement.getNavigablePath() );
}
else {
processStanza(
"entity",
'`' + joinedFromElement.getNavigablePath().getFullPath() + '`',
"`" + joinedFromElement.getNavigablePath() + "`",
() -> {
processJoinPredicate( joinedFromElement );
processJoins( joinedFromElement );
@ -570,12 +570,12 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
@Override
public Object visitQualifiedAttributeJoin(SqmAttributeJoin joinedFromElement) {
if ( inJoinPredicate ) {
logWithIndentation( "-> [joined-path] - `%s`", joinedFromElement.getNavigablePath().getFullPath() );
logWithIndentation( "-> [joined-path] - `%s`", joinedFromElement.getNavigablePath() );
}
else {
processStanza(
"attribute",
'`' + joinedFromElement.getNavigablePath().getFullPath() + '`',
"`" + joinedFromElement.getNavigablePath() + "`",
() -> {
logIndented( "[fetched = " + joinedFromElement.isFetched() + ']' );
@ -589,56 +589,56 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
@Override
public Object visitBasicValuedPath(SqmBasicValuedSimplePath path) {
logWithIndentation( "-> [basic-path] - `%s`", path.getNavigablePath().getFullPath() );
logWithIndentation( "-> [basic-path] - `%s`", path.getNavigablePath() );
return null;
}
@Override
public Object visitEmbeddableValuedPath(SqmEmbeddedValuedSimplePath path) {
logWithIndentation( "-> [embedded-path] - `%s`", path.getNavigablePath().getFullPath() );
logWithIndentation( "-> [embedded-path] - `%s`", path.getNavigablePath() );
return null;
}
@Override
public Object visitAnyValuedValuedPath(SqmAnyValuedSimplePath<?> path) {
logWithIndentation( "-> [any-path] - `%s`", path.getNavigablePath().getFullPath() );
logWithIndentation( "-> [any-path] - `%s`", path.getNavigablePath() );
return null;
}
@Override
public Object visitNonAggregatedCompositeValuedPath(NonAggregatedCompositeSimplePath<?> path) {
logWithIndentation( "-> [non-aggregated-composite-path] - `%s`", path.getNavigablePath().getFullPath() );
logWithIndentation( "-> [non-aggregated-composite-path] - `%s`", path.getNavigablePath() );
return null;
}
@Override
public Object visitFkExpression(SqmFkExpression<?> fkExpression) {
logWithIndentation( "-> [fk-ref] - `%s`", fkExpression.getToOnePath().getNavigablePath().getFullPath() );
logWithIndentation( "-> [fk-ref] - `%s`", fkExpression.getToOnePath().getNavigablePath() );
return null;
}
@Override
public Object visitSelfInterpretingSqmPath(SelfInterpretingSqmPath<?> sqmPath) {
logWithIndentation( "-> [self-interpreting-path] - `%s`", sqmPath.getNavigablePath().getFullPath() );
logWithIndentation( "-> [self-interpreting-path] - `%s`", sqmPath.getNavigablePath() );
return null;
}
@Override
public Object visitEntityValuedPath(SqmEntityValuedSimplePath path) {
logWithIndentation( "-> [entity-path] - `%s`", path.getNavigablePath().getFullPath() );
logWithIndentation( "-> [entity-path] - `%s`", path.getNavigablePath() );
return null;
}
@Override
public Object visitPluralValuedPath(SqmPluralValuedSimplePath path) {
logWithIndentation( "-> [plural-path] - `%s`", path.getNavigablePath().getFullPath() );
logWithIndentation( "-> [plural-path] - `%s`", path.getNavigablePath() );
return null;
}

View File

@ -85,7 +85,7 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
}
}
throw new SemanticException( "`" + sqmPath.getNavigablePath().getFullPath() + "` did not reference a known model part" );
throw new SemanticException( "`" + sqmPath.getNavigablePath() + "` did not reference a known model part" );
}
final TableReference tableReference = tableGroup.resolveTableReference(
@ -148,7 +148,7 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
@Override
public String toString() {
return "BasicValuedPathInterpretation(" + getNavigablePath().getFullPath() + ')';
return "BasicValuedPathInterpretation(" + getNavigablePath() + ")";
}
@Override

View File

@ -102,7 +102,7 @@ public class EmbeddableValuedPathInterpretation<T> extends AbstractSqmPathInterp
@Override
public String toString() {
return "EmbeddableValuedPathInterpretation(" + getNavigablePath().getFullPath() + ')';
return "EmbeddableValuedPathInterpretation(" + getNavigablePath() + ")";
}
@Override

View File

@ -434,7 +434,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
"Passed attribute name [%s] did not correspond to a collection (bag) reference [%s] relative to %s",
attributeName,
joinedPathSource,
getNavigablePath().getFullPath()
getNavigablePath()
)
);
}
@ -465,7 +465,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
"Passed attribute name [%s] did not correspond to a collection (set) reference [%s] relative to %s",
attributeName,
joinedPathSource,
getNavigablePath().getFullPath()
getNavigablePath()
)
);
}
@ -496,7 +496,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
"Passed attribute name [%s] did not correspond to a collection (list) reference [%s] relative to %s",
attributeName,
joinedPathSource,
getNavigablePath().getFullPath()
getNavigablePath()
)
);
}
@ -527,7 +527,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
"Passed attribute name [%s] did not correspond to a collection (map) reference [%s] relative to %s",
attributeName,
joinedPathSource,
getNavigablePath().getFullPath()
getNavigablePath()
)
);
}
@ -659,7 +659,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
"Passed attribute [%s] did not correspond to a joinable reference [%s] relative to %s",
joinedPathSource.getPathName(),
joinedPathSource,
getNavigablePath().getFullPath()
getNavigablePath()
)
);
}

View File

@ -195,6 +195,6 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
@Override
public String toString() {
return getClass().getSimpleName() + "(" + navigablePath.getFullPath() + ")";
return getClass().getSimpleName() + "(" + navigablePath + ")";
}
}

View File

@ -128,7 +128,7 @@ public class SqmPluralPartJoin<O,T> extends AbstractSqmJoin<O,T> implements SqmQ
return String.format(
Locale.ROOT,
"SqmPluralPartJoin(%s : %s)",
getNavigablePath().getFullPath(),
getNavigablePath(),
getReferencedPathSource().getPathName()
);
}

View File

@ -126,7 +126,7 @@ public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> {
return String.format(
Locale.ROOT,
"SqmSingularJoin(%s : %s)",
getNavigablePath().getFullPath(),
getNavigablePath(),
getReferencedPathSource().getPathName()
);
}

View File

@ -30,6 +30,9 @@ public interface DotIdentifierSequence {
/**
* The full sequence text. E.g., given the sequence `a.b.c`,
* this returns `a.b.c`
*
* @implNote This method may dynamically build the returned
* String and should be avoided for critical paths (comparisons, e.g.).
*/
String getFullPath();

View File

@ -24,62 +24,79 @@ public class EntityIdentifierNavigablePath extends NavigablePath {
this.identifierAttributeName = identifierAttributeName;
}
public String getIdentifierAttributeName() {
return identifierAttributeName;
}
@Override
public String getLocalName() {
return EntityIdentifierMapping.ROLE_LOCAL_NAME;
}
@Override
public int hashCode() {
return getParent().getFullPath().hashCode();
}
@Override
public boolean equals(Object other) {
if ( other == null ) {
return false;
}
if ( other == this ) {
return true;
}
if ( ! ( other instanceof NavigablePath ) ) {
return false;
}
final NavigablePath otherPath = (NavigablePath) other;
if ( getFullPath().equals( ( (NavigablePath) other ).getFullPath() ) ) {
return true;
}
if ( getParent() == null ) {
if ( otherPath.getParent() != null ) {
return false;
}
//noinspection RedundantIfStatement
if ( localNamesMatch( otherPath) ) {
return true;
}
return false;
}
if ( otherPath.getParent() == null ) {
return false;
}
return getParent().equals( otherPath.getParent() )
&& localNamesMatch( otherPath );
}
private boolean localNamesMatch(NavigablePath otherPath) {
protected boolean localNamesMatch(DotIdentifierSequence otherPath) {
final String otherLocalName = otherPath.getLocalName();
return otherLocalName.equals( getLocalName() )
|| otherLocalName.equals( identifierAttributeName );
}
@Override
protected boolean localNamesMatch(EntityIdentifierNavigablePath otherPath) {
return super.localNamesMatch( otherPath );
}
// @Override
// public int hashCode() {
// return getParent().getFullPath().hashCode();
// }
//
// @Override
// public boolean equals(Object other) {
// if ( other == null ) {
// return false;
// }
//
// if ( other == this ) {
// return true;
// }
//
// if ( ! ( other instanceof NavigablePath ) ) {
// return false;
// }
//
// final NavigablePath otherPath = (NavigablePath) other;
//
// if ( getFullPath().equals( ( (NavigablePath) other ).getFullPath() ) ) {
// return true;
// }
//
// if ( getParent() == null ) {
// if ( otherPath.getParent() != null ) {
// return false;
// }
//
// //noinspection RedundantIfStatement
// if ( localNamesMatch( otherPath) ) {
// return true;
//
// }
//
// return false;
// }
//
// if ( otherPath.getParent() == null ) {
// return false;
// }
//
// return getParent().equals( otherPath.getParent() )
// && localNamesMatch( otherPath );
// }
//
// private boolean localNamesMatch(NavigablePath otherPath) {
// final String otherLocalName = otherPath.getLocalName();
//
// return otherLocalName.equals( getLocalName() )
// || otherLocalName.equals( identifierAttributeName );
// }
}

View File

@ -7,6 +7,7 @@
package org.hibernate.spi;
import java.io.Serializable;
import java.util.Objects;
import org.hibernate.Incubating;
import org.hibernate.internal.util.StringHelper;
@ -25,41 +26,9 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
private final NavigablePath parent;
private final String localName;
private final String alias;
private final String identifierForTableGroup;
private final String fullPath;
public NavigablePath(NavigablePath parent, String navigableName) {
this.parent = parent;
this.alias = null;
// the _identifierMapper is a "hidden property" on entities with composite keys.
// concatenating it will prevent the path from correctly being used to look up
// various things such as criteria paths and fetch profile association paths
if ( IDENTIFIER_MAPPER_PROPERTY.equals( navigableName ) ) {
this.localName = "";
this.identifierForTableGroup = parent != null ? parent.getIdentifierForTableGroup() : "";
this.fullPath = parent != null ? parent.getFullPath() : "";
}
else {
this.localName = navigableName;
if ( parent != null ) {
final String parentFullPath = parent.getFullPath();
this.fullPath = StringHelper.isEmpty( parentFullPath )
? navigableName
: parentFullPath + "." + navigableName;
this.identifierForTableGroup = StringHelper.isEmpty( parent.getIdentifierForTableGroup() )
? navigableName
: parent.getIdentifierForTableGroup() + "." + navigableName;
}
else {
this.fullPath = navigableName;
this.identifierForTableGroup = navigableName;
}
}
}
private final FullPathCalculator fullPathCalculator;
private final int hashCode;
public NavigablePath(String localName) {
this( localName, null );
@ -67,88 +36,69 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
public NavigablePath(String rootName, String alias) {
this.parent = null;
this.alias = StringHelper.nullIfEmpty( alias );
this.alias = alias = StringHelper.nullIfEmpty( alias );
this.localName = rootName;
this.identifierForTableGroup = rootName;
this.fullPath = alias == null ? rootName : rootName + "(" + alias + ")";
this.fullPathCalculator = NavigablePath::calculateRootFullPath;
this.hashCode = localName.hashCode() + ( alias == null ? 0 : alias.hashCode() );
}
public NavigablePath(NavigablePath parent, String property, String alias) {
alias = StringHelper.nullIfEmpty( alias );
final String navigableName = alias == null
? property
: property + '(' + alias + ')';
public NavigablePath(NavigablePath parent, String navigableName) {
this( parent, navigableName, null );
}
public NavigablePath(NavigablePath parent, String localName, String alias) {
assert parent != null;
this.parent = parent;
this.alias = alias;
this.alias = alias = StringHelper.nullIfEmpty( alias );
final String aliasedLocalName = alias == null
? localName
: localName + '(' + alias + ')';
this.hashCode = parent.hashCode() + aliasedLocalName.hashCode();
// the _identifierMapper is a "hidden property" on entities with composite keys.
// concatenating it will prevent the path from correctly being used to look up
// various things such as criteria paths and fetch profile association paths
if ( IDENTIFIER_MAPPER_PROPERTY.equals( navigableName ) ) {
this.fullPath = parent != null ? parent.getFullPath() : "";
if ( IDENTIFIER_MAPPER_PROPERTY.equals( localName ) ) {
this.localName = "";
identifierForTableGroup = parent != null ? parent.getFullPath() : "";
this.identifierForTableGroup = parent.getFullPath();
this.fullPathCalculator = NavigablePath::calculateIdMapperFullPath;
}
else {
this.localName = property;
if ( parent != null ) {
final String parentFullPath = parent.getFullPath();
this.fullPath = StringHelper.isEmpty( parentFullPath )
? navigableName
: parentFullPath + "." + navigableName;
this.identifierForTableGroup = StringHelper.isEmpty( parent.getIdentifierForTableGroup() )
? navigableName
: parent.getIdentifierForTableGroup() + "." + property;
}
else {
this.fullPath = navigableName;
this.identifierForTableGroup = property;
}
this.localName = localName;
this.identifierForTableGroup = StringHelper.isEmpty( parent.getIdentifierForTableGroup() )
? aliasedLocalName
: parent.getIdentifierForTableGroup() + "." + localName;
this.fullPathCalculator = NavigablePath::calculateNormalFullPath;
}
}
public NavigablePath() {
this( "" );
}
public NavigablePath(
NavigablePath parent,
String fullPath,
String localName,
String identifierForTableGroup) {
String alias,
String identifierForTableGroup,
FullPathCalculator fullPathCalculator,
int hashCode) {
this.parent = parent;
this.alias = null;
this.fullPath = fullPath;
this.localName = localName;
this.hashCode = hashCode;
this.alias = StringHelper.nullIfEmpty( alias );
this.identifierForTableGroup = identifierForTableGroup;
this.fullPathCalculator = fullPathCalculator;
}
public NavigablePath treatAs(String entityName) {
return new TreatedNavigablePath( this, entityName );
}
public NavigablePath treatAs(String entityName, String alias) {
return new TreatedNavigablePath( this, entityName, alias );
}
public NavigablePath append(String property) {
return new NavigablePath( this, property );
}
public NavigablePath append(String property, String alias) {
return new NavigablePath( this, property, alias );
}
@Override
public NavigablePath getParent() {
return parent instanceof TreatedNavigablePath ? parent.getParent() : parent;
}
public NavigablePath getRealParent() {
return parent;
}
@Override
public String getLocalName() {
return localName;
}
@ -162,17 +112,75 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
}
public String getIdentifierForTableGroup() {
// todo (6.0) : is this `if` really needed? seems this is already handled in constructors
if ( parent == null ) {
return fullPath;
}
return identifierForTableGroup;
}
public String getFullPath() {
return fullPath;
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if ( this == other ) {
return true;
}
if ( other == null ) {
return false;
}
final DotIdentifierSequence otherPath = (DotIdentifierSequence) other;
if ( ! localNamesMatch( otherPath ) ) {
return false;
}
if ( otherPath instanceof NavigablePath ) {
final NavigablePath otherNavigablePath = (NavigablePath) otherPath;
if ( ! Objects.equals( getAlias(), otherNavigablePath.getAlias() ) ) {
return false;
}
}
return Objects.equals( getParent(), otherPath.getParent() );
}
protected boolean localNamesMatch(DotIdentifierSequence other) {
if ( other instanceof EntityIdentifierNavigablePath ) {
return localNamesMatch( (EntityIdentifierNavigablePath) other );
}
return Objects.equals( getLocalName(), other.getLocalName() );
}
protected boolean localNamesMatch(EntityIdentifierNavigablePath other) {
return Objects.equals( getLocalName(), other.getLocalName() )
|| Objects.equals( getLocalName(), other.getIdentifierAttributeName() );
}
public NavigablePath append(String property) {
return new NavigablePath( this, property );
}
public NavigablePath append(String property, String alias) {
return new NavigablePath( this, property, alias );
}
public NavigablePath treatAs(String entityName) {
return new TreatedNavigablePath( this, entityName );
}
public NavigablePath treatAs(String entityName, String alias) {
return new TreatedNavigablePath( this, entityName, alias );
}
public NavigablePath getRealParent() {
return parent;
}
/**
* Determine whether this path is part of the given path's parent
*/
public boolean isParent(NavigablePath navigablePath) {
while ( navigablePath != null ) {
if ( this.equals( navigablePath.getParent() ) ) {
@ -183,45 +191,49 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
return false;
}
@Override
public String getFullPath() {
return fullPathCalculator.calculateFullPath( parent, localName, alias );
}
@Override
public String toString() {
return fullPath;
return getFullPath();
}
@Override
public int hashCode() {
return fullPath.hashCode();
/**
* Effectively a tri-function
*/
@FunctionalInterface
protected interface FullPathCalculator extends Serializable {
String calculateFullPath(NavigablePath parent, String localName, String alias);
}
@Override
public boolean equals(Object other) {
if ( this == other ) {
return true;
}
/**
* The pattern used for root NavigablePaths
*/
protected static String calculateRootFullPath(NavigablePath parent, String rootName, String alias) {
assert parent == null;
return alias == null ? rootName : rootName + "(" + alias + ")";
}
if ( other instanceof EntityIdentifierNavigablePath ) {
final EntityIdentifierNavigablePath otherPath = (EntityIdentifierNavigablePath) other;
return otherPath.equals( this );
}
/**
* The normal pattern used for the "full path"
*/
private static String calculateNormalFullPath(NavigablePath parent, String localName, String alias) {
assert parent != null;
if ( ! ( other instanceof NavigablePath ) ) {
return false;
}
final String parentFullPath = parent.getFullPath();
final String baseFullPath = StringHelper.isEmpty( parentFullPath )
? localName
: parentFullPath + "." + localName;
return alias == null ? baseFullPath : baseFullPath + "(" + alias + ")";
}
final NavigablePath otherPath = (NavigablePath) other;
// todo (6.0) : checking the full paths is definitely better performance
// But I'm not sure it is correct in all cases. Take cases referencing
// an identifier at some level - the actual EntityIdentifierNavigablePath
// subclass has special handling for one path using the "role name" (`"{id}"`)
// while the other might instead use the attribute name
// return Objects.equals( getFullPath(), otherPath.getFullPath() );
if ( getParent() == null ) {
return otherPath.getParent() == null;
}
return getParent().equals( otherPath.getParent() )
&& getLocalName().equals( otherPath.getLocalName() );
/**
* Pattern used for `_identifierMapper`
*/
protected static String calculateIdMapperFullPath(NavigablePath parent, String localName, String alias) {
return parent != null ? parent.getFullPath() : "";
}
}

View File

@ -23,14 +23,21 @@ public class TreatedNavigablePath extends NavigablePath {
public TreatedNavigablePath(NavigablePath parent, String entityTypeName, String alias) {
super(
parent,
alias == null ? "treat(" + parent.getFullPath() + " as " + entityTypeName + ")"
: "treat(" + parent.getFullPath() + " as " + entityTypeName + ")(" + alias + ")",
entityTypeName,
"treat(" + parent.getFullPath() + " as " + entityTypeName + ")"
alias,
"treat(" + parent + " as " + entityTypeName + ")",
TreatedNavigablePath::calculateTreatedFullPath,
1
);
assert !( parent instanceof TreatedNavigablePath );
}
protected static String calculateTreatedFullPath(NavigablePath parent, String localName, String alias) {
return alias == null
? "treat(" + parent + " as " + localName + ")"
: "treat(" + parent + " as " + localName + ")(" + alias + ")";
}
@Override
public NavigablePath treatAs(String entityName) {
return new TreatedNavigablePath( getRealParent(), entityName );
@ -41,25 +48,25 @@ public class TreatedNavigablePath extends NavigablePath {
return new TreatedNavigablePath( getRealParent(), entityName, alias );
}
@Override
public int hashCode() {
return getFullPath().hashCode();
}
@Override
public boolean equals(Object other) {
if ( other == null ) {
return false;
}
if ( other == this ) {
return true;
}
if ( ! ( other instanceof NavigablePath ) ) {
return false;
}
return getFullPath().equals( ( (NavigablePath) other ).getFullPath() );
}
// @Override
// public int hashCode() {
// return getFullPath().hashCode();
// }
//
// @Override
// public boolean equals(Object other) {
// if ( other == null ) {
// return false;
// }
//
// if ( other == this ) {
// return true;
// }
//
// if ( ! ( other instanceof NavigablePath ) ) {
// return false;
// }
//
// return getFullPath().equals( ( (NavigablePath) other ).getFullPath() );
// }
}

View File

@ -46,7 +46,7 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath.getFullPath()
navigablePath
)
);
}

View File

@ -49,7 +49,7 @@ public abstract class DerivedTableReference extends AbstractTableReference {
boolean allowFkOptimization) {
throw new UnknownTableReferenceException(
tableExpression,
"TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath.getFullPath()
"TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath
);
}

View File

@ -252,7 +252,7 @@ public class LazyTableGroup extends DelegatingTableGroup {
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath.getFullPath()
navigablePath
)
);
}

View File

@ -130,7 +130,7 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath.getFullPath()
navigablePath
)
);
}

View File

@ -89,7 +89,7 @@ public class NamedTableReference extends AbstractTableReference {
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath.getFullPath()
navigablePath
)
);
}

View File

@ -43,7 +43,7 @@ public class UnionTableReference extends NamedTableReference {
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath.getFullPath()
navigablePath
)
);
}

View File

@ -290,7 +290,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
EntityLoadingLogging.ENTITY_LOADING_LOGGER.tracef(
"(%s) Beginning Initializer#resolveKey process for entity : %s",
StringHelper.collapse( this.getClass().getName() ),
getNavigablePath().getFullPath()
getNavigablePath()
);
}

View File

@ -54,6 +54,6 @@ public class EntityResultInitializer extends AbstractEntityInitializer {
@Override
public String toString() {
return CONCRETE_NAME + "(" + getNavigablePath().getFullPath() + ")";
return CONCRETE_NAME + "(" + getNavigablePath() + ")";
}
}

View File

@ -119,7 +119,7 @@ public class ResultsHelper {
initializerMap.forEach( (navigablePath, initializer) -> {
ResultsLogger.RESULTS_MESSAGE_LOGGER.debugf(
" %s -> %s@%s (%s)",
navigablePath.getFullPath(),
navigablePath,
initializer,
initializer.hashCode(),
initializer.getInitializedPart()