generate typesafe references to named queries, fetch profiles, entity graphs, fetch profiles

add support for about @FilterDef
This commit is contained in:
Gavin King 2023-06-06 15:55:59 +02:00
parent 2fb7cdd08b
commit 727a9b2c03
20 changed files with 632 additions and 127 deletions

View File

@ -23,7 +23,7 @@ import javax.tools.Diagnostic;
import javax.tools.FileObject;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils;
@ -49,7 +49,7 @@ public final class ClassWriter {
private ClassWriter() {
}
public static void writeFile(MetaEntity entity, Context context) {
public static void writeFile(Metamodel entity, Context context) {
try {
String metaModelPackage = entity.getPackageName();
// need to generate the body first, since this will also update the required imports which need to
@ -58,7 +58,7 @@ public final class ClassWriter {
FileObject fo = context.getProcessingEnvironment().getFiler().createSourceFile(
getFullyQualifiedClassName( entity, metaModelPackage ),
entity.getTypeElement()
entity.getElement()
);
OutputStream os = fo.openOutputStream();
PrintWriter pw = new PrintWriter( os );
@ -94,46 +94,43 @@ public final class ClassWriter {
*
* @return body content
*/
private static StringBuffer generateBody(MetaEntity entity, Context context) {
private static StringBuffer generateBody(Metamodel entity, Context context) {
StringWriter sw = new StringWriter();
PrintWriter pw = null;
try {
pw = new PrintWriter( sw );
try ( PrintWriter pw = new PrintWriter(sw) ) {
if ( context.addGeneratedAnnotation() ) {
pw.println( writeGeneratedAnnotation( entity, context ) );
if (context.addGeneratedAnnotation()) {
pw.println(writeGeneratedAnnotation(entity, context));
}
if ( context.isAddSuppressWarningsAnnotation() ) {
pw.println( writeSuppressWarnings() );
if (context.isAddSuppressWarningsAnnotation()) {
pw.println(writeSuppressWarnings());
}
pw.println( writeStaticMetaModelAnnotation( entity ) );
if ( entity.getElement() instanceof TypeElement ) {
pw.println(writeStaticMetaModelAnnotation(entity));
}
printClassDeclaration( entity, pw, context );
printClassDeclaration(entity, pw, context);
pw.println();
List<MetaAttribute> members = entity.getMembers();
for ( MetaAttribute metaMember : members ) {
pw.println( " " + metaMember.getAttributeDeclarationString() );
for (MetaAttribute metaMember : members) {
if (metaMember.hasTypedAttribute()) {
pw.println(" " + metaMember.getAttributeDeclarationString());
}
}
pw.println();
for ( MetaAttribute metaMember : members ) {
pw.println( " " + metaMember.getAttributeNameDeclarationString() );
for (MetaAttribute metaMember : members) {
pw.println(" " + metaMember.getAttributeNameDeclarationString());
}
pw.println();
pw.println( "}" );
pw.println("}");
return sw.getBuffer();
}
finally {
if ( pw != null ) {
pw.close();
}
}
}
private static void printClassDeclaration(MetaEntity entity, PrintWriter pw, Context context) {
private static void printClassDeclaration(Metamodel entity, PrintWriter pw, Context context) {
pw.print( "public abstract class " + entity.getSimpleName() + META_MODEL_CLASS_NAME_SUFFIX );
String superClassName = findMappedSuperClass( entity, context );
@ -144,17 +141,20 @@ public final class ClassWriter {
pw.println( " {" );
}
private static @Nullable String findMappedSuperClass(MetaEntity entity, Context context) {
TypeMirror superClass = entity.getTypeElement().getSuperclass();
//superclass of Object is of NoType which returns some other kind
while ( superClass.getKind() == TypeKind.DECLARED ) {
//F..king Ch...t Have those people used their horrible APIs even once?
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return 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 ) {
//F..king Ch...t Have those people used their horrible APIs even once?
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
String superClassName = ( (TypeElement) superClassElement ).getQualifiedName().toString();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return superClassName;
}
superClass = ( (TypeElement) superClassElement ).getSuperclass();
}
superClass = ( (TypeElement) superClassElement ).getSuperclass();
}
return null;
}
@ -191,7 +191,7 @@ public final class ClassWriter {
return false;
}
private static String getFullyQualifiedClassName(MetaEntity entity, String metaModelPackage) {
private static String getFullyQualifiedClassName(Metamodel entity, String metaModelPackage) {
String fullyQualifiedClassName = "";
if ( !metaModelPackage.isEmpty() ) {
fullyQualifiedClassName = fullyQualifiedClassName + metaModelPackage + ".";
@ -200,7 +200,7 @@ public final class ClassWriter {
return fullyQualifiedClassName;
}
private static String writeGeneratedAnnotation(MetaEntity entity, Context context) {
private static String writeGeneratedAnnotation(Metamodel entity, Context context) {
StringBuilder generatedAnnotation = new StringBuilder();
generatedAnnotation.append( "@" )
.append( entity.importType( "jakarta.annotation.Generated" ) )
@ -221,7 +221,7 @@ public final class ClassWriter {
return "@SuppressWarnings({ \"deprecation\", \"rawtypes\" })";
}
private static String writeStaticMetaModelAnnotation(MetaEntity entity) {
private static String writeStaticMetaModelAnnotation(Metamodel entity) {
return "@" + entity.importType( "jakarta.persistence.metamodel.StaticMetamodel" ) + "(" + entity.getSimpleName() + ".class)";
}
}

View File

@ -19,7 +19,7 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants;
@ -37,15 +37,17 @@ public final class Context {
/**
* Used for keeping track of parsed entities and mapped super classes (xml + annotations).
*/
private final Map<String, MetaEntity> metaEntities = new HashMap<String, MetaEntity>();
private final Map<String, Metamodel> metaEntities = new HashMap<>();
/**
* Used for keeping track of parsed embeddable entities. These entities have to be kept separate since
* they are lazily initialized.
*/
private final Map<String, MetaEntity> metaEmbeddables = new HashMap<String, MetaEntity>();
private final Map<String, Metamodel> metaEmbeddables = new HashMap<>();
private final Map<String, AccessTypeInformation> accessTypeInformation = new HashMap<String, AccessTypeInformation>();
private final Map<String, Metamodel> metaAuxiliaries = new HashMap<>();
private final Map<String, AccessTypeInformation> accessTypeInformation = new HashMap<>();
private final ProcessingEnvironment pe;
private final boolean logDebug;
@ -146,15 +148,15 @@ public final class Context {
return metaEntities.containsKey( fqcn );
}
public @Nullable MetaEntity getMetaEntity(String fqcn) {
public @Nullable Metamodel getMetaEntity(String fqcn) {
return metaEntities.get( fqcn );
}
public Collection<MetaEntity> getMetaEntities() {
public Collection<Metamodel> getMetaEntities() {
return metaEntities.values();
}
public void addMetaEntity(String fqcn, MetaEntity metaEntity) {
public void addMetaEntity(String fqcn, Metamodel metaEntity) {
metaEntities.put( fqcn, metaEntity );
}
@ -162,18 +164,30 @@ public final class Context {
return metaEmbeddables.containsKey( fqcn );
}
public @Nullable MetaEntity getMetaEmbeddable(String fqcn) {
public @Nullable Metamodel getMetaEmbeddable(String fqcn) {
return metaEmbeddables.get( fqcn );
}
public void addMetaEmbeddable(String fqcn, MetaEntity metaEntity) {
public void addMetaEmbeddable(String fqcn, Metamodel metaEntity) {
metaEmbeddables.put( fqcn, metaEntity );
}
public Collection<MetaEntity> getMetaEmbeddables() {
public Collection<Metamodel> getMetaEmbeddables() {
return metaEmbeddables.values();
}
public @Nullable Metamodel getMetaAuxiliary(String fqcn) {
return metaAuxiliaries.get( fqcn );
}
public Collection<Metamodel> getMetaAuxiliaries() {
return metaAuxiliaries.values();
}
public void addMetaAuxiliary(String fqcn, Metamodel metamodel) {
metaAuxiliaries.put( fqcn, metamodel);
}
public void addAccessTypeInformation(String fqcn, AccessTypeInformation info) {
accessTypeInformation.put( fqcn, info );
}

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.jpamodelgen;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@ -20,6 +19,7 @@ import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
@ -30,7 +30,8 @@ import javax.lang.model.util.SimpleTypeVisitor6;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.annotation.AnnotationMetaPackage;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils;
@ -132,6 +133,10 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class " + element.toString() );
handleRootElementAnnotationMirrors( element );
}
else if ( hasAuxiliaryAnnotations( element ) ) {
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class " + element.toString() );
handleRootElementAuxiliaryAnnotationMirrors( element );
}
}
createMetaModelClasses();
@ -139,7 +144,17 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
private void createMetaModelClasses() {
for ( MetaEntity entity : context.getMetaEntities() ) {
for ( Metamodel aux : context.getMetaAuxiliaries() ) {
if ( context.isAlreadyGenerated( aux.getQualifiedName() ) ) {
continue;
}
context.logMessage( Diagnostic.Kind.OTHER, "Writing meta model for auxiliary " + aux );
ClassWriter.writeFile( aux, context );
context.markGenerated( aux.getQualifiedName() );
}
for ( Metamodel entity : context.getMetaEntities() ) {
if ( context.isAlreadyGenerated( entity.getQualifiedName() ) ) {
continue;
}
@ -150,11 +165,11 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
// we cannot process the delayed entities in any order. There might be dependencies between them.
// we need to process the top level entities first
Collection<MetaEntity> toProcessEntities = context.getMetaEmbeddables();
Collection<Metamodel> toProcessEntities = context.getMetaEmbeddables();
while ( !toProcessEntities.isEmpty() ) {
Set<MetaEntity> processedEntities = new HashSet<MetaEntity>();
Set<Metamodel> processedEntities = new HashSet<Metamodel>();
int toProcessCountBeforeLoop = toProcessEntities.size();
for ( MetaEntity entity : toProcessEntities ) {
for ( Metamodel entity : toProcessEntities ) {
// see METAGEN-36
if ( context.isAlreadyGenerated( entity.getQualifiedName() ) ) {
processedEntities.add( entity );
@ -179,32 +194,33 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
}
private boolean modelGenerationNeedsToBeDeferred(Collection<MetaEntity> entities, MetaEntity containedEntity) {
ContainsAttributeTypeVisitor visitor = new ContainsAttributeTypeVisitor(
containedEntity.getTypeElement(), context
);
for ( MetaEntity entity : entities ) {
if ( entity.equals( containedEntity ) ) {
continue;
}
for ( Element subElement : ElementFilter.fieldsIn( entity.getTypeElement().getEnclosedElements() ) ) {
TypeMirror mirror = subElement.asType();
if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) {
private boolean modelGenerationNeedsToBeDeferred(Collection<Metamodel> entities, Metamodel containedEntity) {
Element element = containedEntity.getElement();
if ( element instanceof TypeElement ) {
ContainsAttributeTypeVisitor visitor = new ContainsAttributeTypeVisitor( (TypeElement) element, context );
for ( Metamodel entity : entities ) {
if ( entity.equals( containedEntity ) ) {
continue;
}
boolean contains = mirror.accept( visitor, subElement );
if ( contains ) {
return true;
for ( Element subElement : ElementFilter.fieldsIn( entity.getElement().getEnclosedElements() ) ) {
TypeMirror mirror = subElement.asType();
if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) {
continue;
}
boolean contains = mirror.accept( visitor, subElement );
if ( contains ) {
return true;
}
}
}
for ( Element subElement : ElementFilter.methodsIn( entity.getTypeElement().getEnclosedElements() ) ) {
TypeMirror mirror = subElement.asType();
if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) {
continue;
}
boolean contains = mirror.accept( visitor, subElement );
if ( contains ) {
return true;
for ( Element subElement : ElementFilter.methodsIn( entity.getElement().getEnclosedElements() ) ) {
TypeMirror mirror = subElement.asType();
if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) {
continue;
}
boolean contains = mirror.accept( visitor, subElement );
if ( contains ) {
return true;
}
}
}
}
@ -220,6 +236,28 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
);
}
private boolean hasAuxiliaryAnnotations(Element element) {
return TypeUtils.containsAnnotation(
element,
Constants.NAMED_QUERY,
Constants.NAMED_QUERIES,
Constants.NAMED_NATIVE_QUERY,
Constants.NAMED_NATIVE_QUERIES,
Constants.SQL_RESULT_SET_MAPPING,
Constants.SQL_RESULT_SET_MAPPINGS,
Constants.NAMED_ENTITY_GRAPH,
Constants.NAMED_ENTITY_GRAPHS,
Constants.HIB_NAMED_QUERY,
Constants.HIB_NAMED_QUERIES,
Constants.HIB_NAMED_NATIVE_QUERY,
Constants.HIB_NAMED_NATIVE_QUERIES,
Constants.HIB_FETCH_PROFILE,
Constants.HIB_FETCH_PROFILES,
Constants.HIB_FILTER_DEF,
Constants.HIB_FILTER_DEFS
);
}
private void handleRootElementAnnotationMirrors(final Element element) {
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
for ( AnnotationMirror mirror : annotationMirrors ) {
@ -228,7 +266,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
String fqn = ( (TypeElement) element ).getQualifiedName().toString();
MetaEntity alreadyExistingMetaEntity = tryGettingExistingEntityFromContext( mirror, fqn );
Metamodel alreadyExistingMetaEntity = tryGettingExistingEntityFromContext( mirror, fqn );
if ( alreadyExistingMetaEntity != null && alreadyExistingMetaEntity.isMetaComplete() ) {
String msg = "Skipping processing of annotations for " + fqn + " since xml configuration is metadata complete.";
context.logMessage( Diagnostic.Kind.OTHER, msg );
@ -251,8 +289,22 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
}
private @Nullable MetaEntity tryGettingExistingEntityFromContext(AnnotationMirror mirror, String fqn) {
MetaEntity alreadyExistingMetaEntity = null;
private void handleRootElementAuxiliaryAnnotationMirrors(final Element element) {
if ( element instanceof TypeElement ) {
AnnotationMetaEntity metaEntity =
AnnotationMetaEntity.create( (TypeElement) element, context, false );
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
}
else if ( element instanceof PackageElement ) {
AnnotationMetaPackage metaEntity =
AnnotationMetaPackage.create( (PackageElement) element, context );
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
}
//TODO: handle PackageElement
}
private @Nullable Metamodel tryGettingExistingEntityFromContext(AnnotationMirror mirror, String fqn) {
Metamodel alreadyExistingMetaEntity = null;
if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ENTITY )
|| TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS ) ) {
alreadyExistingMetaEntity = context.getMetaEntity( fqn );
@ -305,27 +357,21 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
returnedElement = (TypeElement) collectionElement;
}
if ( type.getQualifiedName().toString().equals( returnedElement.getQualifiedName().toString() ) ) {
return Boolean.TRUE;
}
else {
return Boolean.FALSE;
}
return type.getQualifiedName().toString().equals( returnedElement.getQualifiedName().toString() );
}
@Override
public Boolean visitExecutable(ExecutableType t, Element element) {
if ( !element.getKind().equals( ElementKind.METHOD ) ) {
return Boolean.FALSE;
return false;
}
String string = element.getSimpleName().toString();
if ( !StringUtil.isProperty( string, TypeUtils.toTypeString( t.getReturnType() ) ) ) {
return Boolean.FALSE;
return false;
}
TypeMirror returnType = t.getReturnType();
return returnType.accept( this, element );
return t.getReturnType().accept( this, element );
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.annotation;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils;
import javax.lang.model.element.AnnotationMirror;
import java.util.List;
public abstract class AnnotationMeta implements Metamodel {
void addAuxiliaryMembers() {
addAuxiliaryMembersForAnnotation( Constants.NAMED_QUERY, "QUERY_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.NAMED_QUERIES, "QUERY_" );
addAuxiliaryMembersForAnnotation( Constants.NAMED_NATIVE_QUERY, "QUERY_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.NAMED_NATIVE_QUERIES, "QUERY_" );
addAuxiliaryMembersForAnnotation( Constants.SQL_RESULT_SET_MAPPING, "MAPPING_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.SQL_RESULT_SET_MAPPINGS, "MAPPING_" );
addAuxiliaryMembersForAnnotation( Constants.NAMED_ENTITY_GRAPH, "GRAPH_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.NAMED_ENTITY_GRAPHS, "GRAPH_" );
addAuxiliaryMembersForAnnotation( Constants.HIB_NAMED_QUERY, "QUERY_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.HIB_NAMED_QUERIES, "QUERY_" );
addAuxiliaryMembersForAnnotation( Constants.HIB_NAMED_NATIVE_QUERY, "QUERY_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.HIB_NAMED_NATIVE_QUERIES, "QUERY_" );
addAuxiliaryMembersForAnnotation( Constants.HIB_FETCH_PROFILE, "PROFILE_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.HIB_FETCH_PROFILES, "PROFILE_" );
addAuxiliaryMembersForAnnotation( Constants.HIB_FILTER_DEF, "FILTER_" );
addAuxiliaryMembersForRepeatableAnnotation( Constants.HIB_FILTER_DEFS, "FILTER_" );
}
private void addAuxiliaryMembersForRepeatableAnnotation(String annotationName, String prefix) {
AnnotationMirror mirror = TypeUtils.getAnnotationMirror( getElement(), annotationName );
if ( mirror != null ) {
mirror.getElementValues().forEach((key, value) -> {
if ( key.getSimpleName().contentEquals("value") ) {
List<? extends AnnotationMirror> values =
(List<? extends AnnotationMirror>) value.getValue();
for ( AnnotationMirror annotationMirror : values ) {
addAuxiliaryMembersForMirror( annotationMirror, prefix );
}
}
});
}
}
private void addAuxiliaryMembersForAnnotation(String annotationName, String prefix) {
AnnotationMirror mirror = TypeUtils.getAnnotationMirror( getElement(), annotationName);
if ( mirror != null ) {
addAuxiliaryMembersForMirror( mirror, prefix );
}
}
private void addAuxiliaryMembersForMirror(AnnotationMirror mirror, String prefix) {
mirror.getElementValues().forEach((key, value) -> {
if ( key.getSimpleName().contentEquals("name") ) {
String name = value.getValue().toString();
putMember( prefix + name, new NameMetaAttribute( this, name, prefix ) );
}
});
}
abstract void putMember(String name, NameMetaAttribute nameMetaAttribute);
}

View File

@ -7,14 +7,12 @@
package org.hibernate.jpamodelgen.annotation;
import java.beans.Introspector;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.util.Elements;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.StringUtil;
/**
@ -36,6 +34,11 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
this.type = type;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public String getAttributeDeclarationString() {
return new StringBuilder().append( "public static volatile " )
@ -64,6 +67,7 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
.toString();
}
@Override
public String getPropertyName() {
Elements elementsUtil = parent.getContext().getElementUtils();
if ( element.getKind() == ElementKind.FIELD ) {
@ -84,12 +88,15 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
}
}
public MetaEntity getHostingEntity() {
@Override
public Metamodel getHostingEntity() {
return parent;
}
@Override
public abstract String getMetaType();
@Override
public String getTypeDeclaration() {
return type;
}

View File

@ -24,7 +24,7 @@ import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.model.ImportContext;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants;
@ -38,7 +38,7 @@ import org.hibernate.jpamodelgen.util.TypeUtils;
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public class AnnotationMetaEntity implements MetaEntity {
public class AnnotationMetaEntity extends AnnotationMeta {
private final ImportContext importContext;
private final TypeElement element;
@ -50,12 +50,12 @@ public class AnnotationMetaEntity implements MetaEntity {
/**
* Whether the members of this type have already been initialized or not.
* <p>
* Embeddables and mapped super-classes need to be lazily initialized since the access type may be determined by
* the class which is embedding or sub-classing the entity or super-class. This might not be known until
* Embeddables and mapped superclasses need to be lazily initialized since the access type may be determined by
* the class which is embedding or subclassing the entity or superclass. This might not be known until
* annotations are processed.
* <p>
* Also note, that if two different classes with different access types embed this entity or extend this mapped
* super-class, the access type of the embeddable/super-class will be the one of the last embedding/sub-classing
* super-class, the access type of the embeddable/superclass will be the one of the last embedding/subclassing
* entity processed. The result is not determined (that's ok according to the spec).
*/
private boolean initialized;
@ -65,7 +65,7 @@ public class AnnotationMetaEntity implements MetaEntity {
* lazily is required for embeddedables and mapped supertypes to only pull in those members matching the access
* type as configured via the embedding entity or subclass (also see METAGEN-85).
*/
private MetaEntity entityToMerge;
private Metamodel entityToMerge;
public AnnotationMetaEntity(TypeElement element, Context context) {
this.element = element;
@ -90,14 +90,17 @@ public class AnnotationMetaEntity implements MetaEntity {
return context;
}
@Override
public final String getSimpleName() {
return element.getSimpleName().toString();
}
@Override
public final String getQualifiedName() {
return element.getQualifiedName().toString();
}
@Override
public final String getPackageName() {
return getPackageName( context, element );
}
@ -107,6 +110,7 @@ public class AnnotationMetaEntity implements MetaEntity {
return context.getElementUtils().getName( packageOf.getQualifiedName() ).toString();
}
@Override
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
init();
@ -115,7 +119,7 @@ public class AnnotationMetaEntity implements MetaEntity {
}
}
return new ArrayList<MetaAttribute>( members.values() );
return new ArrayList<>( members.values() );
}
@Override
@ -133,7 +137,7 @@ public class AnnotationMetaEntity implements MetaEntity {
}
}
public void mergeInMembers(MetaEntity other) {
public void mergeInMembers(Metamodel other) {
// store the entity in order do the merge lazily in case of a non-initialized embeddedable or mapped superclass
if ( !initialized ) {
this.entityToMerge = other;
@ -143,22 +147,31 @@ public class AnnotationMetaEntity implements MetaEntity {
}
}
@Override
public final String generateImports() {
return importContext.generateImports();
}
@Override
public final String importType(String fqcn) {
return importContext.importType( fqcn );
}
@Override
public final String staticImport(String fqcn, String member) {
return importContext.staticImport( fqcn, member );
}
public final TypeElement getTypeElement() {
@Override
public final TypeElement getElement() {
return element;
}
@Override
void putMember(String name, NameMetaAttribute nameMetaAttribute) {
members.put( name, nameMetaAttribute );
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@ -169,16 +182,12 @@ public class AnnotationMetaEntity implements MetaEntity {
return sb.toString();
}
protected TypeElement getElement() {
return element;
}
protected final void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
TypeUtils.determineAccessTypeForHierarchy( element, context );
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( getQualifiedName() );
entityAccessTypeInfo = NullnessUtil.castNonNull(accessTypeInfo);
entityAccessTypeInfo = NullnessUtil.castNonNull( accessTypeInfo );
List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
@ -186,16 +195,19 @@ public class AnnotationMetaEntity implements MetaEntity {
List<? extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
List<Element> gettersAndSettersOfClass = new ArrayList<>();
for (Element rawMethodOfClass: methodsOfClass) {
if ( isGetterOrSetter( rawMethodOfClass)) {
gettersAndSettersOfClass.add(rawMethodOfClass);
for ( Element rawMethodOfClass: methodsOfClass ) {
if ( isGetterOrSetter( rawMethodOfClass ) ) {
gettersAndSettersOfClass.add( rawMethodOfClass );
}
}
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
addAuxiliaryMembers();
initialized = true;
}
/**
* Check if method respects Java Bean conventions for getter and setters.
*
@ -246,4 +258,5 @@ public class AnnotationMetaEntity implements MetaEntity {
}
}
}
}

View File

@ -0,0 +1,127 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.annotation;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.model.ImportContext;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import javax.lang.model.element.PackageElement;
import javax.tools.Diagnostic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class used to collect meta information about an annotated package.
*
* @author Gavin King
*/
public class AnnotationMetaPackage extends AnnotationMeta {
private final ImportContext importContext;
private final PackageElement element;
private final Map<String, MetaAttribute> members;
private final Context context;
/**
* Whether the members of this type have already been initialized or not.
*/
private boolean initialized;
public AnnotationMetaPackage(PackageElement element, Context context) {
this.element = element;
this.context = context;
this.members = new HashMap<>();
this.importContext = new ImportContextImpl( getPackageName( context, element ) );
}
public static AnnotationMetaPackage create(PackageElement element, Context context) {
return new AnnotationMetaPackage( element, context );
}
public final Context getContext() {
return context;
}
@Override
public final String getSimpleName() {
return element.getSimpleName().toString();
}
@Override
public final String getQualifiedName() {
return element.getQualifiedName().toString();
}
@Override
public final String getPackageName() {
return getPackageName( context, element );
}
private static String getPackageName(Context context, PackageElement packageOf) {
return context.getElementUtils().getName( packageOf.getQualifiedName() ).toString();
}
@Override
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
init();
}
return new ArrayList<>( members.values() );
}
@Override
public boolean isMetaComplete() {
return false;
}
@Override
public final String generateImports() {
return importContext.generateImports();
}
@Override
public final String importType(String fqcn) {
return importContext.importType( fqcn );
}
@Override
public final String staticImport(String fqcn, String member) {
return importContext.staticImport( fqcn, member );
}
@Override
public final PackageElement getElement() {
return element;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "AnnotationMetaPackage" );
sb.append( "{element=" ).append( element );
sb.append( '}' );
return sb.toString();
}
protected final void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
addAuxiliaryMembers();
initialized = true;
}
@Override
void putMember(String name, NameMetaAttribute nameMetaAttribute) {
members.put( name, nameMetaAttribute );
}
}

View File

@ -0,0 +1,72 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.annotation;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.StringUtil;
/**
* @author Gavin King
*/
class NameMetaAttribute implements MetaAttribute {
private final Metamodel annotationMetaEntity;
private final String name;
private final String prefix;
public NameMetaAttribute(Metamodel annotationMetaEntity, String name, String prefix) {
this.annotationMetaEntity = annotationMetaEntity;
this.name = name;
this.prefix = prefix;
}
@Override
public boolean hasTypedAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
throw new UnsupportedOperationException();
}
@Override
public String getAttributeNameDeclarationString() {
return new StringBuilder()
.append("public static final ")
.append(annotationMetaEntity.importType(String.class.getName()))
.append(" ")
.append(prefix)
.append(StringUtil.nameToFieldName(name))
.append(" = ")
.append("\"")
.append(name)
.append("\"")
.append(";")
.toString();
}
@Override
public String getMetaType() {
throw new UnsupportedOperationException();
}
@Override
public String getPropertyName() {
return name;
}
@Override
public String getTypeDeclaration() {
return "java.lang.String";
}
@Override
public Metamodel getHostingEntity() {
return annotationMetaEntity;
}
}

View File

@ -11,6 +11,8 @@ package org.hibernate.jpamodelgen.model;
*/
public interface MetaAttribute {
boolean hasTypedAttribute();
String getAttributeDeclarationString();
String getAttributeNameDeclarationString();
@ -21,6 +23,6 @@ public interface MetaAttribute {
String getTypeDeclaration();
MetaEntity getHostingEntity();
Metamodel getHostingEntity();
}

View File

@ -7,12 +7,12 @@
package org.hibernate.jpamodelgen.model;
import java.util.List;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
/**
* @author Hardy Ferentschik
*/
public interface MetaEntity extends ImportContext {
public interface Metamodel extends ImportContext {
String getSimpleName();
String getQualifiedName();
@ -27,7 +27,7 @@ public interface MetaEntity extends ImportContext {
String staticImport(String fqcn, String member);
TypeElement getTypeElement();
Element getElement();
boolean isMetaComplete();
}

View File

@ -13,7 +13,7 @@ import java.util.Map;
* @author Hardy Ferentschik
*/
public final class Constants {
// we are trying to to reference jpa annotations directly
// we are trying to reference jpa annotations directly
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";
@ -32,6 +32,24 @@ public final class Constants {
public static final String CONVERT = "jakarta.persistence.Convert";
public static final String HIBERNATE_TYPE = "org.hibernate.annotations.Type";
public static final String NAMED_QUERY = "jakarta.persistence.NamedQuery";
public static final String NAMED_QUERIES = "jakarta.persistence.NamedQueries";
public static final String NAMED_NATIVE_QUERY = "jakarta.persistence.NamedNativeQuery";
public static final String NAMED_NATIVE_QUERIES = "jakarta.persistence.NamedNativeQueries";
public static final String SQL_RESULT_SET_MAPPING = "jakarta.persistence.SqlResultSetMapping";
public static final String SQL_RESULT_SET_MAPPINGS = "jakarta.persistence.SqlResultSetMappings";
public static final String NAMED_ENTITY_GRAPH = "jakarta.persistence.NamedEntityGraph";
public static final String NAMED_ENTITY_GRAPHS = "jakarta.persistence.NamedEntityGraphs";
public static final String HIB_NAMED_QUERY = "org.hibernate.annotations.NamedQuery";
public static final String HIB_NAMED_QUERIES = "org.hibernate.annotations.NamedQueries";
public static final String HIB_NAMED_NATIVE_QUERY = "org.hibernate.annotations.NamedNativeQuery";
public static final String HIB_NAMED_NATIVE_QUERIES = "org.hibernate.annotations.NamedNativeQueries";
public static final String HIB_FETCH_PROFILE = "org.hibernate.annotations.FetchProfile";
public static final String HIB_FETCH_PROFILES = "org.hibernate.annotations.FetchProfiles";
public static final String HIB_FILTER_DEF = "org.hibernate.annotations.FilterDef";
public static final String HIB_FILTER_DEFS = "org.hibernate.annotations.FilterDefs";
public static final Map<String, String> COLLECTIONS = allCollectionTypes();
private static Map<String, String> allCollectionTypes() {

View File

@ -93,6 +93,14 @@ public final class StringUtil {
}
}
public static String nameToFieldName(String name){
return getUpperUnderscoreCaseFromLowerCamelCase(nameToMethodName(name));
}
public static String nameToMethodName(String name) {
return name.replaceAll("[\\s.\\-!@#%=+/*^&|(){}\\[\\]]", "_");
}
public static String getUpperUnderscoreCaseFromLowerCamelCase(String lowerCamelCaseString){
return lowerCamelCaseString.replaceAll("(.)(\\p{Upper})", "$1_$2").toUpperCase();
}

View File

@ -6,11 +6,12 @@
*/
package org.hibernate.jpamodelgen.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
@ -138,7 +139,7 @@ public final class TypeUtils {
assert element != null;
assert annotations != null;
List<String> annotationClassNames = new ArrayList<String>();
Set<String> annotationClassNames = new HashSet<>();
Collections.addAll( annotationClassNames, annotations );
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();

View File

@ -7,12 +7,9 @@
package org.hibernate.jpamodelgen.xml;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
/**
* @author Hardy Ferentschik
*/
@ -28,6 +25,11 @@ public abstract class XmlMetaAttribute implements MetaAttribute {
this.type = type;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public String getAttributeDeclarationString() {
return "public static volatile " + hostingEntity.importType( getMetaType() )
@ -50,15 +52,18 @@ public abstract class XmlMetaAttribute implements MetaAttribute {
.toString();
}
@Override
public String getPropertyName() {
return propertyName;
}
@Override
public String getTypeDeclaration() {
return type;
}
public MetaEntity getHostingEntity() {
@Override
public Metamodel getHostingEntity() {
return hostingEntity;
}

View File

@ -24,7 +24,7 @@ import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.MetaModelGenerationException;
import org.hibernate.jpamodelgen.model.ImportContext;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.util.StringUtil;
@ -52,7 +52,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*
* @author Hardy Ferentschik
*/
public class XmlMetaEntity implements MetaEntity {
public class XmlMetaEntity implements Metamodel {
static final Map<String, String> COLLECTIONS = new HashMap<String, String>();
@ -184,7 +184,7 @@ public class XmlMetaEntity implements MetaEntity {
return importContext.staticImport( fqcn, member );
}
public TypeElement getTypeElement() {
public TypeElement getElement() {
return element;
}

View File

@ -6,7 +6,7 @@
*/
package org.hibernate.jpamodelgen.xml;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.model.Metamodel;
/**
* @author Hardy Ferentschik
@ -22,7 +22,7 @@ public class XmlMetaMap extends XmlMetaCollection {
@Override
public String getAttributeDeclarationString() {
final MetaEntity hostingEntity = getHostingEntity();
final Metamodel hostingEntity = getHostingEntity();
return new StringBuilder().append( "public static volatile " )
.append( hostingEntity.importType( getMetaType() ) )
.append( "<" )

View File

@ -0,0 +1,83 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.test.namedquery;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import org.hibernate.jpamodelgen.test.util.TestUtil;
import org.hibernate.jpamodelgen.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertPresenceOfNameFieldInMetamodelFor;
/**
* @author Gavin King
*/
public class AuxiliaryTest extends CompilationTest {
@Test
@WithClasses({ Book.class, Main.class })
public void testGeneratedAnnotationNotGenerated() {
System.out.println( TestUtil.getMetaModelSourceAsString( Main.class ) );
assertMetamodelClassGeneratedFor( Book.class );
assertMetamodelClassGeneratedFor( Main.class );
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"QUERY_BOOK_BY_TITLE",
"Missing named query attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"QUERY_BOOK_BY_ISBN",
"Missing named query attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"QUERY_BOOK_NATIVE_QUERY",
"Missing named native query attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"MAPPING_BOOK_NATIVE_QUERY_RESULT",
"Missing result set mapping."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"PROFILE_FETCH_ONE",
"Missing fetch profile attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"PROFILE_FETCH_TWO",
"Missing fetch profile attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"PROFILE_DUMMY_FETCH",
"Missing fetch profile attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"MAPPING_RESULT_SET_MAPPING_ONE",
"Missing fetch profile attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"MAPPING_RESULT_SET_MAPPING_TWO",
"Missing fetch profile attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Main.class,
"QUERY__SYSDATE_",
"Missing fetch profile attribute."
);
assertPresenceOfNameFieldInMetamodelFor(
Book.class,
"GRAPH_ENTITY_GRAPH",
"Missing fetch profile attribute."
);
}
}

View File

@ -0,0 +1,13 @@
package org.hibernate.jpamodelgen.test.namedquery;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedEntityGraph;
@Entity
@NamedEntityGraph(name = "entityGraph")
public class Book {
@Id String isbn;
String title;
String text;
}

View File

@ -0,0 +1,21 @@
package org.hibernate.jpamodelgen.test.namedquery;
import jakarta.persistence.NamedNativeQueries;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.SqlResultSetMappings;
import org.hibernate.annotations.FetchProfile;
import org.hibernate.annotations.FetchProfiles;
@NamedQueries(@NamedQuery(name = "bookByIsbn", query = "from Book where isbn = :isbn"))
@NamedQuery(name = "bookByTitle", query = "from Book where title = :title")
@FetchProfile(name = "dummy-fetch")
@FetchProfiles({@FetchProfile(name = "fetch.one"), @FetchProfile(name = "fetch#two")})
@NamedNativeQuery(name = "bookNativeQuery", query = "select * from Book")
@NamedNativeQueries(@NamedNativeQuery(name = "(sysdate)", query = "select sysdate from dual"))
@SqlResultSetMapping(name = "bookNativeQueryResult")
@SqlResultSetMappings({@SqlResultSetMapping(name="result set mapping one"), @SqlResultSetMapping(name = "result_set-mapping-two")})
public class Main {
}

View File

@ -86,6 +86,11 @@ public class TestUtil {
assertTrue( buildErrorString( errorString, clazz ), hasFieldInMetamodelFor( clazz, fieldName ) );
}
public static void assertPresenceOfNameFieldInMetamodelFor(Class<?> clazz, String fieldName, String errorString) {
assertTrue( buildErrorString( errorString, clazz ), hasFieldInMetamodelFor( clazz, fieldName ) );
assertEquals(buildErrorString(errorString, clazz), getFieldFromMetamodelFor(clazz, fieldName).getType(), String.class);
}
public static void assertAttributeTypeInMetaModelFor(Class<?> clazz, String fieldName, Class<?> expectedType, String errorString) {
Field field = getFieldFromMetamodelFor( clazz, fieldName );
assertNotNull( "Cannot find field '" + fieldName + "' in " + clazz.getName(), field );