HHH-17864 generate static metamodel fields for embeddables

This commit is contained in:
Gavin King 2024-03-23 15:31:27 +01:00
parent b7038b2294
commit f3dd73dd2f
6 changed files with 95 additions and 28 deletions

View File

@ -483,6 +483,8 @@ public class HibernateProcessor extends AbstractProcessor {
} }
addMetamodelToContext( typeElement, metaEntity ); addMetamodelToContext( typeElement, metaEntity );
if ( context.generateJakartaDataStaticMetamodel() if ( context.generateJakartaDataStaticMetamodel()
// no static metamodel for embeddable classes in Jakarta Data
&& hasAnnotation( element, ENTITY, MAPPED_SUPERCLASS )
// Don't generate a Jakarta Data metamodel // Don't generate a Jakarta Data metamodel
// if this entity was partially mapped in XML // if this entity was partially mapped in XML
&& alreadyExistingMetaEntity == null ) { && alreadyExistingMetaEntity == null ) {

View File

@ -734,25 +734,72 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) { private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
for ( Element memberOfClass : membersOfClass ) { for ( Element memberOfClass : membersOfClass ) {
if ( isPersistent( memberOfClass, membersKind ) ) { if ( isPersistent(memberOfClass, membersKind) ) {
if ( jakartaDataStaticModel ) { addPersistentMember(memberOfClass);
final DataAnnotationMetaAttribute dataMetaAttribute = }
memberOfClass.asType() }
.accept( new DataMetaAttributeGenerationVisitor( this, context ), memberOfClass ); }
if ( dataMetaAttribute != null ) {
members.put( '_' + dataMetaAttribute.getPropertyName(), dataMetaAttribute ); private void addPersistentMember(Element memberOfClass) {
} if ( jakartaDataStaticModel ) {
} final DataAnnotationMetaAttribute dataMetaAttribute =
else { memberOfClass.asType()
final AnnotationMetaAttribute jpaMetaAttribute = .accept( new DataMetaAttributeGenerationVisitor(this, context), memberOfClass );
memberOfClass.asType() if ( dataMetaAttribute != null ) {
.accept( new MetaAttributeGenerationVisitor( this, context ), memberOfClass ); final String path = dataMetaAttribute.getPropertyName();
if ( jpaMetaAttribute != null ) { members.put('_' + path, dataMetaAttribute);
members.put( jpaMetaAttribute.getPropertyName(), jpaMetaAttribute ); 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) { private void validateAssociation(Element memberOfClass) {

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.processor.annotation; package org.hibernate.processor.annotation;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.processor.model.MetaAttribute; import org.hibernate.processor.model.MetaAttribute;
import org.hibernate.processor.model.Metamodel; import org.hibernate.processor.model.Metamodel;
@ -21,14 +22,17 @@ import static org.hibernate.processor.util.TypeUtils.propertyName;
*/ */
public class DataAnnotationMetaAttribute implements MetaAttribute { public class DataAnnotationMetaAttribute implements MetaAttribute {
final Element element; private final Element element;
final AnnotationMetaEntity parent; private final AnnotationMetaEntity parent;
private final String type; 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.element = element;
this.parent = parent; this.parent = parent;
this.type = type; this.type = type;
this.path = path;
} }
@Override @Override
@ -48,7 +52,8 @@ public class DataAnnotationMetaAttribute implements MetaAttribute {
@Override @Override
public String getAttributeDeclarationString() { public String getAttributeDeclarationString() {
final String className = parent.importType( parent.getQualifiedName() ); 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() final String impl = isTextual()
? parent.importType("jakarta.data.metamodel.impl.TextAttributeRecord") ? parent.importType("jakarta.data.metamodel.impl.TextAttributeRecord")
: parent.importType("jakarta.data.metamodel.impl.SortableAttributeRecord"); : parent.importType("jakarta.data.metamodel.impl.SortableAttributeRecord");
@ -63,22 +68,24 @@ public class DataAnnotationMetaAttribute implements MetaAttribute {
.append( "<" ) .append( "<" )
.append( className ) .append( className )
.append( "> " ) .append( "> " )
.append( getPropertyName() ) .append( getPropertyName().replace('.','_') )
.append(" = new ") .append(" = new ")
.append( impl ) .append( impl )
.append( "<>(\"" ) .append( "<>(\"" )
.append(memberName) .append( getPropertyName() )
.append( "\");" ) .append( "\");" )
.toString(); .toString();
} }
@Override @Override
public String getAttributeNameDeclarationString(){ public String getAttributeNameDeclarationString(){
final String fieldName =
getUpperUnderscoreCaseFromLowerCamelCase(getPropertyName().replace('.', '_'));
return new StringBuilder() return new StringBuilder()
.append("public static final ") .append("public static final ")
.append(parent.importType(String.class.getName())) .append(parent.importType(String.class.getName()))
.append(" ") .append(" ")
.append(getUpperUnderscoreCaseFromLowerCamelCase(getPropertyName())) .append(fieldName)
.append(" = ") .append(" = ")
.append("\"") .append("\"")
.append(getPropertyName()) .append(getPropertyName())
@ -89,7 +96,8 @@ public class DataAnnotationMetaAttribute implements MetaAttribute {
@Override @Override
public String getPropertyName() { public String getPropertyName() {
return propertyName( parent, element ); final String propertyName = propertyName(parent, element);
return path == null ? propertyName : path + '.' + propertyName;
} }
@Override @Override

View File

@ -33,10 +33,18 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null
private final AnnotationMetaEntity entity; private final AnnotationMetaEntity entity;
private final Context context; private final Context context;
private final @Nullable String path;
DataMetaAttributeGenerationVisitor(AnnotationMetaEntity entity, Context context) { DataMetaAttributeGenerationVisitor(AnnotationMetaEntity entity, Context context) {
this.entity = entity; this.entity = entity;
this.context = context; 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() { private Types typeUtils() {
@ -45,19 +53,19 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null
@Override @Override
public @Nullable DataAnnotationMetaAttribute visitPrimitive(PrimitiveType primitiveType, Element element) { public @Nullable DataAnnotationMetaAttribute visitPrimitive(PrimitiveType primitiveType, Element element) {
return new DataAnnotationMetaAttribute( entity, element, toTypeString( primitiveType ) ); return new DataAnnotationMetaAttribute( entity, element, toTypeString( primitiveType ), path );
} }
@Override @Override
public @Nullable DataAnnotationMetaAttribute visitArray(ArrayType arrayType, Element element) { 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 @Override
public @Nullable DataAnnotationMetaAttribute visitTypeVariable(TypeVariable typeVariable, Element element) { public @Nullable DataAnnotationMetaAttribute visitTypeVariable(TypeVariable typeVariable, Element element) {
// METAGEN-29 - for a type variable we use the upper bound // METAGEN-29 - for a type variable we use the upper bound
return new DataAnnotationMetaAttribute( entity, element, return new DataAnnotationMetaAttribute( entity, element,
typeUtils().erasure( typeVariable.getUpperBound() ).toString() ); typeUtils().erasure( typeVariable.getUpperBound() ).toString(), path );
} }
@Override @Override
@ -72,7 +80,7 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null
} }
else if ( isBasicAttribute( element, returnedElement, context ) ) { else if ( isBasicAttribute( element, returnedElement, context ) ) {
final String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString(); final String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString();
return new DataAnnotationMetaAttribute( entity, element, type ); return new DataAnnotationMetaAttribute( entity, element, type, path );
} }
else { else {
return null; return null;

View File

@ -22,6 +22,7 @@ public final class Constants {
public static final String ENTITY = "jakarta.persistence.Entity"; public static final String ENTITY = "jakarta.persistence.Entity";
public static final String MAPPED_SUPERCLASS = "jakarta.persistence.MappedSuperclass"; public static final String MAPPED_SUPERCLASS = "jakarta.persistence.MappedSuperclass";
public static final String EMBEDDABLE = "jakarta.persistence.Embeddable"; 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 = "jakarta.persistence.Id";
public static final String ID_CLASS = "jakarta.persistence.IdClass"; public static final String ID_CLASS = "jakarta.persistence.IdClass";
public static final String EMBEDDED_ID = "jakarta.persistence.EmbeddedId"; public static final String EMBEDDED_ID = "jakarta.persistence.EmbeddedId";

View File

@ -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.BASIC;
import static org.hibernate.processor.util.Constants.ELEMENT_COLLECTION; import static org.hibernate.processor.util.Constants.ELEMENT_COLLECTION;
import static org.hibernate.processor.util.Constants.EMBEDDABLE; 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.EMBEDDED_ID;
import static org.hibernate.processor.util.Constants.ENTITY; import static org.hibernate.processor.util.Constants.ENTITY;
import static org.hibernate.processor.util.Constants.ID; 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) { 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 || hasAnnotation( element, "org.hibernate.annotations.Type") // METAGEN-28
|| returnedElement.asType().accept( new BasicAttributeVisitor( context ), returnedElement ); || returnedElement.asType().accept( new BasicAttributeVisitor( context ), returnedElement );
} }