Skip to content

Commit

Permalink
HHH-15349 Fix rendering of EntityValuedPathInterpretation when compar…
Browse files Browse the repository at this point in the history
…ing different model parts
  • Loading branch information
beikov committed Jun 23, 2022
1 parent b3d0add commit 9cff075
Show file tree
Hide file tree
Showing 17 changed files with 990 additions and 272 deletions.
Expand Up @@ -2,6 +2,8 @@

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
Expand All @@ -14,6 +16,7 @@
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;

import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -29,6 +32,13 @@
*/
public class NotFoundTest extends BaseEntityManagerFunctionalTestCase {

private SQLStatementInterceptor sqlStatementInterceptor;

@Override
protected void addConfigOptions(Map options) {
sqlStatementInterceptor = new SQLStatementInterceptor( options );
}

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Expand Down Expand Up @@ -111,19 +121,25 @@ public void queryTestFk() {
breakForeignKey();

inTransaction( entityManagerFactory(), (entityManager) -> {
sqlStatementInterceptor.clear();
//tag::associations-not-found-fk-function-example[]
final List<Person> nullResults = entityManager
.createSelectionQuery( "from Person p where fk( p.city ) is null", Person.class )
final List<String> nullResults = entityManager
.createSelectionQuery( "select p.name from Person p where fk( p.city ) is null", String.class )
.list();

assertThat( nullResults ).isEmpty();

final List<Person> nonNullResults = entityManager
.createSelectionQuery( "from Person p where fk( p.city ) is not null", Person.class )
final List<String> nonNullResults = entityManager
.createSelectionQuery( "select p.name from Person p where fk( p.city ) is not null", String.class )
.list();
assertThat( nonNullResults ).hasSize( 1 );
assertThat( nonNullResults.get( 0 ).getName() ).isEqualTo( "John Doe" );
assertThat( nonNullResults.get( 0 ) ).isEqualTo( "John Doe" );
//end::associations-not-found-fk-function-example[]

// In addition, make sure that the two executed queries do not create a join
assertThat( sqlStatementInterceptor.getQueryCount() ).isEqualTo( 2 );
assertThat( sqlStatementInterceptor.getSqlQueries().get( 0 ) ).doesNotContain( " join " );
assertThat( sqlStatementInterceptor.getSqlQueries().get( 1 ) ).doesNotContain( " join " );
} );
}

Expand Down
Expand Up @@ -135,6 +135,10 @@ public static void createSyntheticPropertyReference(
String syntheticPropertyName =
"_" + associatedClass.getEntityName().replace('.', '_') +
"_" + columns[0].getPropertyName().replace('.', '_');
if ( inverse ) {
// Use a different name for inverse synthetic properties to avoid duplicate properties for self-referencing models
syntheticPropertyName += "_inverse";
}
//find properties associated to a certain column
Object columnOwner = findColumnOwner( ownerEntity, columns[0].getReferencedColumn(), context );
List<Property> properties = findPropertiesByColumns( columnOwner, columns, context );
Expand Down
Expand Up @@ -2271,7 +2271,7 @@ public static void bindManytoManyInverseFk(
}
String referencedPropertyName =
buildingContext.getMetadataCollector().getPropertyReferencedAssociation(
"inverse__" + referencedEntity.getEntityName(), mappedBy
referencedEntity.getEntityName(), mappedBy
);
if ( referencedPropertyName != null ) {
//TODO always a many to one?
Expand Down
Expand Up @@ -27,6 +27,10 @@ default String getFetchableName() {
*/
ModelPart getKeyTargetMatchPart();

boolean isReferenceToPrimaryKey();

boolean isFkOptimizationAllowed();

@Override
default boolean incrementFetchDepth(){
return true;
Expand Down
Expand Up @@ -50,6 +50,15 @@ interface Side {

String getTargetTable();

default String getTable(Nature nature) {
if ( nature == Nature.KEY ) {
return getKeyTable();
}
else {
return getTargetTable();
}
}

ModelPart getKeyPart();

ModelPart getTargetPart();
Expand Down
Expand Up @@ -341,9 +341,14 @@ public Predicate generateJoinPredicate(
SqlAstCreationContext creationContext) {
final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(),
targetTable
targetTable,
false
);
final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference(
null,
keyTable,
false
);
final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( keyTable );

return generateJoinPredicate(
lhsTableReference,
Expand Down Expand Up @@ -459,27 +464,6 @@ else if ( keyExpression.equals( rhs.getColumnExpression() ) ) {
return true;
}

protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) {
TableReference tableReference = lhs.getPrimaryTableReference().resolveTableReference( table );
if ( tableReference != null ) {
return tableReference;
}
tableReference = tableGroup.getPrimaryTableReference().resolveTableReference( table );
if ( tableReference != null ) {
return tableReference;
}

tableReference = lhs.resolveTableReference(
lhs.getNavigablePath().append( getNavigableRole().getNavigableName() ),
table
);
if ( tableReference != null ) {
return tableReference;
}

throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
}

@Override
public int visitKeySelectables(int offset, SelectableConsumer consumer) {
return keySelectableMappings.forEachSelectable( offset, consumer );
Expand Down
Expand Up @@ -600,6 +600,20 @@ public ForeignKeyDescriptor.Nature getSideNature() {
: ForeignKeyDescriptor.Nature.KEY;
}

@Override
public boolean isReferenceToPrimaryKey() {
return fkDescriptor.getSide( getSideNature().inverse() ).getModelPart() instanceof EntityIdentifierMapping;
}

@Override
public boolean isFkOptimizationAllowed() {
return !collectionDescriptor.isOneToMany();
}

public CollectionPersister getCollectionDescriptor() {
return collectionDescriptor;
}

@Override
public FetchStyle getStyle() {
return FetchStyle.JOIN;
Expand Down Expand Up @@ -710,7 +724,9 @@ public LazyTableGroup createRootTableGroupJoin(
return false;
}

return targetKeyPropertyNames.contains( relativePath );
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
},
this,
explicitSourceAlias,
Expand Down
Expand Up @@ -316,10 +316,13 @@ public Predicate generateJoinPredicate(
SqlAstCreationContext creationContext) {
final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(),
targetSide.getModelPart().getContainingTableExpression()
targetSide.getModelPart().getContainingTableExpression(),
false
);
final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference(
keySide.getModelPart().getContainingTableExpression()
null,
keySide.getModelPart().getContainingTableExpression(),
false
);

return generateJoinPredicate(
Expand Down Expand Up @@ -352,23 +355,6 @@ public boolean isSimpleJoinPredicate(Predicate predicate) {
|| ( lhs.equals( targetExpression ) && rhs.equals( keyExpression ) );
}

protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) {
final NavigablePath navigablePath = lhs.getNavigablePath().append( getTargetPart().getFetchableName() );
if ( lhs.getPrimaryTableReference().getTableReference( navigablePath, table ) != null ) {
return lhs.getPrimaryTableReference();
}
else if ( tableGroup.getPrimaryTableReference().getTableReference( navigablePath, table ) != null ) {
return tableGroup.getPrimaryTableReference();
}

final TableReference tableReference = lhs.resolveTableReference( navigablePath, table );
if ( tableReference != null ) {
return tableReference;
}

throw new IllegalStateException( "Could not resolve binding for table `" + table + "`" );
}

@Override
public MappingType getPartMappingType() {
return targetSide.getModelPart().getMappedType();
Expand Down
Expand Up @@ -703,10 +703,16 @@ public ForeignKeyDescriptor.Nature getSideNature() {
return sideNature;
}

@Override
public boolean isReferenceToPrimaryKey() {
return foreignKeyDescriptor.getSide( sideNature.inverse() ).getModelPart() instanceof EntityIdentifierMapping;
}

@Override
public boolean isFkOptimizationAllowed() {
return canUseParentTableGroup;
}

public String getReferencedPropertyName() {
return referencedPropertyName;
}
Expand Down Expand Up @@ -1576,7 +1582,9 @@ public TableGroupJoin createTableGroupJoin(
return false;
}

return targetKeyPropertyNames.contains( relativePath );
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
}
),
null
Expand Down Expand Up @@ -1606,23 +1614,31 @@ public TableGroupJoin createTableGroupJoin(

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
)
) );
lazyTableGroup.setTableGroupInitializerCallback(
tableGroup -> {
join.applyPredicate(
foreignKeyDescriptor.generateJoinPredicate(
sideNature == ForeignKeyDescriptor.Nature.TARGET ?
lhsTableReference :
tableGroup.getPrimaryTableReference(),
sideNature == ForeignKeyDescriptor.Nature.TARGET ?
tableGroup.getPrimaryTableReference() :
lhsTableReference,
sqlExpressionResolver,
creationContext
)
);

if ( hasNotFoundAction() ) {
getAssociatedEntityMappingType().applyWhereRestrictions(
join::applyPredicate,
lazyTableGroup.getTableGroup(),
true,
null
);
}
if ( hasNotFoundAction() ) {
getAssociatedEntityMappingType().applyWhereRestrictions(
join::applyPredicate,
tableGroup,
true,
null
);
}
}
);

return join;
}
Expand Down Expand Up @@ -1697,7 +1713,9 @@ else if ( isNullable || hasNotFoundAction() ) {
return false;
}

return targetKeyPropertyNames.contains( relativePath );
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
},
tableGroupProducer,
explicitSourceAlias,
Expand Down
Expand Up @@ -145,16 +145,16 @@ private static ModelPart resolveSqmPath(
MappingMetamodel domainModel,
Function<NavigablePath,TableGroup> tableGroupLocator) {

if ( sqmPath instanceof SqmTreatedPath ) {
final SqmTreatedPath treatedPath = (SqmTreatedPath) sqmPath;
final EntityDomainType treatTargetType = treatedPath.getTreatTarget();
if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmPath;
final EntityDomainType<?> treatTargetType = treatedPath.getTreatTarget();
return domainModel.findEntityDescriptor( treatTargetType.getHibernateEntityName() );
}

// see if the LHS is treated
if ( sqmPath.getLhs() instanceof SqmTreatedPath ) {
final SqmTreatedPath treatedPath = (SqmTreatedPath) sqmPath.getLhs();
final EntityDomainType treatTargetType = treatedPath.getTreatTarget();
if ( sqmPath.getLhs() instanceof SqmTreatedPath<?, ?> ) {
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) sqmPath.getLhs();
final EntityDomainType<?> treatTargetType = treatedPath.getTreatTarget();
final EntityPersister container = domainModel.findEntityDescriptor( treatTargetType.getHibernateEntityName() );

return container.findSubPart( sqmPath.getNavigablePath().getLocalName(), container );
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
Expand Down Expand Up @@ -350,6 +351,10 @@ private static void createValueBindings(
if ( parameterType == null ) {
throw new SqlTreeCreationException( "Unable to interpret mapping-model type for Query parameter : " + domainParam );
}
else if ( parameterType instanceof PluralAttributeMapping ) {
// Default to the collection element
parameterType = ( (PluralAttributeMapping) parameterType ).getElementDescriptor();
}

if ( parameterType instanceof EntityIdentifierMapping ) {
final EntityIdentifierMapping identifierMapping = (EntityIdentifierMapping) parameterType;
Expand All @@ -368,17 +373,20 @@ else if ( parameterType instanceof EntityMappingType ) {
}
else if ( parameterType instanceof EntityAssociationMapping ) {
EntityAssociationMapping association = (EntityAssociationMapping) parameterType;
bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide(
bindValue,
association.getSideNature().inverse(),
session
);
parameterType = association.getForeignKeyDescriptor();
}
else if ( parameterType instanceof PluralAttributeMapping ) {
// we'd expect the values to refer to the collection element
// for now, let's blow up and see where this happens and fix the specifics...
throw new NotYetImplementedFor6Exception( "Binding parameters whose inferred type comes from plural attribute not yet implemented" );
if ( association.getSideNature() == ForeignKeyDescriptor.Nature.TARGET ) {
// If the association is the target, we must use the identifier of the EntityMappingType
bindValue = association.getAssociatedEntityMappingType().getIdentifierMapping()
.getIdentifier( bindValue );
parameterType = association.getAssociatedEntityMappingType().getIdentifierMapping();
}
else {
bindValue = association.getForeignKeyDescriptor().getAssociationKeyFromSide(
bindValue,
association.getSideNature().inverse(),
session
);
parameterType = association.getForeignKeyDescriptor();
}
}

int offset = jdbcParameterBindings.registerParametersForEachJdbcValue(
Expand Down

0 comments on commit 9cff075

Please sign in to comment.