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.Recognizer;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
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.AnnotationValue;
import javax.lang.model.element.Element;
import java.util.List;
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
// query result in compilation errors. Otherwise, they only
// 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() )
);
if ( statement instanceof SqmSelectStatement
@ -158,12 +161,29 @@ public abstract class AnnotationMeta implements Metamodel {
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 checkHql;
public WarningErrorHandler(AnnotationMirror mirror, AnnotationValue value, String hql, boolean reportErrors, boolean checkHql) {
super( AnnotationMeta.this.getElement(), mirror, value, hql, AnnotationMeta.this.getContext() );
private WarningErrorHandler(
Context context,
Element element,
AnnotationMirror mirror,
AnnotationValue value, String hql,
boolean reportErrors,
boolean checkHql) {
super(context, element, mirror, value, hql);
this.reportErrors = reportErrors;
this.checkHql = checkHql;
}
@ -188,7 +208,9 @@ public abstract class AnnotationMeta implements Metamodel {
}
@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) {
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).
* 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 Hardy Ferentschik
@ -74,19 +78,19 @@ public class AnnotationMetaEntity extends AnnotationMeta {
/**
* Whether the members of this type have already been initialized or not.
* <p>
* 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
* 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/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).
*/
private boolean initialized;
/**
* 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).
*/
private Metamodel entityToMerge;
@ -95,6 +99,10 @@ public class AnnotationMetaEntity extends AnnotationMeta {
* True if this "metamodel class" is actually an instantiable DAO-style repository.
*/
private boolean dao = false;
/**
* The type of the "session getter" method of a DAO-style repository.
*/
private String sessionType = Constants.ENTITY_MANAGER;
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> gettersAndSettersOfClass = new ArrayList<>();
final List<ExecutableElement> queryMethods = new ArrayList<>();
for ( ExecutableElement rawMethodOfClass: methodsOfClass ) {
if ( isGetterOrSetter( rawMethodOfClass ) ) {
gettersAndSettersOfClass.add( rawMethodOfClass );
for ( ExecutableElement method: methodsOfClass ) {
if ( isGetterOrSetter( method ) ) {
gettersAndSettersOfClass.add( method );
}
else if ( containsAnnotation( rawMethodOfClass, Constants.HQL, Constants.SQL, Constants.FIND ) ) {
queryMethods.add( rawMethodOfClass );
else if ( containsAnnotation( method, Constants.HQL, Constants.SQL, Constants.FIND ) ) {
queryMethods.add( method );
}
}
if ( !containsAnnotation( element, Constants.ENTITY ) ) {
for ( ExecutableElement getterOrSetter : gettersAndSettersOfClass ) {
if ( isSessionGetter( getterOrSetter ) ) {
dao = true;
sessionType = addDaoConstructor( getterOrSetter );
}
}
}
findSessionGetter( methodsOfClass );
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
@ -265,6 +266,22 @@ public class AnnotationMetaEntity extends AnnotationMeta {
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) {
String name = getterOrSetter.getSimpleName().toString();
String typeName = element.getSimpleName().toString() + '_';
@ -274,16 +291,23 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return sessionType;
}
private static boolean isSessionGetter(ExecutableElement getterOrSetter) {
final TypeMirror type = getterOrSetter.getReturnType();
if ( type.getKind() == TypeKind.DECLARED ) {
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);
/**
* The session getter method doesn't have to be a JavaBeans-style
* getter. It can be any method with no parameters and one of the
* needed return types.
*/
private static boolean isSessionGetter(ExecutableElement method) {
if ( method.getParameters().isEmpty() ) {
final TypeMirror type = method.getReturnType();
if ( type.getKind() == TypeKind.DECLARED ) {
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;
@ -684,7 +708,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
hql,
false, true,
emptySet(), emptySet(),
new ErrorHandler( method, mirror, value, hql, context ),
new ErrorHandler(context, method, mirror, value, hql),
ProcessorSessionFactory.create( context.getProcessingEnvironment() )
);
}

View File

@ -23,6 +23,9 @@ import java.util.BitSet;
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
*/
class ErrorHandler implements Validation.Handler {
@ -33,11 +36,11 @@ class ErrorHandler implements Validation.Handler {
private final Context context;
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.mirror = mirror;
this.value = value;
this.queryString = queryString;
this.queryString = hql;
this.context = context;
}