HHH-16633 allow the "session getter" method to not be a getter

add some Javadoc
This commit is contained in:
Gavin King 2023-07-10 12:02:41 +02:00
parent 99d8bf0832
commit 78843fb2a9
3 changed files with 84 additions and 35 deletions

View File

@ -8,6 +8,7 @@ package org.hibernate.jpamodelgen.annotation;
import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Recognizer;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.model.MetaAttribute; import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel; import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants; import org.hibernate.jpamodelgen.util.Constants;
@ -18,6 +19,7 @@ import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import java.util.List; import java.util.List;
import static java.util.Collections.emptySet; import static java.util.Collections.emptySet;
@ -94,7 +96,8 @@ public abstract class AnnotationMeta implements Metamodel {
// If we are in the scope of @CheckHQL, semantic errors in the // If we are in the scope of @CheckHQL, semantic errors in the
// query result in compilation errors. Otherwise, they only // query result in compilation errors. Otherwise, they only
// result in warnings, so we don't break working code. // result in warnings, so we don't break working code.
new WarningErrorHandler( mirror, value, hql, reportErrors, checkHql ), new WarningErrorHandler( getContext(), getElement(), mirror, value, hql,
reportErrors, checkHql ),
ProcessorSessionFactory.create( getContext().getProcessingEnvironment() ) ProcessorSessionFactory.create( getContext().getProcessingEnvironment() )
); );
if ( statement instanceof SqmSelectStatement if ( statement instanceof SqmSelectStatement
@ -158,12 +161,29 @@ public abstract class AnnotationMeta implements Metamodel {
abstract void putMember(String name, MetaAttribute nameMetaAttribute); abstract void putMember(String name, MetaAttribute nameMetaAttribute);
private class WarningErrorHandler extends ErrorHandler { /**
* Adjusts the severity of validation errors that occur type-checking
* a named HQL query, depending on whether we are within the scope
* of a {@link org.hibernate.annotations.processing.CheckHQL} annotation.
* <p>
* We always type-check; but when there's no {@code CheckHQL} in scope,
* we report problems as warnings only. This is necessary, since we don't
* yet accept absolutely everything that is legal: we don't know about
* XML, we don't handle converters, and we don't handle {@code enum}
* types very well.
*/
private static class WarningErrorHandler extends ErrorHandler {
private final boolean reportErrors; private final boolean reportErrors;
private final boolean checkHql; private final boolean checkHql;
public WarningErrorHandler(AnnotationMirror mirror, AnnotationValue value, String hql, boolean reportErrors, boolean checkHql) { private WarningErrorHandler(
super( AnnotationMeta.this.getElement(), mirror, value, hql, AnnotationMeta.this.getContext() ); Context context,
Element element,
AnnotationMirror mirror,
AnnotationValue value, String hql,
boolean reportErrors,
boolean checkHql) {
super(context, element, mirror, value, hql);
this.reportErrors = reportErrors; this.reportErrors = reportErrors;
this.checkHql = checkHql; this.checkHql = checkHql;
} }
@ -188,7 +208,9 @@ public abstract class AnnotationMeta implements Metamodel {
} }
@Override @Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String message, RecognitionException e) { public void syntaxError(
Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
String message, RecognitionException e) {
if (reportErrors) { if (reportErrors) {
super.syntaxError( recognizer, offendingSymbol, line, charPositionInLine, message, e ); super.syntaxError( recognizer, offendingSymbol, line, charPositionInLine, message, e );
} }

View File

@ -56,6 +56,10 @@ import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValueRef;
/** /**
* Class used to collect meta information about an annotated type (entity, embeddable or mapped superclass). * Class used to collect meta information about an annotated type (entity, embeddable or mapped superclass).
* Also repurposed for any type with "auxiliary" annotations like {@code @NamedQuery}, {@code @FetchProfile},
* {@code @Find}, or {@code @HQL}. We do not distinguish these two kinds of thing, since an entity class may
* {@code @NamedQuery} or {@code @FetchProfile} annotations. Entities may not, however, have methods annotated
* {@code @Find} or {@code @HQL}, since entity classes are usually concrete classes.
* *
* @author Max Andersen * @author Max Andersen
* @author Hardy Ferentschik * @author Hardy Ferentschik
@ -74,19 +78,19 @@ public class AnnotationMetaEntity extends AnnotationMeta {
/** /**
* Whether the members of this type have already been initialized or not. * Whether the members of this type have already been initialized or not.
* <p> * <p>
* Embeddables and mapped superclasses need to be lazily initialized since the access type may be determined by * Embeddables and mapped superclasses need to be lazily initialized since the access type may be determined
* the class which is embedding or subclassing the entity or superclass. This might not be known until * by the class which is embedding or subclassing the entity or superclass. This might not be known until
* annotations are processed. * annotations are processed.
* <p> * <p>
* Also note, that if two different classes with different access types embed this entity or extend this mapped * 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/superclass will be the one of the last embedding/subclassing * superclass, 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). * entity processed. The result is not determined (that's ok according to the spec).
*/ */
private boolean initialized; private boolean initialized;
/** /**
* Another meta entity for the same type which should be merged lazily with this meta entity. Doing the merge * Another meta entity for the same type which should be merged lazily with this meta entity. Doing the merge
* lazily is required for embeddedables and mapped supertypes to only pull in those members matching the access * lazily is required for embeddables 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). * type as configured via the embedding entity or subclass (also see METAGEN-85).
*/ */
private Metamodel entityToMerge; private Metamodel entityToMerge;
@ -95,6 +99,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
* True if this "metamodel class" is actually an instantiable DAO-style repository. * True if this "metamodel class" is actually an instantiable DAO-style repository.
*/ */
private boolean dao = false; private boolean dao = false;
/**
* The type of the "session getter" method of a DAO-style repository.
*/
private String sessionType = Constants.ENTITY_MANAGER; private String sessionType = Constants.ENTITY_MANAGER;
public AnnotationMetaEntity(TypeElement element, Context context) { public AnnotationMetaEntity(TypeElement element, Context context) {
@ -235,23 +243,16 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<ExecutableElement> methodsOfClass = methodsIn( element.getEnclosedElements() ); final List<ExecutableElement> methodsOfClass = methodsIn( element.getEnclosedElements() );
final List<ExecutableElement> gettersAndSettersOfClass = new ArrayList<>(); final List<ExecutableElement> gettersAndSettersOfClass = new ArrayList<>();
final List<ExecutableElement> queryMethods = new ArrayList<>(); final List<ExecutableElement> queryMethods = new ArrayList<>();
for ( ExecutableElement rawMethodOfClass: methodsOfClass ) { for ( ExecutableElement method: methodsOfClass ) {
if ( isGetterOrSetter( rawMethodOfClass ) ) { if ( isGetterOrSetter( method ) ) {
gettersAndSettersOfClass.add( rawMethodOfClass ); gettersAndSettersOfClass.add( method );
} }
else if ( containsAnnotation( rawMethodOfClass, Constants.HQL, Constants.SQL, Constants.FIND ) ) { else if ( containsAnnotation( method, Constants.HQL, Constants.SQL, Constants.FIND ) ) {
queryMethods.add( rawMethodOfClass ); queryMethods.add( method );
} }
} }
if ( !containsAnnotation( element, Constants.ENTITY ) ) { findSessionGetter( methodsOfClass );
for ( ExecutableElement getterOrSetter : gettersAndSettersOfClass ) {
if ( isSessionGetter( getterOrSetter ) ) {
dao = true;
sessionType = addDaoConstructor( getterOrSetter );
}
}
}
addPersistentMembers( fieldsOfClass, AccessType.FIELD ); addPersistentMembers( fieldsOfClass, AccessType.FIELD );
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY ); addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
@ -265,6 +266,22 @@ public class AnnotationMetaEntity extends AnnotationMeta {
initialized = true; initialized = true;
} }
private void findSessionGetter(List<ExecutableElement> methodsOfClass) {
if ( !containsAnnotation( element, Constants.ENTITY ) ) {
for ( ExecutableElement method : methodsOfClass ) {
if ( isSessionGetter( method ) ) {
dao = true;
sessionType = addDaoConstructor( method );
}
}
}
}
/**
* If there is a session getter method, we generate an instance
* variable backing it, together with a constructor that initializes
* it.
*/
private String addDaoConstructor(ExecutableElement getterOrSetter) { private String addDaoConstructor(ExecutableElement getterOrSetter) {
String name = getterOrSetter.getSimpleName().toString(); String name = getterOrSetter.getSimpleName().toString();
String typeName = element.getSimpleName().toString() + '_'; String typeName = element.getSimpleName().toString() + '_';
@ -274,16 +291,23 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return sessionType; return sessionType;
} }
private static boolean isSessionGetter(ExecutableElement getterOrSetter) { /**
final TypeMirror type = getterOrSetter.getReturnType(); * The session getter method doesn't have to be a JavaBeans-style
if ( type.getKind() == TypeKind.DECLARED ) { * getter. It can be any method with no parameters and one of the
final DeclaredType declaredType = (DeclaredType) type; * needed return types.
final Element element = declaredType.asElement(); */
if ( element.getKind() == ElementKind.INTERFACE ) { private static boolean isSessionGetter(ExecutableElement method) {
final Name name = ((TypeElement) element).getQualifiedName(); if ( method.getParameters().isEmpty() ) {
return name.contentEquals(Constants.HIB_SESSION) final TypeMirror type = method.getReturnType();
|| name.contentEquals(Constants.HIB_STATELESS_SESSION) if ( type.getKind() == TypeKind.DECLARED ) {
|| name.contentEquals(Constants.ENTITY_MANAGER); final DeclaredType declaredType = (DeclaredType) type;
final Element element = declaredType.asElement();
if ( element.getKind() == ElementKind.INTERFACE ) {
final Name name = ((TypeElement) element).getQualifiedName();
return name.contentEquals(Constants.HIB_SESSION)
|| name.contentEquals(Constants.HIB_STATELESS_SESSION)
|| name.contentEquals(Constants.ENTITY_MANAGER);
}
} }
} }
return false; return false;
@ -684,7 +708,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
hql, hql,
false, true, false, true,
emptySet(), emptySet(), emptySet(), emptySet(),
new ErrorHandler( method, mirror, value, hql, context ), new ErrorHandler(context, method, mirror, value, hql),
ProcessorSessionFactory.create( context.getProcessingEnvironment() ) ProcessorSessionFactory.create( context.getProcessingEnvironment() )
); );
} }

View File

@ -23,6 +23,9 @@ import java.util.BitSet;
import static org.hibernate.query.hql.internal.StandardHqlTranslator.prettifyAntlrError; import static org.hibernate.query.hql.internal.StandardHqlTranslator.prettifyAntlrError;
/** /**
* Responsible for forwarding errors from the HQL typechecker to the Java compiler,
* via {@link Context#message}.
*
* @author Gavin King * @author Gavin King
*/ */
class ErrorHandler implements Validation.Handler { class ErrorHandler implements Validation.Handler {
@ -33,11 +36,11 @@ class ErrorHandler implements Validation.Handler {
private final Context context; private final Context context;
private int errorCount; private int errorCount;
public ErrorHandler(Element element, AnnotationMirror mirror, AnnotationValue value, String queryString, Context context) { public ErrorHandler(Context context, Element element, AnnotationMirror mirror, AnnotationValue value, String hql) {
this.element = element; this.element = element;
this.mirror = mirror; this.mirror = mirror;
this.value = value; this.value = value;
this.queryString = queryString; this.queryString = hql;
this.context = context; this.context = context;
} }