HHH-16238 Correct path model in createSqmPath for SingularAttributeImpl

This commit is contained in:
Marco Belladelli 2023-03-02 16:29:52 +01:00 committed by Christian Beikov
parent 16c9b1f5b7
commit e896656bb3
21 changed files with 122 additions and 35 deletions

View File

@ -414,6 +414,7 @@ public abstract class AbstractIdentifiableType<J>
if ( type instanceof BasicDomainType ) {
return new BasicSqmPathSource<>(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
(SqmPathSource) id,
(BasicDomainType<?>) type,
Bindable.BindableType.SINGULAR_ATTRIBUTE
);
@ -423,6 +424,7 @@ public abstract class AbstractIdentifiableType<J>
final EmbeddableDomainType<?> compositeType = (EmbeddableDomainType<?>) type;
return new EmbeddedSqmPathSource<>(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
(SqmPathSource) id,
compositeType,
Bindable.BindableType.SINGULAR_ATTRIBUTE,
id.isGeneric()
@ -434,6 +436,7 @@ public abstract class AbstractIdentifiableType<J>
if ( idClassType == null ) {
return new NonAggregatedCompositeSqmPathSource<>(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
null,
Bindable.BindableType.SINGULAR_ATTRIBUTE,
this
);
@ -441,6 +444,7 @@ public abstract class AbstractIdentifiableType<J>
else {
return new EmbeddedSqmPathSource<>(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
null,
idClassType,
Bindable.BindableType.SINGULAR_ATTRIBUTE,
false

View File

@ -27,6 +27,8 @@ public interface SingularPersistentAttribute<D,J>
@Override
DomainType<J> getSqmPathType();
SqmPathSource<J> getPathSource();
/**
* For a singular attribute, the value type is defined as the
* attribute type

View File

@ -15,14 +15,17 @@ import org.hibernate.type.descriptor.java.JavaType;
*/
public abstract class AbstractSqmPathSource<J> implements SqmPathSource<J> {
private final String localPathName;
protected final SqmPathSource<J> pathModel;
private final DomainType<J> domainType;
private final BindableType jpaBindableType;
public AbstractSqmPathSource(
String localPathName,
SqmPathSource<J> pathModel,
DomainType<J> domainType,
BindableType jpaBindableType) {
this.localPathName = localPathName;
this.pathModel = pathModel == null ? this : pathModel;
this.domainType = domainType;
this.jpaBindableType = jpaBindableType;
}

View File

@ -12,6 +12,7 @@ import org.hibernate.query.PathException;
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.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.domain.AbstractSqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
@ -22,7 +23,7 @@ public class AnyDiscriminatorSqmPath<T> extends AbstractSqmPath<T> {
protected AnyDiscriminatorSqmPath(
NavigablePath navigablePath,
AnyDiscriminatorSqmPathSource referencedPathSource,
SqmPathSource referencedPathSource,
SqmPath lhs,
NodeBuilder nodeBuilder) {
super( navigablePath, referencedPathSource, lhs, nodeBuilder );

View File

@ -23,9 +23,10 @@ public class AnyDiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
public AnyDiscriminatorSqmPathSource(
String localPathName,
SqmPathSource<D> pathModel,
SimpleDomainType<D> domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
super( localPathName, pathModel, domainType, jpaBindableType );
}
@Override
@ -37,7 +38,7 @@ public class AnyDiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
else {
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() );
}
return new AnyDiscriminatorSqmPath( navigablePath, this, lhs, lhs.nodeBuilder() );
return new AnyDiscriminatorSqmPath( navigablePath, pathModel, lhs, lhs.nodeBuilder() );
}
@Override

View File

@ -13,8 +13,6 @@ import org.hibernate.spi.NavigablePath;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmAnyValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.type.BasicType;
import org.hibernate.type.ConvertedBasicType;
import static jakarta.persistence.metamodel.Bindable.BindableType.SINGULAR_ATTRIBUTE;
@ -27,12 +25,19 @@ public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> {
public AnyMappingSqmPathSource(
String localPathName,
SqmPathSource<J> pathModel,
AnyMappingDomainType<J> domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
keyPathSource = new BasicSqmPathSource<>( "id", (BasicDomainType<?>) domainType.getKeyType(), SINGULAR_ATTRIBUTE );
super( localPathName, pathModel, domainType, jpaBindableType );
keyPathSource = new BasicSqmPathSource<>(
"id",
null,
(BasicDomainType<?>) domainType.getKeyType(),
SINGULAR_ATTRIBUTE
);
discriminatorPathSource = new AnyDiscriminatorSqmPathSource<>(
localPathName,
null,
domainType.getDiscriminatorType(),
jpaBindableType
);
@ -64,6 +69,6 @@ public class AnyMappingSqmPathSource<J> extends AbstractSqmPathSource<J> {
else {
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
}
return new SqmAnyValuedSimplePath<>( navigablePath, this, lhs, lhs.nodeBuilder() );
return new SqmAnyValuedSimplePath<>( navigablePath, pathModel, lhs, lhs.nodeBuilder() );
}
}

View File

@ -22,9 +22,10 @@ public class BasicSqmPathSource<J>
public BasicSqmPathSource(
String localPathName,
SqmPathSource<J> pathModel,
BasicDomainType<J> domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
super( localPathName, pathModel, domainType, jpaBindableType );
}
@Override
@ -49,7 +50,7 @@ public class BasicSqmPathSource<J>
}
return new SqmBasicValuedSimplePath<>(
navigablePath,
this,
pathModel,
lhs,
lhs.nodeBuilder()
);

View File

@ -29,7 +29,7 @@ public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
DomainType<D> discriminatorValueType,
EntityDomainType<?> entityDomainType,
EntityMappingType entityMapping) {
super( EntityDiscriminatorMapping.ROLE_NAME, discriminatorValueType, BindableType.SINGULAR_ATTRIBUTE );
super( EntityDiscriminatorMapping.ROLE_NAME, null, discriminatorValueType, BindableType.SINGULAR_ATTRIBUTE );
this.entityDomainType = entityDomainType;
this.entityMapping = entityMapping;
}
@ -43,7 +43,7 @@ public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
else {
navigablePath = lhs.getNavigablePath().append( intermediatePathSource.getPathName() ).append( getPathName() );
}
return new DiscriminatorSqmPath( navigablePath, this, lhs, entityDomainType, entityMapping, lhs.nodeBuilder() );
return new DiscriminatorSqmPath( navigablePath, pathModel, lhs, entityDomainType, entityMapping, lhs.nodeBuilder() );
}
@Override

View File

@ -22,10 +22,11 @@ public class EmbeddedSqmPathSource<J>
public EmbeddedSqmPathSource(
String localPathName,
SqmPathSource<J> pathModel,
EmbeddableDomainType<J> domainType,
BindableType jpaBindableType,
boolean isGeneric) {
super( localPathName, domainType, jpaBindableType );
super( localPathName, pathModel, domainType, jpaBindableType );
this.isGeneric = isGeneric;
}
@ -56,7 +57,7 @@ public class EmbeddedSqmPathSource<J>
}
return new SqmEmbeddedValuedSimplePath<>(
navigablePath,
this,
pathModel,
lhs,
lhs.nodeBuilder()
);

View File

@ -23,9 +23,10 @@ import org.hibernate.query.sqm.tree.from.SqmFrom;
public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> implements SqmJoinable<Object, J> {
public EntitySqmPathSource(
String localPathName,
SqmPathSource<J> pathModel,
EntityDomainType<J> domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
super( localPathName, pathModel, domainType, jpaBindableType );
}
@Override
@ -51,7 +52,7 @@ public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> implements
}
return new SqmEntityValuedSimplePath<>(
navigablePath,
this,
pathModel,
lhs,
lhs.nodeBuilder()
);
@ -66,7 +67,7 @@ public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> implements
SqmCreationState creationState) {
return new SqmPluralPartJoin<>(
lhs,
this,
pathModel,
alias,
joinType,
creationState.getCreationContext().getNodeBuilder()

View File

@ -23,9 +23,10 @@ import org.hibernate.query.sqm.tree.from.SqmFrom;
public class MappedSuperclassSqmPathSource<J> extends AbstractSqmPathSource<J> implements SqmJoinable<Object, J> {
public MappedSuperclassSqmPathSource(
String localPathName,
SqmPathSource<J> pathModel,
MappedSuperclassDomainType<J> domainType,
BindableType jpaBindableType) {
super( localPathName, domainType, jpaBindableType );
super( localPathName, pathModel, domainType, jpaBindableType );
}
@Override
@ -51,7 +52,7 @@ public class MappedSuperclassSqmPathSource<J> extends AbstractSqmPathSource<J> i
}
return new SqmEntityValuedSimplePath<>(
navigablePath,
this,
pathModel,
lhs,
lhs.nodeBuilder()
);
@ -66,7 +67,7 @@ public class MappedSuperclassSqmPathSource<J> extends AbstractSqmPathSource<J> i
SqmCreationState creationState) {
return new SqmPluralPartJoin<>(
lhs,
this,
pathModel,
alias,
joinType,
creationState.getCreationContext().getNodeBuilder()

View File

@ -20,9 +20,10 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
public class NonAggregatedCompositeSqmPathSource<J> extends AbstractSqmPathSource<J> implements CompositeSqmPathSource<J> {
public NonAggregatedCompositeSqmPathSource(
String localName,
SqmPathSource<J> pathModel,
BindableType bindableType,
ManagedDomainType<J> container) {
super( localName, container, bindableType );
super( localName, pathModel, container, bindableType );
}
@Override
@ -46,7 +47,7 @@ public class NonAggregatedCompositeSqmPathSource<J> extends AbstractSqmPathSourc
}
return new NonAggregatedCompositeSimplePath<>(
navigablePath,
this,
pathModel,
lhs,
lhs.nodeBuilder()
);

View File

@ -77,6 +77,7 @@ public class SingularAttributeImpl<D,J>
this.sqmPathSource = SqmMappingModelHelper.resolveSqmPathSource(
name,
this,
attributeType,
BindableType.SINGULAR_ATTRIBUTE,
isGeneric
@ -123,6 +124,11 @@ public class SingularAttributeImpl<D,J>
return sqmPathSource.findSubPathSource( name );
}
@Override
public SqmPathSource<J> getPathSource() {
return this.sqmPathSource;
}
@Override
public boolean isGeneric() {
return sqmPathSource.isGeneric();

View File

@ -15,6 +15,7 @@ import jakarta.persistence.metamodel.PluralAttribute;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.spi.NavigablePath;
/**
@ -43,6 +44,11 @@ public interface JpaPath<T> extends JpaExpression<T>, Path<T> {
*/
<S extends T> JpaPath<S> treatAs(EntityDomainType<S> treatJavaType);
/**
* Get this pat's actual resolved model, e.g. the
* concrete embeddable type for generic embeddables
*/
SqmPathSource<?> getResolvedModel();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Covariant overrides

View File

@ -14,6 +14,7 @@ import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
@ -65,6 +66,11 @@ public class AnonymousTupleSqmAssociationPathSource<O, J> extends AnonymousTuple
return null;
}
@Override
public SqmPathSource<J> getPathSource() {
return this;
}
@Override
public boolean isId() {
return false;

View File

@ -78,11 +78,12 @@ public class SqmMappingModelHelper {
String name,
DomainType<J> valueDomainType,
Bindable.BindableType jpaBindableType) {
return resolveSqmPathSource( name, valueDomainType, jpaBindableType, false );
return resolveSqmPathSource( name, null, valueDomainType, jpaBindableType, false );
}
public static <J> SqmPathSource<J> resolveSqmPathSource(
String name,
SqmPathSource<J> pathModel,
DomainType<J> valueDomainType,
Bindable.BindableType jpaBindableType,
boolean isGeneric) {
@ -90,6 +91,7 @@ public class SqmMappingModelHelper {
if ( valueDomainType instanceof BasicDomainType<?> ) {
return new BasicSqmPathSource<>(
name,
pathModel,
(BasicDomainType<J>) valueDomainType,
jpaBindableType
);
@ -98,6 +100,7 @@ public class SqmMappingModelHelper {
if ( valueDomainType instanceof AnyMappingDomainType<?> ) {
return new AnyMappingSqmPathSource<>(
name,
pathModel,
(AnyMappingDomainType<J>) valueDomainType,
jpaBindableType
);
@ -106,6 +109,7 @@ public class SqmMappingModelHelper {
if ( valueDomainType instanceof EmbeddableDomainType<?> ) {
return new EmbeddedSqmPathSource<>(
name,
pathModel,
(EmbeddableDomainType<J>) valueDomainType,
jpaBindableType,
isGeneric
@ -115,6 +119,7 @@ public class SqmMappingModelHelper {
if ( valueDomainType instanceof EntityDomainType<?> ) {
return new EntitySqmPathSource<>(
name,
pathModel,
(EntityDomainType<J>) valueDomainType,
jpaBindableType
);
@ -123,6 +128,7 @@ public class SqmMappingModelHelper {
if ( valueDomainType instanceof MappedSuperclassDomainType<?> ) {
return new MappedSuperclassSqmPathSource<>(
name,
pathModel,
(MappedSuperclassDomainType<J>) valueDomainType,
jpaBindableType
);

View File

@ -138,6 +138,11 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
return getReferencedPathSource();
}
@Override
public SqmPathSource<?> getResolvedModel() {
return getModel();
}
@Override
@SuppressWarnings("unchecked")
public SqmExpression<Class<? extends T>> type() {

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmPathSource;
@ -42,4 +43,23 @@ public abstract class AbstractSqmSimplePath<T> extends AbstractSqmPath<T> implem
}
sb.append( getReferencedPathSource().getPathName() );
}
@Override
public SqmPathSource<T> getNodeType() {
return getReferencedPathSource();
}
@Override
public SqmPathSource<T> getReferencedPathSource() {
final SqmPathSource<T> pathSource = super.getNodeType();
if ( pathSource instanceof SingularPersistentAttribute ) {
return ( (SingularPersistentAttribute<?, T>) pathSource ).getPathSource();
}
return pathSource;
}
@Override
public SqmPathSource<T> getModel() {
return super.getNodeType();
}
}

View File

@ -82,6 +82,12 @@ public class SqmEmbeddedValuedSimplePath<T>
@Override
public SqmPath<?> get(String attributeName) {
final SqmPathSource<?> subNavigable = getResolvedModel().getSubPathSource( attributeName );
return resolvePath( attributeName, subNavigable );
}
@Override
public SqmPathSource<?> getResolvedModel() {
final DomainType<?> lhsType;
final SqmPathSource<T> pathSource = getReferencedPathSource();
if ( pathSource.isGeneric() && ( lhsType = getLhs().getReferencedPathSource()
@ -91,11 +97,10 @@ public class SqmEmbeddedValuedSimplePath<T>
pathSource.getPathName()
);
if ( concreteEmbeddable != null ) {
final SqmPathSource<?> subNavigable = concreteEmbeddable.getSubPathSource( attributeName );
return resolvePath( attributeName, subNavigable );
return concreteEmbeddable;
}
}
return super.get( attributeName );
return getModel();
}
@Override
@ -128,6 +133,4 @@ public class SqmEmbeddedValuedSimplePath<T>
public Class<T> getBindableJavaType() {
return getJavaType();
}
}

View File

@ -9,6 +9,8 @@ package org.hibernate.orm.test.annotations.generics;
import java.io.Serializable;
import java.util.Random;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.SessionFactory;
@ -79,9 +81,10 @@ public class EmbeddedIdGenericsSuperclassTest {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Customer> query = cb.createQuery( Customer.class );
final Root<Customer> root = query.from( Customer.class );
final Path<CustomerId> id = root.get( "id" );
assertThat( id ).isNotNull();
assertThat( id.getJavaType() ).isEqualTo( CustomerId.class );
final Path<DomainEntityId> id = root.get( "id" );
assertThat( id.getJavaType() ).isEqualTo( DomainEntityId.class );
assertThat( id.getModel() ).isSameAs( root.getModel().getAttribute( "id" ) );
assertThat( ( (JpaPath<?>) id ).getResolvedModel().getBindableJavaType() ).isEqualTo( CustomerId.class );
query.select( root ).where( cb.equal( id.get( "someDomainField" ), 1 ) );
final Customer customer = session.createQuery( query ).getSingleResult();
assertThat( customer ).isNotNull();
@ -108,9 +111,10 @@ public class EmbeddedIdGenericsSuperclassTest {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Invoice> query = cb.createQuery( Invoice.class );
final Root<Invoice> root = query.from( Invoice.class );
final Path<InvoiceId> id = root.get( "id" );
assertThat( id ).isNotNull();
assertThat( id.getJavaType() ).isEqualTo( InvoiceId.class );
final Path<DomainEntityId> id = root.get( "id" );
assertThat( id.getJavaType() ).isEqualTo( DomainEntityId.class );
assertThat( id.getModel() ).isSameAs( root.getModel().getAttribute( "id" ) );
assertThat( ( (JpaPath<?>) id ).getResolvedModel().getBindableJavaType() ).isEqualTo( InvoiceId.class );
query.select( root ).where( cb.equal( id.get( "someOtherDomainField" ), 1 ) );
final Invoice invoice = session.createQuery( query ).getSingleResult();
assertThat( invoice ).isNotNull();

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.orm.test.annotations.generics;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.SessionFactory;
@ -35,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat;
MultipleEmbeddedGenericsTest.Invoice.class
})
@SessionFactory
@Jira("https://hibernate.atlassian.net/browse/HHH-TODO") // todo marco : create specific issue for this
@Jira("https://hibernate.atlassian.net/browse/HHH-16238")
public class MultipleEmbeddedGenericsTest {
@BeforeAll
public void setUp(SessionFactoryScope scope) {
@ -75,9 +77,13 @@ public class MultipleEmbeddedGenericsTest {
final Path<CustomerEmbeddableOne> firstEmbedded = root.get( "firstEmbedded" );
assertThat( firstEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableOne.class );
assertThat( firstEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "firstEmbedded" ) );
assertThat( ( (JpaPath<?>) firstEmbedded ).getResolvedModel().getBindableJavaType() )
.isEqualTo( CustomerEmbeddableOne.class );
final Path<CustomerEmbeddableTwo> secondEmbedded = root.get( "secondEmbedded" );
assertThat( secondEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableTwo.class );
assertThat( secondEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "secondEmbedded" ) );
assertThat( ( (JpaPath<?>) secondEmbedded ).getResolvedModel().getBindableJavaType() )
.isEqualTo( CustomerEmbeddableTwo.class );
query.select( root ).where( cb.and(
cb.equal( firstEmbedded.get( "genericPropertyA" ), "1" ),
cb.equal( secondEmbedded.get( "customerPropertyB" ), 2 )
@ -107,9 +113,13 @@ public class MultipleEmbeddedGenericsTest {
final Path<InvoiceEmbeddableOne> firstEmbedded = root.get( "firstEmbedded" );
assertThat( firstEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableOne.class );
assertThat( firstEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "firstEmbedded" ) );
assertThat( ( (JpaPath<?>) firstEmbedded ).getResolvedModel().getBindableJavaType() )
.isEqualTo( InvoiceEmbeddableOne.class );
final Path<InvoiceEmbeddableTwo> secondEmbedded = root.get( "secondEmbedded" );
assertThat( secondEmbedded.getJavaType() ).isEqualTo( GenericEmbeddableTwo.class );
assertThat( secondEmbedded.getModel() ).isSameAs( root.getModel().getAttribute( "secondEmbedded" ) );
assertThat( ( (JpaPath<?>) secondEmbedded ).getResolvedModel().getBindableJavaType() )
.isEqualTo( InvoiceEmbeddableTwo.class );
query.select( root ).where( cb.and(
cb.equal( firstEmbedded.get( "invoicePropertyA" ), 1 ),
cb.equal( secondEmbedded.get( "genericPropertyB" ), "2" )