HHH-17772 Jakarta Data static metamodel

This commit is contained in:
Gavin King 2024-02-24 17:15:33 +01:00
parent a23792ca1b
commit 67a5590316
11 changed files with 221 additions and 151 deletions

View File

@ -6,6 +6,14 @@
*/
package org.hibernate.jpamodelgen;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import javax.annotation.processing.FilerException;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
@ -13,23 +21,6 @@ import java.io.StringWriter;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.annotation.processing.FilerException;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
/**
* Helper class to write the actual metamodel class using the {@link javax.annotation.processing.Filer} API.
@ -50,7 +41,7 @@ public final class ClassWriter {
String body = generateBody( entity, context ).toString();
FileObject fo = context.getProcessingEnvironment().getFiler().createSourceFile(
getFullyQualifiedClassName( entity, metaModelPackage, context ),
getFullyQualifiedClassName( entity, metaModelPackage ),
entity.getElement()
);
OutputStream os = fo.openOutputStream();
@ -95,7 +86,7 @@ public final class ClassWriter {
pw.println( writeScopeAnnotation( entity ) );
}
if ( entity.getElement() instanceof TypeElement && !entity.isInjectable() ) {
pw.println( writeStaticMetaModelAnnotation( entity, context ) );
pw.println( writeStaticMetaModelAnnotation( entity ) );
}
if ( context.addGeneratedAnnotation() ) {
pw.println( writeGeneratedAnnotation( entity, context ) );
@ -104,7 +95,7 @@ public final class ClassWriter {
pw.println( writeSuppressWarnings() );
}
printClassDeclaration( entity, pw, context );
printClassDeclaration( entity, pw );
pw.println();
@ -128,13 +119,13 @@ public final class ClassWriter {
}
}
private static void printClassDeclaration(Metamodel entity, PrintWriter pw, Context context) {
private static void printClassDeclaration(Metamodel entity, PrintWriter pw) {
pw.print( entity.isImplementation() ? "public class " : "public abstract class " );
pw.print( generatedClassName( context, entity.getSimpleName() ) );
pw.print( getGeneratedClassName(entity) );
String superClassName = findMappedSuperClass( entity, context );
String superClassName = entity.getSupertypeName();
if ( superClassName != null ) {
pw.print( " extends " + generatedClassName( context, superClassName ) );
pw.print( " extends " + getGeneratedSuperclassName(entity, superClassName) );
}
if ( entity.isImplementation() ) {
pw.print( entity.getElement().getKind() == ElementKind.CLASS ? " extends " : " implements " );
@ -144,68 +135,36 @@ public final class ClassWriter {
pw.println( " {" );
}
private static String generatedClassName(Context context, String superClassName) {
return context.useJakartaDataStyle() ? '_' + superClassName : superClassName + '_';
}
private static @Nullable String findMappedSuperClass(Metamodel entity, Context context) {
Element element = entity.getElement();
if ( element instanceof TypeElement ) {
TypeMirror superClass = ((TypeElement) element).getSuperclass();
//superclass of Object is of NoType which returns some other kind
while ( superClass.getKind() == TypeKind.DECLARED ) {
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return superClassName;
}
superClass = ( (TypeElement) superClassElement ).getSuperclass();
}
}
return null;
}
/**
* Checks whether this metamodel class needs to extend another metamodel class.
* This method checks whether the processor has generated a metamodel class for the super class, but it also
* allows for the possibility that the metamodel class was generated in a previous compilation. (It could be
* part of a separate jar. See also METAGEN-35.)
*
* @param superClassElement the super class element
* @param entityMetaComplete flag indicating if the entity for which the metamodel should be generated is
* metamodel complete. If so we cannot use reflection to decide whether we have to add the extends clause
* @param context the execution context
*
* @return {@code true} in case there is super class metamodel to extend from {@code false} otherwise.
*/
private static boolean extendsSuperMetaModel(Element superClassElement, boolean entityMetaComplete, Context context) {
// if we processed the superclass in the same run we definitely need to extend
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( context.containsMetaEntity( superClassName )
|| context.containsMetaEmbeddable( superClassName ) ) {
return true;
}
// to allow for the case that the metamodel class for the super entity is for example contained in another
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
// and annotations should be ignored
if ( !entityMetaComplete
&& containsAnnotation( superClassElement, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
return true;
}
return false;
}
private static String getFullyQualifiedClassName(Metamodel entity, String metaModelPackage, Context context) {
private static String getFullyQualifiedClassName(Metamodel entity, String metaModelPackage) {
String fullyQualifiedClassName = "";
if ( !metaModelPackage.isEmpty() ) {
fullyQualifiedClassName = fullyQualifiedClassName + metaModelPackage + ".";
}
fullyQualifiedClassName = fullyQualifiedClassName + generatedClassName( context, entity.getSimpleName() );
fullyQualifiedClassName = fullyQualifiedClassName + getGeneratedClassName( entity );
return fullyQualifiedClassName;
}
private static String getGeneratedClassName(Metamodel entity) {
final String className = entity.getSimpleName();
return entity.isJakartaDataStyle() ? '_' + className : className + '_';
}
private static String getGeneratedSuperclassName(Metamodel entity, String superClassName) {
if ( entity.isJakartaDataStyle() ) {
int lastDot = superClassName.lastIndexOf('.');
if ( lastDot<0 ) {
return '_' + superClassName;
}
else {
return superClassName.substring(0,lastDot+1)
+ '_' + superClassName.substring(lastDot+1);
}
}
else {
return superClassName + '_';
}
}
private static String writeGeneratedAnnotation(Metamodel entity, Context context) {
StringBuilder generatedAnnotation = new StringBuilder();
generatedAnnotation
@ -239,8 +198,8 @@ public final class ClassWriter {
return "@" + entity.importType( entity.scope() );
}
private static String writeStaticMetaModelAnnotation(Metamodel entity, Context context) {
final String annotation = context.useJakartaDataStyle()
private static String writeStaticMetaModelAnnotation(Metamodel entity) {
final String annotation = entity.isJakartaDataStyle()
? "jakarta.data.metamodel.StaticMetamodel"
: "jakarta.persistence.metamodel.StaticMetamodel";
return "@" + entity.importType( annotation ) + "(" + entity.getSimpleName() + ".class)";

View File

@ -76,7 +76,6 @@ public final class Context {
private boolean addTransactionScopedAnnotation;
private AccessType persistenceUnitDefaultAccessType;
private boolean generateJakartaDataStaticMetamodel;
private boolean jakartaDataStyle;
// keep track of all classes for which model have been generated
private final Collection<String> generatedModelClasses = new HashSet<>();
@ -122,14 +121,6 @@ public final class Context {
return processingEnvironment;
}
public boolean useJakartaDataStyle() {
return jakartaDataStyle;
}
public void setJakartaDataStyle(boolean jakartaDataStyle) {
this.jakartaDataStyle = jakartaDataStyle;
}
public boolean generateJakartaDataStaticMetamodel() {
return generateJakartaDataStaticMetamodel;
}
@ -210,61 +201,61 @@ public final class Context {
return ormXmlFiles;
}
public boolean containsMetaEntity(String fqcn) {
return metaEntities.containsKey( fqcn );
public boolean containsMetaEntity(String qualifiedName) {
return metaEntities.containsKey( qualifiedName );
}
public @Nullable Metamodel getMetaEntity(String fqcn) {
return metaEntities.get( fqcn );
public @Nullable Metamodel getMetaEntity(String qualifiedName) {
return metaEntities.get( qualifiedName );
}
public Collection<Metamodel> getMetaEntities() {
return metaEntities.values();
}
public void addMetaEntity(String fqcn, Metamodel metaEntity) {
metaEntities.put( fqcn, metaEntity );
public void addMetaEntity(String qualifiedName, Metamodel metaEntity) {
metaEntities.put( qualifiedName, metaEntity );
}
public boolean containsMetaEmbeddable(String fqcn) {
return metaEmbeddables.containsKey( fqcn );
public boolean containsMetaEmbeddable(String qualifiedName) {
return metaEmbeddables.containsKey( qualifiedName );
}
public @Nullable Metamodel getMetaEmbeddable(String fqcn) {
return metaEmbeddables.get( fqcn );
public @Nullable Metamodel getMetaEmbeddable(String qualifiedName) {
return metaEmbeddables.get( qualifiedName );
}
public void addMetaEmbeddable(String fqcn, Metamodel metaEntity) {
metaEmbeddables.put( fqcn, metaEntity );
public void addMetaEmbeddable(String qualifiedName, Metamodel metaEntity) {
metaEmbeddables.put( qualifiedName, metaEntity );
}
public Collection<Metamodel> getMetaEmbeddables() {
return metaEmbeddables.values();
}
public @Nullable Metamodel getMetaAuxiliary(String fqcn) {
return metaAuxiliaries.get( fqcn );
public @Nullable Metamodel getMetaAuxiliary(String qualifiedName) {
return metaAuxiliaries.get( qualifiedName );
}
public Collection<Metamodel> getMetaAuxiliaries() {
return metaAuxiliaries.values();
}
public void addMetaAuxiliary(String fqcn, Metamodel metamodel) {
metaAuxiliaries.put( fqcn, metamodel);
public void addMetaAuxiliary(String qualifiedName, Metamodel metamodel) {
metaAuxiliaries.put( qualifiedName, metamodel);
}
public void addAccessTypeInformation(String fqcn, AccessTypeInformation info) {
accessTypeInformation.put( fqcn, info );
public void addAccessTypeInformation(String qualifiedName, AccessTypeInformation info) {
accessTypeInformation.put( qualifiedName, info );
}
public @Nullable AccessTypeInformation getAccessTypeInfo(String fqcn) {
return accessTypeInformation.get( fqcn );
public @Nullable AccessTypeInformation getAccessTypeInfo(String qualifiedName) {
return accessTypeInformation.get( qualifiedName );
}
public TypeElement getTypeElementForFullyQualifiedName(String fqcn) {
public TypeElement getTypeElementForFullyQualifiedName(String qualifiedName) {
Elements elementUtils = processingEnvironment.getElementUtils();
return elementUtils.getTypeElement( fqcn );
return elementUtils.getTypeElement( qualifiedName );
}
void markGenerated(String name) {

View File

@ -67,14 +67,10 @@ import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.*;
LAZY_XML_PARSING,
ADD_GENERATION_DATE,
ADD_GENERATED_ANNOTATION,
ADD_SUPPRESS_WARNINGS_ANNOTATION,
JAKARTA_DATA_OPTION
ADD_SUPPRESS_WARNINGS_ANNOTATION
})
public class JPAMetaModelEntityProcessor extends AbstractProcessor {
/**
* Produce Jakarta Data style static metamodel
*/
public static final String JAKARTA_DATA_OPTION = "jakartaDataStyle";
/**
* Debug logging from the processor
*/
@ -174,8 +170,6 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
context.setAddSuppressWarningsAnnotation( parseBoolean( options.get( ADD_SUPPRESS_WARNINGS_ANNOTATION ) ) );
context.setJakartaDataStyle( parseBoolean( options.get( JAKARTA_DATA_OPTION ) ) );
return parseBoolean( options.get( FULLY_ANNOTATION_CONFIGURED_OPTION ) );
}
@ -287,18 +281,22 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
private void createMetaModelClasses() {
for ( Metamodel aux : context.getMetaAuxiliaries() ) {
if ( !context.isAlreadyGenerated( aux.getQualifiedName() ) ) {
final String key = aux.getQualifiedName();
if ( !context.isAlreadyGenerated(key) ) {
context.logMessage( Diagnostic.Kind.OTHER, "Writing metamodel for auxiliary '" + aux + "'" );
ClassWriter.writeFile( aux, context );
context.markGenerated( aux.getQualifiedName() );
context.markGenerated(key);
}
}
for ( Metamodel entity : context.getMetaEntities() ) {
if ( !context.isAlreadyGenerated( entity.getQualifiedName() ) ) {
final String key = entity.isJakartaDataStyle()
? '_' + entity.getQualifiedName()
: entity.getQualifiedName();
if ( !context.isAlreadyGenerated(key) ) {
context.logMessage( Diagnostic.Kind.OTHER, "Writing metamodel for entity '" + entity + "'" );
ClassWriter.writeFile( entity, context );
context.markGenerated( entity.getQualifiedName() );
context.markGenerated(key);
}
}
@ -310,7 +308,10 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
int toProcessCountBeforeLoop = toProcessEntities.size();
for ( Metamodel entity : toProcessEntities ) {
// see METAGEN-36
if ( context.isAlreadyGenerated( entity.getQualifiedName() ) ) {
final String key = entity.isJakartaDataStyle()
? '_' + entity.getQualifiedName()
: entity.getQualifiedName();
if ( context.isAlreadyGenerated(key) ) {
processedEntities.add( entity );
}
else if ( !modelGenerationNeedsToBeDeferred( toProcessEntities, entity ) ) {
@ -319,7 +320,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
"Writing meta model for embeddable/mapped superclass " + entity
);
ClassWriter.writeFile( entity, context );
context.markGenerated( entity.getQualifiedName() );
context.markGenerated(key);
processedEntities.add( entity );
}
}
@ -400,7 +401,8 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
for ( AnnotationMirror mirror : element.getAnnotationMirrors() ) {
final TypeElement typeElement = (TypeElement) element;
final String qualifiedName = typeElement.getQualifiedName().toString();
final Metamodel alreadyExistingMetaEntity = tryGettingExistingEntityFromContext( mirror, qualifiedName );
final Metamodel alreadyExistingMetaEntity =
tryGettingExistingEntityFromContext( mirror, qualifiedName );
if ( alreadyExistingMetaEntity != null && alreadyExistingMetaEntity.isMetaComplete() ) {
context.logMessage(
Diagnostic.Kind.OTHER,
@ -408,15 +410,29 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
+ "' since XML configuration is metadata complete.");
}
else {
boolean requiresLazyMemberInitialization
= containsAnnotation( element, EMBEDDABLE )
|| containsAnnotation( element, MAPPED_SUPERCLASS );
final boolean requiresLazyMemberInitialization
= hasAnnotation( element, EMBEDDABLE, MAPPED_SUPERCLASS );
final AnnotationMetaEntity metaEntity =
AnnotationMetaEntity.create( typeElement, context, requiresLazyMemberInitialization, true );
AnnotationMetaEntity.create( typeElement, context,
requiresLazyMemberInitialization, true );
if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity );
}
addMetaEntityToContext( mirror, metaEntity );
if ( context.generateJakartaDataStaticMetamodel()
// Don't generate a Jakarta Data metamodel
// if this entity was partially mapped in XML
&& alreadyExistingMetaEntity == null ) {
final AnnotationMetaEntity dataMetaEntity =
AnnotationMetaEntity.create( typeElement, context,
requiresLazyMemberInitialization, true, true );
// final Metamodel alreadyExistingDataMetaEntity =
// tryGettingExistingEntityFromContext( mirror, '_' + qualifiedName );
// if ( alreadyExistingDataMetaEntity != null ) {
// dataMetaEntity.mergeInMembers( alreadyExistingDataMetaEntity );
// }
addMetaEntityToContext( mirror, dataMetaEntity );
}
}
}
}
@ -448,14 +464,17 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
private void addMetaEntityToContext(AnnotationMirror mirror, AnnotationMetaEntity metaEntity) {
final String key = metaEntity.isJakartaDataStyle()
? '_' + metaEntity.getQualifiedName()
: metaEntity.getQualifiedName();
if ( isAnnotationMirrorOfType( mirror, ENTITY ) ) {
context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity );
context.addMetaEntity( key, metaEntity );
}
else if ( isAnnotationMirrorOfType( mirror, MAPPED_SUPERCLASS ) ) {
context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity );
context.addMetaEntity( key, metaEntity );
}
else if ( isAnnotationMirrorOfType( mirror, EMBEDDABLE ) ) {
context.addMetaEmbeddable( metaEntity.getQualifiedName(), metaEntity );
context.addMetaEmbeddable( key, metaEntity );
}
}

View File

@ -59,7 +59,6 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
.append( parent.importType( getTypeDeclaration() ) )
.append( "> " )
.append( getPropertyName() )
.append( parent.getContext().useJakartaDataStyle() ? "_" : "" )
.append( ";" )
.toString();
}

View File

@ -86,6 +86,7 @@ import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAccessTypeForHierarchy;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecifiedAccessType;
import static org.hibernate.jpamodelgen.util.TypeUtils.findMappedSuperClass;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValue;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValueRef;
@ -113,6 +114,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private final Context context;
private final boolean managed;
private boolean dataRepository;
private String qualifiedName;
private final boolean jakartaDataStaticModel;
private AccessTypeInformation entityAccessTypeInfo;
@ -148,16 +151,25 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private final Map<String,String> memberTypes = new HashMap<>();
public AnnotationMetaEntity(TypeElement element, Context context, boolean managed) {
public AnnotationMetaEntity(TypeElement element, Context context, boolean managed, boolean jakartaData) {
this.element = element;
this.context = context;
this.managed = managed;
this.members = new HashMap<>();
this.importContext = new ImportContextImpl( getPackageName( context, element ) );
jakartaDataStaticModel = jakartaData;
}
public static AnnotationMetaEntity create(TypeElement element, Context context, boolean lazilyInitialised, boolean managed) {
final AnnotationMetaEntity annotationMetaEntity = new AnnotationMetaEntity( element, context, managed );
public static AnnotationMetaEntity create(
TypeElement element, Context context,
boolean lazilyInitialised, boolean managed) {
return create( element,context, lazilyInitialised, managed, false );
}
public static AnnotationMetaEntity create(
TypeElement element, Context context,
boolean lazilyInitialised, boolean managed, boolean jakartaData) {
final AnnotationMetaEntity annotationMetaEntity = new AnnotationMetaEntity( element, context, managed, jakartaData );
if ( !lazilyInitialised ) {
annotationMetaEntity.init();
}
@ -182,6 +194,11 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return repository;
}
@Override
public boolean isJakartaDataStyle() {
return jakartaDataStaticModel;
}
@Override
public final String getSimpleName() {
return element.getSimpleName().toString();
@ -189,7 +206,15 @@ public class AnnotationMetaEntity extends AnnotationMeta {
@Override
public final String getQualifiedName() {
return element.getQualifiedName().toString();
if ( qualifiedName == null ) {
qualifiedName = element.getQualifiedName().toString();
}
return qualifiedName;
}
@Override
public @Nullable String getSupertypeName() {
return findMappedSuperClass( this, context );
}
@Override
@ -498,13 +523,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
for ( Element memberOfClass : membersOfClass ) {
if ( isPersistent( memberOfClass, membersKind ) ) {
final AnnotationMetaAttribute jpaMetaAttribute =
memberOfClass.asType()
.accept( new MetaAttributeGenerationVisitor( this, context ), memberOfClass );
if ( jpaMetaAttribute != null ) {
members.put( jpaMetaAttribute.getPropertyName(), jpaMetaAttribute );
}
if ( context.generateJakartaDataStaticMetamodel() ) {
if ( jakartaDataStaticModel ) {
final DataAnnotationMetaAttribute dataMetaAttribute =
memberOfClass.asType()
.accept( new DataMetaAttributeGenerationVisitor( this, context ), memberOfClass );
@ -512,6 +531,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
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 );
}
}
}
}
}

View File

@ -41,7 +41,6 @@ public class AnnotationMetaMap extends AnnotationMetaCollection {
.append( parent.importType( getTypeDeclaration() ) )
.append("> ")
.append( getPropertyName() )
.append( parent.getContext().useJakartaDataStyle() ? "_" : "" )
.append(";")
.toString();
}

View File

@ -67,6 +67,11 @@ public class AnnotationMetaPackage extends AnnotationMeta {
return element.getQualifiedName().toString();
}
@Override
public @Nullable String getSupertypeName() {
throw new UnsupportedOperationException();
}
@Override
public final String getPackageName() {
return getPackageName( context, element );
@ -154,4 +159,9 @@ public class AnnotationMetaPackage extends AnnotationMeta {
public String scope() {
throw new UnsupportedOperationException();
}
@Override
public boolean isJakartaDataStyle() {
return false;
}
}

View File

@ -60,7 +60,6 @@ public class DataAnnotationMetaAttribute implements MetaAttribute {
.append( "<" )
.append( className )
.append( "> " )
.append( parent.getContext().useJakartaDataStyle() ? "" : "_" )
.append( getPropertyName() )
.append(" = ")
.append( getMetaImpl(className, memberName) )

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.jpamodelgen.model;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.jpamodelgen.Context;
import java.util.List;
@ -19,6 +20,8 @@ public interface Metamodel extends ImportContext {
String getQualifiedName();
@Nullable String getSupertypeName();
String getPackageName();
List<MetaAttribute> getMembers();
@ -40,4 +43,6 @@ public interface Metamodel extends ImportContext {
boolean isInjectable();
String scope();
boolean isJakartaDataStyle();
}

View File

@ -10,6 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.MetaModelGenerationException;
import org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
@ -610,6 +611,55 @@ public final class TypeUtils {
}
}
public static @Nullable String findMappedSuperClass(Metamodel entity, Context context) {
Element element = entity.getElement();
if ( element instanceof TypeElement ) {
TypeMirror superClass = ((TypeElement) element).getSuperclass();
//superclass of Object is of NoType which returns some other kind
while ( superClass.getKind() == TypeKind.DECLARED ) {
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return superClassName;
}
superClass = ( (TypeElement) superClassElement ).getSuperclass();
}
}
return null;
}
/**
* Checks whether this metamodel class needs to extend another metamodel class.
* This method checks whether the processor has generated a metamodel class for the super class, but it also
* allows for the possibility that the metamodel class was generated in a previous compilation. (It could be
* part of a separate jar. See also METAGEN-35.)
*
* @param superClassElement the super class element
* @param entityMetaComplete flag indicating if the entity for which the metamodel should be generated is
* metamodel complete. If so we cannot use reflection to decide whether we have to add the extends clause
* @param context the execution context
*
* @return {@code true} in case there is super class metamodel to extend from {@code false} otherwise.
*/
private static boolean extendsSuperMetaModel(Element superClassElement, boolean entityMetaComplete, Context context) {
// if we processed the superclass in the same run we definitely need to extend
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( context.containsMetaEntity( superClassName )
|| context.containsMetaEmbeddable( superClassName ) ) {
return true;
}
// to allow for the case that the metamodel class for the super entity is for example contained in another
// jar file we use reflection. However, we need to consider the fact that there is xml configuration
// and annotations should be ignored
if ( !entityMetaComplete
&& containsAnnotation( superClassElement, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
return true;
}
return false;
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable String, Element> {
private final Context context;

View File

@ -47,6 +47,8 @@ import org.hibernate.jpamodelgen.xml.jaxb.OneToOne;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.jpamodelgen.util.StringUtil.determineFullyQualifiedClassName;
import static org.hibernate.jpamodelgen.util.TypeUtils.extractClosestRealTypeAsString;
import static org.hibernate.jpamodelgen.util.TypeUtils.findMappedSuperClass;
import static org.hibernate.jpamodelgen.util.TypeUtils.getElementKindForAccessType;
import static org.hibernate.jpamodelgen.xml.jaxb.AccessType.*;
@ -132,7 +134,7 @@ public class XmlMetaEntity implements Metamodel {
this.isMetaComplete = initIsMetaComplete( context, metaComplete );
}
private final void init() {
private void init() {
context.logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
this.accessTypeInfo = NullnessUtil.castNonNull( context.getAccessTypeInfo( getQualifiedName() ) );
@ -158,6 +160,11 @@ public class XmlMetaEntity implements Metamodel {
return packageName;
}
@Override
public @Nullable String getSupertypeName() {
return findMappedSuperClass( this, context );
}
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
init();
@ -265,12 +272,12 @@ public class XmlMetaEntity implements Metamodel {
private void determineTargetType(DeclaredType type, String propertyName, String explicitTargetEntity, @Nullable String[] types) {
List<? extends TypeMirror> typeArguments = type.getTypeArguments();
if ( typeArguments.size() == 0 && explicitTargetEntity == null ) {
if ( typeArguments.isEmpty() && explicitTargetEntity == null ) {
throw new MetaModelGenerationException( "Unable to determine target entity type for " + clazzName + "." + propertyName + "." );
}
if ( explicitTargetEntity == null ) {
types[0] = TypeUtils.extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
types[0] = extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
}
else {
types[0] = explicitTargetEntity;
@ -632,4 +639,9 @@ public class XmlMetaEntity implements Metamodel {
public String scope() {
throw new UnsupportedOperationException();
}
@Override
public boolean isJakartaDataStyle() {
return false;
}
}