diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index fc6259bc7f..5140680888 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -481,10 +481,22 @@ public class EntityBinder { final ClassDetails classWithIdClass = inheritanceState.getClassWithIdClass( false ); if ( classWithIdClass != null ) { final IdClass idClassAnn = classWithIdClass.getDirectAnnotationUsage( IdClass.class ); - final Class idClassValue = idClassAnn.value(); - final ClassDetails compositeClass = - getMetadataCollector().getSourceModelBuildingContext().getClassDetailsRegistry() - .resolveClassDetails( idClassValue.getName() ); + final ClassDetails compositeClass; + if ( idClassAnn == null ) { + try { + compositeClass = getMetadataCollector().getSourceModelBuildingContext() + .getClassDetailsRegistry() + .resolveClassDetails( inheritanceState.getClassDetails().getClassName() + "_$Id" ); + } + catch (RuntimeException e) { + return false; + } + } + else { + final Class idClassValue = idClassAnn.value(); + compositeClass = getMetadataCollector().getSourceModelBuildingContext() + .getClassDetailsRegistry().resolveClassDetails( idClassValue.getName() ); + } final TypeDetails compositeType = new ClassTypeDetailsImpl( compositeClass, TypeDetails.Kind.CLASS ); final TypeDetails classWithIdType = new ClassTypeDetailsImpl( classWithIdClass, TypeDetails.Kind.CLASS ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java index c149b68062..cb52313d36 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/InheritanceState.java @@ -7,6 +7,7 @@ package org.hibernate.boot.model.internal; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import org.hibernate.AnnotationException; import org.hibernate.boot.spi.AccessType; @@ -183,6 +184,13 @@ public class InheritanceState { return classDetails; } else { + final long count = Stream.concat( + classDetails.getFields().stream(), + classDetails.getMethods().stream() + ).filter( t -> t.hasDirectAnnotationUsage( Id.class ) ).count(); + if ( count > 1 ) { + return classDetails; + } final InheritanceState state = getSuperclassInheritanceState( classDetails, inheritanceStatePerClass ); if ( state != null ) { return state.getClassWithIdClass( true ); 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 18979638df..1ab334d77d 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 @@ -108,6 +108,8 @@ import static org.hibernate.processor.util.TypeUtils.propertyName; */ public class AnnotationMetaEntity extends AnnotationMeta { + private static final String ID_CLASS_MEMBER_NAME = ""; + private final ImportContext importContext; private final TypeElement element; private final Map members; @@ -438,6 +440,8 @@ public class AnnotationMetaEntity extends AnnotationMeta { addPersistentMembers( fieldsOfClass, AccessType.FIELD ); addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY ); + + addIdClassIfNeeded( fieldsOfClass, gettersAndSettersOfClass ); } addAuxiliaryMembers(); @@ -451,6 +455,33 @@ public class AnnotationMetaEntity extends AnnotationMeta { initialized = true; } + private void addIdClassIfNeeded(List fields, List methods) { + if ( hasAnnotation( element, ID_CLASS ) ) { + return; + } + final List components = new ArrayList<>(); + for ( Element field : fields ) { + if ( hasAnnotation( field, ID ) && isPersistent( field, AccessType.FIELD ) ) { + final String propertyName = propertyName( this, field ); + if ( members.containsKey( propertyName ) ) { + components.add( members.get( propertyName ) ); + } + } + } + for ( Element method : methods ) { + if ( hasAnnotation( method, ID ) && isPersistent( method, AccessType.PROPERTY ) ) { + final String propertyName = propertyName( this, method ); + if ( members.containsKey( propertyName ) ) { + components.add( members.get( propertyName ) ); + } + } + } + if ( components.size() < 2 ) { + return; + } + putMember( ID_CLASS_MEMBER_NAME, new IdClassMetaAttribute( this, components ) ); + } + private boolean checkEntities(List lifecycleMethods) { boolean foundPersistenceEntity = false; VariableElement nonPersistenceParameter = null; diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaSingleAttribute.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaSingleAttribute.java index c51ff79f5b..e7c40cf38d 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaSingleAttribute.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaSingleAttribute.java @@ -4,11 +4,15 @@ */ package org.hibernate.processor.annotation; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import org.hibernate.processor.model.MetaSingleAttribute; import org.hibernate.processor.util.Constants; +import java.util.ArrayList; +import java.util.List; + /** * @author Max Andersen * @author Hardy Ferentschik @@ -24,4 +28,9 @@ public class AnnotationMetaSingleAttribute extends AnnotationMetaAttribute imple public final String getMetaType() { return Constants.SINGULAR_ATTRIBUTE; } + + @Override + public List inheritedAnnotations() { + return new ArrayList<>(element.getAnnotationMirrors()); + } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdClassMetaAttribute.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdClassMetaAttribute.java new file mode 100644 index 0000000000..9843789cae --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdClassMetaAttribute.java @@ -0,0 +1,73 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.processor.annotation; + +import org.hibernate.processor.model.MetaAttribute; +import org.hibernate.processor.model.Metamodel; + +import java.util.List; + +public class IdClassMetaAttribute implements MetaAttribute { + + private final Metamodel parent; + + private final List components; + + public IdClassMetaAttribute(Metamodel parent, List components) { + this.parent = parent; + this.components = components; + } + + @Override + public boolean hasTypedAttribute() { + return true; + } + + @Override + public boolean hasStringAttribute() { + return false; + } + + @Override + public String getAttributeDeclarationString() { + final StringBuilder decl = new StringBuilder() + .append("\n/**\n * Static ID class for {@link ") + .append( parent.getQualifiedName() ) + .append( "}\n **/\n" ) + .append( "public record Id" ); + String delimiter = "("; + for ( MetaAttribute component : components ) { + decl.append( delimiter ).append( parent.importType( component.getTypeDeclaration() ) ) + .append( ' ' ).append( component.getPropertyName() ); + delimiter = ", "; + } + return decl.append( ") {}" ).toString(); + } + + @Override + public String getAttributeNameDeclarationString() { + return ""; + } + + @Override + public String getMetaType() { + return ""; + } + + @Override + public String getPropertyName() { + return ""; + } + + @Override + public String getTypeDeclaration() { + return ""; + } + + @Override + public Metamodel getHostingEntity() { + return parent; + } +}