diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java index 4ba720d25e..f9f4f247c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java @@ -423,11 +423,10 @@ public abstract class AbstractIdentifiableType } else { assert type instanceof EmbeddableDomainType; - final EmbeddableDomainType compositeType = (EmbeddableDomainType) type; return new EmbeddedSqmPathSource<>( EntityIdentifierMapping.ID_ROLE_NAME, (SqmPathSource) id, - compositeType, + (EmbeddableDomainType) type, Bindable.BindableType.SINGULAR_ATTRIBUTE, id.isGeneric() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 0be91cfdc2..478002be05 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -2999,9 +2999,14 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem final SqmPath sqmPath = consumeDomainPath( ctx.path() ); final DomainType sqmPathType = sqmPath.getReferencedPathSource().getSqmPathType(); - - if ( sqmPathType instanceof IdentifiableDomainType ) { - final SqmPathSource identifierDescriptor = ( (IdentifiableDomainType) sqmPathType ).getIdentifierDescriptor(); + if ( sqmPathType instanceof IdentifiableDomainType identifiableType ) { + final SqmPathSource identifierDescriptor = identifiableType.getIdentifierDescriptor(); + if ( identifierDescriptor == null ) { + // mainly for benefit of Hibernate Processor + throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() + + "' of 'id()' is a '" + identifiableType.getTypeName() + + "' and does not have a well-defined '@Id' attribute" ); + } return sqmPath.get( identifierDescriptor.getPathName() ); } else if ( sqmPath instanceof SqmAnyValuedSimplePath ) { @@ -3009,7 +3014,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem } else { throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() - + "' of 'id()' function does not resolve to an entity type" ); + + "' of 'id()' does not resolve to an entity type" ); } } @@ -3022,26 +3027,21 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem public SqmPath visitEntityVersionReference(HqlParser.EntityVersionReferenceContext ctx) { final SqmPath sqmPath = consumeDomainPath( ctx.path() ); final DomainType sqmPathType = sqmPath.getReferencedPathSource().getSqmPathType(); - - if ( sqmPathType instanceof IdentifiableDomainType ) { - @SuppressWarnings("unchecked") - final IdentifiableDomainType identifiableType = (IdentifiableDomainType) sqmPathType; - final SingularPersistentAttribute versionAttribute = identifiableType.findVersionAttribute(); - if ( versionAttribute == null ) { - throw new FunctionArgumentException( - String.format( - "Argument '%s' of 'version()' function resolved to entity type '%s' which does not have a '@Version' attribute", - sqmPath.getNavigablePath(), - identifiableType.getTypeName() - ) - ); + if ( sqmPathType instanceof IdentifiableDomainType identifiableType ) { + if ( !identifiableType.hasVersionAttribute() ) { + throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() + + "' of 'version()' is a '" + identifiableType.getTypeName() + + "' and does not have a '@Version' attribute" ); } - + @SuppressWarnings("unchecked") + final SingularPersistentAttribute versionAttribute = + (SingularPersistentAttribute) identifiableType.findVersionAttribute(); return sqmPath.get( versionAttribute ); } - - throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() - + "' of 'version()' function does not resolve to an entity type" ); + else { + throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() + + "' of 'version()' does not resolve to an entity type" ); + } } @Override @@ -3060,35 +3060,29 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem if ( sqmPathType instanceof IdentifiableDomainType ) { @SuppressWarnings("unchecked") - final IdentifiableDomainType identifiableType = (IdentifiableDomainType) sqmPathType; + final IdentifiableDomainType identifiableType = (IdentifiableDomainType) sqmPathType; final List> attributes = identifiableType.findNaturalIdAttributes(); if ( attributes == null ) { - throw new FunctionArgumentException( - String.format( - "Argument '%s' of 'naturalid()' function resolved to entity type '%s' which does not have a natural id", - sqmPath.getNavigablePath(), - identifiableType.getTypeName() - ) + throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() + + "' of 'naturalid()' is a '" + identifiableType.getTypeName() + + "' and does not have a natural id" ); } else if ( attributes.size() >1 ) { - throw new FunctionArgumentException( - String.format( - "Argument '%s' of 'naturalid()' function resolved to entity type '%s' which has a composite natural id", - sqmPath.getNavigablePath(), - identifiableType.getTypeName() - ) + throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() + + "' of 'naturalid()' is a '" + identifiableType.getTypeName() + + "' and has a composite natural id" ); } @SuppressWarnings("unchecked") - SingularAttribute naturalIdAttribute + final SingularAttribute naturalIdAttribute = (SingularAttribute) attributes.get(0); return sqmPath.get( naturalIdAttribute ); } throw new FunctionArgumentException( "Argument '" + sqmPath.getNavigablePath() - + "' of 'naturalid()' function does not resolve to an entity type" ); + + "' of 'naturalid()' does not resolve to an entity type" ); } // // @Override diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Concrete.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Concrete.java index 049a4c5f92..af416bc08f 100644 --- a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Concrete.java +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Concrete.java @@ -3,5 +3,5 @@ package org.hibernate.processor.test.data.basic; import jakarta.data.repository.Repository; @Repository -public interface Concrete extends IdOperations { +public interface Concrete extends IdOperations { } diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/DataTest.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/DataTest.java index 7fcf07a4c8..5637f5977e 100644 --- a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/DataTest.java +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/DataTest.java @@ -19,7 +19,7 @@ import static org.hibernate.processor.test.util.TestUtil.getMetaModelSourceAsStr */ public class DataTest extends CompilationTest { @Test - @WithClasses({ Author.class, Book.class, BookAuthorRepository.class, IdOperations.class, Concrete.class }) + @WithClasses({ Author.class, Book.class, BookAuthorRepository.class, IdOperations.class, Concrete.class, Thing.class }) public void test() { System.out.println( getMetaModelSourceAsString( Author.class ) ); System.out.println( getMetaModelSourceAsString( Book.class ) ); diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/IdOperations.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/IdOperations.java index 0f45d49beb..0525ad8632 100644 --- a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/IdOperations.java +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/IdOperations.java @@ -23,11 +23,6 @@ import jakarta.data.Order; import jakarta.data.Sort; import jakarta.data.repository.Query; -/** - * This interface contains common operations for the NaturalNumbers and AsciiCharacters repositories. - * - * @param type of entity. - */ public interface IdOperations { @Query("where id(this) between ?1 and ?2") Stream findByIdBetween(long minimum, long maximum, Sort sort); diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Thing.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Thing.java new file mode 100644 index 0000000000..5181e9babd --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/basic/Thing.java @@ -0,0 +1,9 @@ +package org.hibernate.processor.test.data.basic; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class Thing { + @Id long id; +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/SpecialVersioned.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/SpecialVersioned.java new file mode 100644 index 0000000000..610c7efb6d --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/SpecialVersioned.java @@ -0,0 +1,7 @@ +package org.hibernate.processor.test.data.versioned; + +import jakarta.persistence.Entity; + +@Entity +public class SpecialVersioned extends Versioned { +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/SpecialVersionedRepo.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/SpecialVersionedRepo.java new file mode 100644 index 0000000000..11fb3fdddb --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/SpecialVersionedRepo.java @@ -0,0 +1,17 @@ +package org.hibernate.processor.test.data.versioned; + +import jakarta.data.repository.Query; +import jakarta.data.repository.Repository; + +@Repository +public interface SpecialVersionedRepo { + @Query("where id(this) = ?1") + SpecialVersioned forId(long id); + + @Query("where id(this) = ?1 and version(this) = ?2") + SpecialVersioned forIdAndVersion(long id, int version); + + @Query("select count(this) from SpecialVersioned") + long count(); + +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/Versioned.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/Versioned.java new file mode 100644 index 0000000000..61405f61ed --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/Versioned.java @@ -0,0 +1,11 @@ +package org.hibernate.processor.test.data.versioned; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class Versioned { + @Id long id; + @Version int version; +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/VersionedRepo.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/VersionedRepo.java new file mode 100644 index 0000000000..df312b5415 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/VersionedRepo.java @@ -0,0 +1,17 @@ +package org.hibernate.processor.test.data.versioned; + +import jakarta.data.repository.Query; +import jakarta.data.repository.Repository; + +@Repository +public interface VersionedRepo { + @Query("where id(this) = ?1") + Versioned forId(long id); + + @Query("where id(this) = ?1 and version(this) = ?2") + Versioned forIdAndVersion(long id, int version); + + @Query("select count(this) from Versioned") + long count(); + +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/VersionedTest.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/VersionedTest.java new file mode 100644 index 0000000000..fbfc622cc3 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/versioned/VersionedTest.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.processor.test.data.versioned; + +import org.hibernate.processor.test.util.CompilationTest; +import org.hibernate.processor.test.util.WithClasses; +import org.junit.Test; + +import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor; +import static org.hibernate.processor.test.util.TestUtil.getMetaModelSourceAsString; + +/** + * @author Gavin King + */ +public class VersionedTest extends CompilationTest { + @Test + @WithClasses({ Versioned.class, VersionedRepo.class, SpecialVersioned.class, SpecialVersionedRepo.class }) + public void test() { + System.out.println( getMetaModelSourceAsString( VersionedRepo.class ) ); + assertMetamodelClassGeneratedFor( Versioned.class, true ); + assertMetamodelClassGeneratedFor( Versioned.class ); + assertMetamodelClassGeneratedFor( SpecialVersioned.class, true ); + assertMetamodelClassGeneratedFor( SpecialVersioned.class ); + assertMetamodelClassGeneratedFor( VersionedRepo.class ); + assertMetamodelClassGeneratedFor( SpecialVersionedRepo.class ); + } +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java index ab8b7b2499..7c2203da39 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java @@ -71,11 +71,9 @@ import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; -import static org.hibernate.grammars.hql.HqlLexer.AS; import static org.hibernate.grammars.hql.HqlLexer.FROM; import static org.hibernate.grammars.hql.HqlLexer.GROUP; import static org.hibernate.grammars.hql.HqlLexer.HAVING; -import static org.hibernate.grammars.hql.HqlLexer.IDENTIFIER; import static org.hibernate.grammars.hql.HqlLexer.ORDER; import static org.hibernate.grammars.hql.HqlLexer.WHERE; import static org.hibernate.internal.util.StringHelper.qualify; @@ -2408,47 +2406,22 @@ public class AnnotationMetaEntity extends AnnotationMeta { } else { final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( hql ); - String thisText = ""; final List allTokens = hqlLexer.getAllTokens(); - for (Token token : allTokens) { - if ( token.getType() == IDENTIFIER ) { - //TEMPORARY until HQL gets support for 'this' - final String text = token.getText(); - if ( text.equalsIgnoreCase("this") ) { - thisText = " as " + text; - } - break; - } - } for (int i = 0; i < allTokens.size(); i++) { final Token token = allTokens.get(i); switch ( token.getType() ) { case FROM: - return thisText.isEmpty() || hasAlias(i, allTokens) ? hql - : new StringBuilder(hql) - .insert(allTokens.get(i+1).getStopIndex() + 1, thisText) - .toString(); + return hql; case WHERE: case HAVING: case GROUP: case ORDER: return new StringBuilder(hql) - .insert(token.getStartIndex(), "from " + entityType + thisText + " ") + .insert(token.getStartIndex(), "from " + entityType + " ") .toString(); } } - return hql + " from " + entityType + thisText; - } - } - - private static boolean hasAlias(int i, List allTokens) { - if ( allTokens.size() <= i+2 ) { - return false; - } - else { - final int nextTokenType = allTokens.get(i+2).getType(); - return nextTokenType == IDENTIFIER - || nextTokenType == AS; + return hql + " from " + entityType; } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockEntityPersister.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockEntityPersister.java index f00fd44572..a533eb72c7 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockEntityPersister.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockEntityPersister.java @@ -12,6 +12,7 @@ import org.hibernate.persister.entity.DiscriminatorMetadata; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.tuple.entity.EntityMetamodel; +import org.hibernate.type.BasicType; import org.hibernate.type.Type; import java.io.Serializable; @@ -113,27 +114,41 @@ public abstract class MockEntityPersister implements EntityPersister, Joinable, abstract Type createPropertyType(String propertyPath); - @Override - public Type getIdentifierType() { - //TODO: propertyType(getIdentifierPropertyName()) - return typeConfiguration.getBasicTypeForJavaType(Long.class); - } - + /** + * Override on subclasses! + */ @Override public String getIdentifierPropertyName() { - //TODO: return the correct @Id property name - return "id"; + return getRootEntityPersister().identifierPropertyName(); } + protected abstract String identifierPropertyName(); + + /** + * Override on subclasses! + */ @Override - public String getRootEntityName() { - for (MockEntityPersister persister : factory.getMockEntityPersisters()) { - if (this != persister && !persister.isSamePersister(this) - && persister.isSubclassPersister(this)) { - return persister.getRootEntityName(); - } - } - return entityName; + public Type getIdentifierType() { + return getRootEntityPersister().identifierType(); + } + + protected abstract Type identifierType(); + + /** + * Override on subclasses! + */ + @Override + public BasicType getVersionType() { + return getRootEntityPersister().versionType(); + } + + protected abstract BasicType versionType(); + + @Override + public abstract String getRootEntityName(); + + public MockEntityPersister getRootEntityPersister() { + return factory.createMockEntityPersister(getRootEntityName()); } @Override @@ -195,7 +210,12 @@ public abstract class MockEntityPersister implements EntityPersister, Joinable, @Override public int getVersionProperty() { - return -66; + return 0; + } + + @Override + public boolean isVersioned() { + return true; } @Override diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java index ead587c394..54e93ba116 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/MockSessionFactory.java @@ -6,6 +6,7 @@ */ package org.hibernate.processor.validation; +import jakarta.persistence.metamodel.Bindable; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.EntityNameResolver; @@ -56,17 +57,24 @@ import org.hibernate.metamodel.internal.JpaMetaModelPopulationSetting; import org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetting; import org.hibernate.metamodel.internal.MetadataContext; import org.hibernate.metamodel.internal.RuntimeMetamodelsImpl; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.metamodel.model.domain.EmbeddableDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.PersistentAttribute; +import org.hibernate.metamodel.model.domain.SimpleDomainType; +import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.metamodel.model.domain.internal.AbstractAttribute; import org.hibernate.metamodel.model.domain.internal.AbstractPluralAttribute; import org.hibernate.metamodel.model.domain.internal.BagAttributeImpl; +import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; import org.hibernate.metamodel.model.domain.internal.BasicTypeImpl; import org.hibernate.metamodel.model.domain.internal.EmbeddableTypeImpl; +import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; import org.hibernate.metamodel.model.domain.internal.EntityTypeImpl; import org.hibernate.metamodel.model.domain.internal.JpaMetamodelImpl; import org.hibernate.metamodel.model.domain.internal.ListAttributeImpl; @@ -101,6 +109,7 @@ import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory; import org.hibernate.stat.internal.StatisticsImpl; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.type.BagType; +import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.ListType; @@ -341,6 +350,12 @@ public abstract class MockSessionFactory .getIdentifierType(); } + public BasicType getVersionType(String className) + throws MappingException { + return createEntityPersister(className) + .getVersionType(); + } + @Override public String getIdentifierPropertyName(String className) throws MappingException { @@ -915,9 +930,73 @@ public abstract class MockSessionFactory metamodel.getJpaMetamodel()); } + @Override + public SingularPersistentAttribute findVersionAttribute() { + final BasicType type = getVersionType(getHibernateEntityName()); + if (type == null) { + return null; + } + else { + return new SingularAttributeImpl<>( + MockEntityDomainType.this, + "{version}", + AttributeClassification.BASIC, + type, + type.getRelationalJavaType(), + null, + false, + true, + false, + false, + metadataContext + ); + } + } + + @Override + public boolean hasVersionAttribute() { + return getVersionType(getHibernateEntityName()) != null; + } + + @Override + public SqmPathSource getIdentifierDescriptor() { + final Type type = getIdentifierType(getHibernateEntityName()); + if (type == null) { + return null; + } + else if (type instanceof BasicDomainType) { + return new BasicSqmPathSource<>( + EntityIdentifierMapping.ID_ROLE_NAME, + null, + (BasicDomainType) type, + MockEntityDomainType.this.getExpressibleJavaType(), + Bindable.BindableType.SINGULAR_ATTRIBUTE, + false + ); + } + else if (type instanceof EmbeddableDomainType) { + return new EmbeddedSqmPathSource<>( + EntityIdentifierMapping.ID_ROLE_NAME, + null, + (EmbeddableDomainType) type, + Bindable.BindableType.SINGULAR_ATTRIBUTE, + false + ); + } + else { + return null; + } + } + @Override public SqmPathSource findSubPathSource(String name, JpaMetamodel metamodel) { - SqmPathSource source = super.findSubPathSource(name, metamodel); + switch (name) { + case EntityIdentifierMapping.ID_ROLE_NAME: + return getIdentifierDescriptor(); + case "{version}": + return findVersionAttribute(); + } + final SqmPathSource source = super.findSubPathSource(name, metamodel); if ( source != null ) { return source; } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/ProcessorSessionFactory.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/ProcessorSessionFactory.java index 0e99f587f6..853113169b 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/ProcessorSessionFactory.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/validation/ProcessorSessionFactory.java @@ -10,6 +10,7 @@ import jakarta.persistence.AccessType; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.PropertyNotFoundException; import org.hibernate.engine.spi.Mapping; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; @@ -148,6 +149,7 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory { } } + @Override Type propertyType(String typeName, String propertyPath) { TypeElement type = findClassByQualifiedName(typeName); @@ -366,6 +368,21 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory { initSubclassPersisters(); } + @Override + public String getRootEntityName() { + TypeElement result = type; + TypeMirror superclass = type.getSuperclass(); + while ( superclass!=null && superclass.getKind() == TypeKind.DECLARED ) { + final DeclaredType declaredType = (DeclaredType) superclass; + final TypeElement typeElement = (TypeElement) declaredType.asElement(); + if ( hasAnnotation(typeElement, "Entity") ) { + result = typeElement; + } + superclass = typeElement.getSuperclass(); + } + return ProcessorSessionFactory.getEntityName(result); + } + @Override boolean isSamePersister(MockEntityPersister entityPersister) { EntityPersister persister = (EntityPersister) entityPersister; @@ -385,6 +402,35 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory { propertyType(symbol, getEntityName(), propertyPath, defaultAccessType); } + @Override + public String identifierPropertyName() { + for (Element element : type.getEnclosedElements()) { + if ( hasAnnotation(element, "Id") || hasAnnotation(element, "EmbeddedId") ) { + return element.getSimpleName().toString(); + } + } + return "id"; + } + + @Override + public Type identifierType() { + for (Element element : type.getEnclosedElements()) { + if ( hasAnnotation(element, "Id")|| hasAnnotation(element, "EmbeddedId") ) { + return propertyType(element, getEntityName(), EntityIdentifierMapping.ID_ROLE_NAME, defaultAccessType); + } + } + return null; + } + + @Override + public BasicType versionType() { + for (Element element : type.getEnclosedElements()) { + if ( hasAnnotation(element, "Version") ) { + return (BasicType) propertyType(element, getEntityName(), "{version}", defaultAccessType); + } + } + return null; + } } public abstract static class ToManyAssociationPersister extends MockCollectionPersister {