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 );
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 ) {

View File

@ -734,25 +734,72 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private void addPersistentMembers(List<? extends Element> 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) {

View File

@ -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

View File

@ -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;

View File

@ -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";

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.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 );
}