diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java index 4199b5afb5..ed1729bdf3 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java @@ -483,6 +483,8 @@ public class HibernateProcessor extends AbstractProcessor { } addMetamodelToContext( typeElement, metaEntity ); if ( context.generateJakartaDataStaticMetamodel() + // no static metamodel for embeddable classes in Jakarta Data + && hasAnnotation( element, ENTITY, MAPPED_SUPERCLASS ) // Don't generate a Jakarta Data metamodel // if this entity was partially mapped in XML && alreadyExistingMetaEntity == null ) { 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 34f7555260..02c71f732a 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 @@ -734,25 +734,72 @@ public class AnnotationMetaEntity extends AnnotationMeta { private void addPersistentMembers(List membersOfClass, AccessType membersKind) { for ( Element memberOfClass : membersOfClass ) { - if ( isPersistent( memberOfClass, membersKind ) ) { - if ( jakartaDataStaticModel ) { - final DataAnnotationMetaAttribute dataMetaAttribute = - memberOfClass.asType() - .accept( new DataMetaAttributeGenerationVisitor( this, context ), memberOfClass ); - if ( dataMetaAttribute != null ) { - members.put( '_' + dataMetaAttribute.getPropertyName(), dataMetaAttribute ); - } - } - else { - final AnnotationMetaAttribute jpaMetaAttribute = - memberOfClass.asType() - .accept( new MetaAttributeGenerationVisitor( this, context ), memberOfClass ); - if ( jpaMetaAttribute != null ) { - members.put( jpaMetaAttribute.getPropertyName(), jpaMetaAttribute ); + if ( isPersistent(memberOfClass, membersKind) ) { + addPersistentMember(memberOfClass); + } + } + } + + private void addPersistentMember(Element memberOfClass) { + if ( jakartaDataStaticModel ) { + final DataAnnotationMetaAttribute dataMetaAttribute = + memberOfClass.asType() + .accept( new DataMetaAttributeGenerationVisitor(this, context), memberOfClass ); + if ( dataMetaAttribute != null ) { + final String path = dataMetaAttribute.getPropertyName(); + members.put('_' + path, dataMetaAttribute); + if ( isEmbedded(memberOfClass) ) { + final TypeMirror type = attributeType(memberOfClass); + final DeclaredType declaredType = (DeclaredType) type; + final TypeElement typeElement = (TypeElement) declaredType.asElement(); + for ( Element field : fieldsIn( typeElement.getEnclosedElements() ) ) { + addEmbeddablePersistentMember(field, path, AccessType.FIELD); + } + for ( Element method : methodsIn( typeElement.getEnclosedElements() ) ) { + if ( isGetterOrSetter(method) ) { + addEmbeddablePersistentMember(method, path, AccessType.PROPERTY); + } } } } } + else { + final AnnotationMetaAttribute jpaMetaAttribute = + memberOfClass.asType() + .accept( new MetaAttributeGenerationVisitor( this, context ), memberOfClass); + if ( jpaMetaAttribute != null ) { + members.put( jpaMetaAttribute.getPropertyName(), jpaMetaAttribute ); + } + } + } + + private void addEmbeddablePersistentMember(Element memberOfEmbeddable, String path, AccessType membersKind) { + if ( isPersistent(memberOfEmbeddable, membersKind) ) { //TODO respect AccessType of embeddable + final DataAnnotationMetaAttribute metaAttribute = + memberOfEmbeddable.asType() + .accept( new DataMetaAttributeGenerationVisitor(this, path, context), + memberOfEmbeddable ); + if (metaAttribute != null) { + members.put('_' + metaAttribute.getPropertyName(), + metaAttribute); + } + } + } + + static boolean isEmbedded(Element memberOfClass) { + if ( hasAnnotation(memberOfClass, EMBEDDED) ) { + return true; + } + else { + final TypeMirror type = attributeType(memberOfClass); + if ( type.getKind() == TypeKind.DECLARED ) { + final DeclaredType declaredType = (DeclaredType) type; + return hasAnnotation( declaredType.asElement(), EMBEDDABLE ); + } + else { + return false; + } + } } private void validateAssociation(Element memberOfClass) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataAnnotationMetaAttribute.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataAnnotationMetaAttribute.java index 234a03cff5..0393efd186 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataAnnotationMetaAttribute.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataAnnotationMetaAttribute.java @@ -6,6 +6,7 @@ */ package org.hibernate.processor.annotation; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.processor.model.MetaAttribute; import org.hibernate.processor.model.Metamodel; @@ -21,14 +22,17 @@ import static org.hibernate.processor.util.TypeUtils.propertyName; */ public class DataAnnotationMetaAttribute implements MetaAttribute { - final Element element; - final AnnotationMetaEntity parent; + private final Element element; + private final AnnotationMetaEntity parent; private final String type; + private final @Nullable String path; - public DataAnnotationMetaAttribute(AnnotationMetaEntity parent, Element element, String type) { + public DataAnnotationMetaAttribute( + AnnotationMetaEntity parent, Element element, String type, @Nullable String path) { this.element = element; this.parent = parent; this.type = type; + this.path = path; } @Override @@ -48,7 +52,8 @@ public class DataAnnotationMetaAttribute implements MetaAttribute { @Override public String getAttributeDeclarationString() { final String className = parent.importType( parent.getQualifiedName() ); - final String memberName = element.getSimpleName().toString(); + final String elementName = element.getSimpleName().toString(); + final String memberName = path == null ? elementName : path + '.' + elementName; final String impl = isTextual() ? parent.importType("jakarta.data.metamodel.impl.TextAttributeRecord") : parent.importType("jakarta.data.metamodel.impl.SortableAttributeRecord"); @@ -63,22 +68,24 @@ public class DataAnnotationMetaAttribute implements MetaAttribute { .append( "<" ) .append( className ) .append( "> " ) - .append( getPropertyName() ) + .append( getPropertyName().replace('.','_') ) .append(" = new ") .append( impl ) .append( "<>(\"" ) - .append(memberName) + .append( getPropertyName() ) .append( "\");" ) .toString(); } @Override public String getAttributeNameDeclarationString(){ + final String fieldName = + getUpperUnderscoreCaseFromLowerCamelCase(getPropertyName().replace('.', '_')); return new StringBuilder() .append("public static final ") .append(parent.importType(String.class.getName())) .append(" ") - .append(getUpperUnderscoreCaseFromLowerCamelCase(getPropertyName())) + .append(fieldName) .append(" = ") .append("\"") .append(getPropertyName()) @@ -89,7 +96,8 @@ public class DataAnnotationMetaAttribute implements MetaAttribute { @Override public String getPropertyName() { - return propertyName( parent, element ); + final String propertyName = propertyName(parent, element); + return path == null ? propertyName : path + '.' + propertyName; } @Override diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataMetaAttributeGenerationVisitor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataMetaAttributeGenerationVisitor.java index 5fa3cd8f25..e0873377b9 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataMetaAttributeGenerationVisitor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DataMetaAttributeGenerationVisitor.java @@ -33,10 +33,18 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null private final AnnotationMetaEntity entity; private final Context context; + private final @Nullable String path; DataMetaAttributeGenerationVisitor(AnnotationMetaEntity entity, Context context) { this.entity = entity; this.context = context; + this.path = null; + } + + DataMetaAttributeGenerationVisitor(AnnotationMetaEntity entity, String path, Context context) { + this.entity = entity; + this.context = context; + this.path = path; } private Types typeUtils() { @@ -45,19 +53,19 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null @Override public @Nullable DataAnnotationMetaAttribute visitPrimitive(PrimitiveType primitiveType, Element element) { - return new DataAnnotationMetaAttribute( entity, element, toTypeString( primitiveType ) ); + return new DataAnnotationMetaAttribute( entity, element, toTypeString( primitiveType ), path ); } @Override public @Nullable DataAnnotationMetaAttribute visitArray(ArrayType arrayType, Element element) { - return new DataAnnotationMetaAttribute( entity, element, toArrayTypeString( arrayType, context ) ); + return new DataAnnotationMetaAttribute( entity, element, toArrayTypeString( arrayType, context ), path ); } @Override public @Nullable DataAnnotationMetaAttribute visitTypeVariable(TypeVariable typeVariable, Element element) { // METAGEN-29 - for a type variable we use the upper bound return new DataAnnotationMetaAttribute( entity, element, - typeUtils().erasure( typeVariable.getUpperBound() ).toString() ); + typeUtils().erasure( typeVariable.getUpperBound() ).toString(), path ); } @Override @@ -72,7 +80,7 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null } else if ( isBasicAttribute( element, returnedElement, context ) ) { final String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString(); - return new DataAnnotationMetaAttribute( entity, element, type ); + return new DataAnnotationMetaAttribute( entity, element, type, path ); } else { return null; diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java index 309607f9be..a00e5df64e 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java @@ -22,6 +22,7 @@ public final class Constants { public static final String ENTITY = "jakarta.persistence.Entity"; public static final String MAPPED_SUPERCLASS = "jakarta.persistence.MappedSuperclass"; public static final String EMBEDDABLE = "jakarta.persistence.Embeddable"; + public static final String EMBEDDED = "jakarta.persistence.Embedded"; public static final String ID = "jakarta.persistence.Id"; public static final String ID_CLASS = "jakarta.persistence.IdClass"; public static final String EMBEDDED_ID = "jakarta.persistence.EmbeddedId"; diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java index 3c5090375f..960ca86fcc 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java @@ -43,6 +43,7 @@ import static org.hibernate.processor.util.Constants.ACCESS; import static org.hibernate.processor.util.Constants.BASIC; import static org.hibernate.processor.util.Constants.ELEMENT_COLLECTION; import static org.hibernate.processor.util.Constants.EMBEDDABLE; +import static org.hibernate.processor.util.Constants.EMBEDDED; import static org.hibernate.processor.util.Constants.EMBEDDED_ID; import static org.hibernate.processor.util.Constants.ENTITY; import static org.hibernate.processor.util.Constants.ID; @@ -551,7 +552,7 @@ public final class TypeUtils { } public static boolean isBasicAttribute(Element element, Element returnedElement, Context context) { - return hasAnnotation( element, BASIC, ONE_TO_ONE, MANY_TO_ONE, EMBEDDED_ID, ID ) + return hasAnnotation( element, BASIC, ONE_TO_ONE, MANY_TO_ONE, EMBEDDED, EMBEDDED_ID, ID ) || hasAnnotation( element, "org.hibernate.annotations.Type") // METAGEN-28 || returnedElement.asType().accept( new BasicAttributeVisitor( context ), returnedElement ); }