HHH-17772 initial, rudimentary support for Jakarta Data annotations
This commit is contained in:
parent
0163fceed9
commit
5be9463364
|
@ -41,13 +41,10 @@ import static javax.lang.model.util.ElementFilter.fieldsIn;
|
|||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.FIND;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.HQL;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_REPOSITORY;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.SQL;
|
||||
import static org.hibernate.jpamodelgen.util.StringUtil.isProperty;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.getCollectionElementType;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.isAnnotationMirrorOfType;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.isClassOrRecordType;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.toTypeString;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.*;
|
||||
|
||||
/**
|
||||
* Main annotation processor.
|
||||
|
@ -70,7 +67,9 @@ import static org.hibernate.jpamodelgen.util.TypeUtils.toTypeString;
|
|||
Constants.HIB_FETCH_PROFILE,
|
||||
Constants.HIB_FILTER_DEF,
|
||||
Constants.HIB_NAMED_QUERY,
|
||||
Constants.HIB_NAMED_NATIVE_QUERY
|
||||
Constants.HIB_NAMED_NATIVE_QUERY,
|
||||
// do not need to list other Jakarta Data annotations here
|
||||
Constants.JD_REPOSITORY
|
||||
})
|
||||
@SupportedOptions({
|
||||
JPAMetaModelEntityProcessor.DEBUG_OPTION,
|
||||
|
@ -246,8 +245,20 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
|||
}
|
||||
else if ( element instanceof TypeElement ) {
|
||||
final TypeElement typeElement = (TypeElement) element;
|
||||
final AnnotationMirror repository = getAnnotationMirror( element, JD_REPOSITORY );
|
||||
if ( repository != null ) {
|
||||
final String provider = (String) getAnnotationValue( repository, "provider" );
|
||||
if ( provider == null || provider.isEmpty()
|
||||
|| provider.equalsIgnoreCase("hibernate") ) {
|
||||
context.logMessage( Diagnostic.Kind.OTHER, "Processing repository class '" + element + "'" );
|
||||
final AnnotationMetaEntity metaEntity =
|
||||
AnnotationMetaEntity.create( typeElement, context, false, false );
|
||||
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( Element member : typeElement.getEnclosedElements() ) {
|
||||
if ( containsAnnotation( member, HQL, SQL, FIND ) ) {
|
||||
if ( hasAnnotation( member, HQL, SQL, FIND ) ) {
|
||||
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" );
|
||||
final AnnotationMetaEntity metaEntity =
|
||||
AnnotationMetaEntity.create( typeElement, context, false, false );
|
||||
|
@ -257,6 +268,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( ProcessLaterException processLaterException ) {
|
||||
if ( element instanceof TypeElement ) {
|
||||
context.logMessage(
|
||||
|
|
|
@ -32,6 +32,7 @@ import javax.lang.model.util.Types;
|
|||
import javax.tools.Diagnostic;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.jpamodelgen.Context;
|
||||
import org.hibernate.jpamodelgen.ImportContextImpl;
|
||||
import org.hibernate.jpamodelgen.ProcessLaterException;
|
||||
|
@ -64,10 +65,19 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
|
|||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam;
|
||||
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.FIND;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.HIB_SESSION;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.HIB_STATELESS_SESSION;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.HQL;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_DELETE;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_FIND;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_INSERT;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_QUERY;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_REPOSITORY;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.JD_UPDATE;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.MUTINY_SESSION;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.SESSION_TYPES;
|
||||
import static org.hibernate.jpamodelgen.util.Constants.SQL;
|
||||
import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAccessTypeForHierarchy;
|
||||
|
@ -75,6 +85,7 @@ import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecif
|
|||
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValue;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValueRef;
|
||||
import static org.hibernate.jpamodelgen.util.TypeUtils.hasAnnotation;
|
||||
|
||||
/**
|
||||
* Class used to collect meta information about an annotated type (entity, embeddable or mapped superclass).
|
||||
|
@ -284,16 +295,25 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
final List<ExecutableElement> methodsOfClass = methodsIn( element.getEnclosedElements() );
|
||||
final List<ExecutableElement> gettersAndSettersOfClass = new ArrayList<>();
|
||||
final List<ExecutableElement> queryMethods = new ArrayList<>();
|
||||
final List<ExecutableElement> lifecycleMethods = new ArrayList<>();
|
||||
for ( ExecutableElement method: methodsOfClass ) {
|
||||
if ( isGetterOrSetter( method ) ) {
|
||||
gettersAndSettersOfClass.add( method );
|
||||
}
|
||||
else if ( containsAnnotation( method, Constants.HQL, Constants.SQL, Constants.FIND ) ) {
|
||||
else if ( containsAnnotation( method, HQL, SQL, JD_QUERY, FIND, JD_FIND ) ) {
|
||||
queryMethods.add( method );
|
||||
}
|
||||
else if ( containsAnnotation( method, JD_INSERT, JD_UPDATE, JD_DELETE ) ) {
|
||||
lifecycleMethods.add( method );
|
||||
}
|
||||
}
|
||||
|
||||
findSessionGetter( element );
|
||||
if ( !dao && hasAnnotation( element, JD_REPOSITORY ) ) {
|
||||
dao = true;
|
||||
sessionType = HIB_STATELESS_SESSION;
|
||||
addDaoConstructor( null );
|
||||
}
|
||||
|
||||
if ( managed ) {
|
||||
putMember( "class", new AnnotationMetaType(this) );
|
||||
|
@ -306,15 +326,17 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
|
||||
checkNamedQueries();
|
||||
|
||||
addLifecycleMethods( lifecycleMethods );
|
||||
|
||||
addQueryMethods( queryMethods );
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void findSessionGetter(TypeElement type) {
|
||||
if ( !containsAnnotation( type, Constants.ENTITY )
|
||||
&& !containsAnnotation( type, Constants.MAPPED_SUPERCLASS )
|
||||
&& !containsAnnotation( type, Constants.EMBEDDABLE ) ) {
|
||||
if ( !hasAnnotation( type, Constants.ENTITY )
|
||||
&& !hasAnnotation( type, Constants.MAPPED_SUPERCLASS )
|
||||
&& !hasAnnotation( type, Constants.EMBEDDABLE ) ) {
|
||||
for ( ExecutableElement method : methodsIn( type.getEnclosedElements() ) ) {
|
||||
if ( isSessionGetter( method ) ) {
|
||||
dao = true;
|
||||
|
@ -342,19 +364,21 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
* variable backing it, together with a constructor that initializes
|
||||
* it.
|
||||
*/
|
||||
private String addDaoConstructor(ExecutableElement method) {
|
||||
final String name = method.getSimpleName().toString();
|
||||
private String addDaoConstructor(@Nullable ExecutableElement method) {
|
||||
final String sessionType = method == null ? this.sessionType : method.getReturnType().toString();
|
||||
final String sessionVariableName = getSessionVariableName( sessionType );
|
||||
final String name = method == null ? sessionVariableName : method.getSimpleName().toString();
|
||||
final String typeName = element.getSimpleName().toString() + '_';
|
||||
final String sessionType = method.getReturnType().toString();
|
||||
putMember( name,
|
||||
new DaoConstructor(
|
||||
this,
|
||||
typeName,
|
||||
name,
|
||||
sessionType,
|
||||
getSessionVariableName(sessionType),
|
||||
sessionVariableName,
|
||||
context.addInjectAnnotation(),
|
||||
context.addNonnullAnnotation()
|
||||
context.addNonnullAnnotation(),
|
||||
method != null
|
||||
)
|
||||
);
|
||||
return sessionType;
|
||||
|
@ -445,6 +469,16 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
&& isSessionGetter( (ExecutableElement) memberOfClass ) );
|
||||
}
|
||||
|
||||
private void addLifecycleMethods(List<ExecutableElement> queryMethods) {
|
||||
for ( ExecutableElement method : queryMethods) {
|
||||
if ( method.getModifiers().contains(Modifier.ABSTRACT) ) {
|
||||
if ( hasAnnotation( method, JD_INSERT, JD_UPDATE, JD_DELETE ) ) {
|
||||
addLifecycleMethod( method );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addQueryMethods(List<ExecutableElement> queryMethods) {
|
||||
for ( ExecutableElement method : queryMethods) {
|
||||
if ( method.getModifiers().contains(Modifier.ABSTRACT) ) {
|
||||
|
@ -521,20 +555,91 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
ExecutableElement method,
|
||||
@Nullable TypeMirror returnType,
|
||||
@Nullable TypeElement containerType) {
|
||||
final AnnotationMirror hql = getAnnotationMirror( method, Constants.HQL );
|
||||
final AnnotationMirror hql = getAnnotationMirror( method, HQL );
|
||||
if ( hql != null ) {
|
||||
addQueryMethod( method, returnType, containerType, hql, false );
|
||||
}
|
||||
final AnnotationMirror sql = getAnnotationMirror( method, Constants.SQL );
|
||||
final AnnotationMirror sql = getAnnotationMirror( method, SQL );
|
||||
if ( sql != null ) {
|
||||
addQueryMethod( method, returnType, containerType, sql, true );
|
||||
}
|
||||
final AnnotationMirror find = getAnnotationMirror( method, Constants.FIND );
|
||||
if ( find != null ) {
|
||||
final AnnotationMirror jdql = getAnnotationMirror( method, JD_QUERY );
|
||||
if ( jdql != null ) {
|
||||
addQueryMethod( method, returnType, containerType, jdql, false );
|
||||
}
|
||||
if ( hasAnnotation( method, FIND, JD_FIND ) ) {
|
||||
addFinderMethod( method, returnType, containerType );
|
||||
}
|
||||
}
|
||||
|
||||
private void addLifecycleMethod(ExecutableElement method) {
|
||||
final TypeMirror returnType = method.getReturnType();
|
||||
if ( !HIB_STATELESS_SESSION.equals(sessionType) ) {
|
||||
context.message( method,
|
||||
"repository must be backed by a 'StatelessSession'",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
else if ( method.getParameters().size() != 1 ) {
|
||||
context.message( method,
|
||||
"must have exactly one parameter",
|
||||
Diagnostic.Kind.ERROR );
|
||||
|
||||
}
|
||||
else if ( returnType == null || returnType.getKind() != TypeKind.VOID ) {
|
||||
context.message( method,
|
||||
"must be declared 'void'",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
else {
|
||||
final String operation = lifecycleOperation( method );
|
||||
final VariableElement parameter = method.getParameters().get(0);
|
||||
final TypeMirror parameterType = parameter.asType();
|
||||
if ( parameterType.getKind() != TypeKind.DECLARED ) {
|
||||
context.message( parameter,
|
||||
"incorrect parameter type '" + parameterType + "' is not an entity type",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
else {
|
||||
final DeclaredType declaredType = (DeclaredType) parameterType;
|
||||
if ( !containsAnnotation( declaredType.asElement(), Constants.ENTITY ) ) {
|
||||
context.message( parameter,
|
||||
"incorrect parameter type '" + parameterType + "' is not annotated '@Entity'",
|
||||
Diagnostic.Kind.ERROR );
|
||||
}
|
||||
else {
|
||||
putMember(
|
||||
method.getSimpleName().toString()
|
||||
+ '.' + operation,
|
||||
new LifecycleMethod(
|
||||
this,
|
||||
parameterType.toString(),
|
||||
method.getSimpleName().toString(),
|
||||
parameter.getSimpleName().toString(),
|
||||
getSessionVariableName(),
|
||||
operation,
|
||||
context.addNonnullAnnotation()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String lifecycleOperation(ExecutableElement method) {
|
||||
if ( hasAnnotation(method, JD_INSERT ) ) {
|
||||
return "insert";
|
||||
}
|
||||
else if ( hasAnnotation(method, JD_UPDATE ) ) {
|
||||
return "update";
|
||||
}
|
||||
else if ( hasAnnotation(method, JD_DELETE ) ) {
|
||||
return "delete";
|
||||
}
|
||||
else {
|
||||
throw new AssertionFailure("Unrecognized lifecycle operation");
|
||||
}
|
||||
}
|
||||
|
||||
private void addFinderMethod(
|
||||
ExecutableElement method,
|
||||
@Nullable TypeMirror returnType,
|
||||
|
@ -700,9 +805,13 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
}
|
||||
|
||||
private static List<String> enabledFetchProfiles(ExecutableElement method) {
|
||||
final AnnotationMirror findAnnotation = getAnnotationMirror( method, FIND );
|
||||
if ( findAnnotation == null ) {
|
||||
return emptyList();
|
||||
}
|
||||
else {
|
||||
final Object enabledFetchProfiles =
|
||||
getAnnotationValue( castNonNull( getAnnotationMirror( method, Constants.FIND ) ),
|
||||
"enabledFetchProfiles" );
|
||||
getAnnotationValue( findAnnotation, "enabledFetchProfiles" );
|
||||
if ( enabledFetchProfiles == null ) {
|
||||
return emptyList();
|
||||
}
|
||||
|
@ -716,6 +825,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createMultipleParameterFinder(ExecutableElement method, TypeMirror returnType, TypeElement entity) {
|
||||
final String methodName = method.getSimpleName().toString();
|
||||
|
|
|
@ -21,6 +21,7 @@ public class DaoConstructor implements MetaAttribute {
|
|||
private final String sessionVariableName;
|
||||
private final boolean addInjectAnnotation;
|
||||
private final boolean addNonnullAnnotation;
|
||||
private final boolean addOverrideAnnotation;
|
||||
|
||||
public DaoConstructor(
|
||||
Metamodel annotationMetaEntity,
|
||||
|
@ -29,7 +30,8 @@ public class DaoConstructor implements MetaAttribute {
|
|||
String sessionTypeName,
|
||||
String sessionVariableName,
|
||||
boolean addInjectAnnotation,
|
||||
boolean addNonnullAnnotation) {
|
||||
boolean addNonnullAnnotation,
|
||||
boolean addOverrideAnnotation) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.constructorName = constructorName;
|
||||
this.methodName = methodName;
|
||||
|
@ -37,6 +39,7 @@ public class DaoConstructor implements MetaAttribute {
|
|||
this.sessionVariableName = sessionVariableName;
|
||||
this.addInjectAnnotation = addInjectAnnotation;
|
||||
this.addNonnullAnnotation = addNonnullAnnotation;
|
||||
this.addOverrideAnnotation = addOverrideAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,7 +81,11 @@ public class DaoConstructor implements MetaAttribute {
|
|||
.append(sessionVariableName)
|
||||
.append(";")
|
||||
.append("\n}")
|
||||
.append("\n\n")
|
||||
.append("\n\n");
|
||||
if (addOverrideAnnotation) {
|
||||
declaration.append("@Override\n");
|
||||
}
|
||||
declaration
|
||||
.append("public ");
|
||||
notNull( declaration );
|
||||
declaration
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public class LifecycleMethod implements MetaAttribute {
|
||||
private final AnnotationMetaEntity annotationMetaEntity;
|
||||
private final String entity;
|
||||
private final String methodName;
|
||||
private final String parameterName;
|
||||
private final String sessionName;
|
||||
private final String operationName;
|
||||
private final boolean addNonnullAnnotation;
|
||||
|
||||
public LifecycleMethod(
|
||||
AnnotationMetaEntity annotationMetaEntity,
|
||||
String entity,
|
||||
String methodName,
|
||||
String parameterName,
|
||||
String sessionName,
|
||||
String operationName,
|
||||
boolean addNonnullAnnotation) {
|
||||
this.annotationMetaEntity = annotationMetaEntity;
|
||||
this.entity = entity;
|
||||
this.methodName = methodName;
|
||||
this.parameterName = parameterName;
|
||||
this.sessionName = sessionName;
|
||||
this.operationName = operationName;
|
||||
this.addNonnullAnnotation = addNonnullAnnotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypedAttribute() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStringAttribute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeDeclarationString() {
|
||||
StringBuilder declaration = new StringBuilder()
|
||||
.append("\n@Override\npublic void ")
|
||||
.append(methodName)
|
||||
.append('(');
|
||||
notNull( declaration );
|
||||
declaration
|
||||
.append(annotationMetaEntity.importType(entity))
|
||||
.append(' ')
|
||||
.append(parameterName)
|
||||
.append(')')
|
||||
.append(" {\n")
|
||||
.append("\t")
|
||||
.append(sessionName)
|
||||
.append('.')
|
||||
.append(operationName)
|
||||
.append('(')
|
||||
.append(parameterName)
|
||||
.append(')')
|
||||
.append(";\n}");
|
||||
return declaration.toString();
|
||||
}
|
||||
|
||||
private void notNull(StringBuilder declaration) {
|
||||
if ( addNonnullAnnotation ) {
|
||||
declaration
|
||||
.append('@')
|
||||
.append(annotationMetaEntity.importType("jakarta.annotation.Nonnull"))
|
||||
.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributeNameDeclarationString() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMetaType() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeDeclaration() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getHostingEntity() {
|
||||
return annotationMetaEntity;
|
||||
}
|
||||
}
|
|
@ -61,6 +61,13 @@ public final class Constants {
|
|||
public static final String SQL = "org.hibernate.annotations.processing.SQL";
|
||||
public static final String FIND = "org.hibernate.annotations.processing.Find";
|
||||
|
||||
public static final String JD_REPOSITORY = "jakarta.data.repository.Repository";
|
||||
public static final String JD_QUERY = "jakarta.data.repository.Query";
|
||||
public static final String JD_FIND = "jakarta.data.repository.Find";
|
||||
public static final String JD_INSERT = "jakarta.data.repository.Insert";
|
||||
public static final String JD_UPDATE = "jakarta.data.repository.Update";
|
||||
public static final String JD_DELETE = "jakarta.data.repository.Delete";
|
||||
|
||||
public static final String CHECK_HQL = "org.hibernate.annotations.processing.CheckHQL";
|
||||
|
||||
public static final String ENTITY_MANAGER = "jakarta.persistence.EntityManager";
|
||||
|
|
|
@ -231,6 +231,15 @@ public final class TypeUtils {
|
|||
return getAnnotationMirror( element, qualifiedName ) != null;
|
||||
}
|
||||
|
||||
public static boolean hasAnnotation(Element element, String... qualifiedNames) {
|
||||
for ( String qualifiedName : qualifiedNames ) {
|
||||
if ( hasAnnotation( element, qualifiedName ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static @Nullable Object getAnnotationValue(AnnotationMirror annotationMirror, String parameterValue) {
|
||||
assert annotationMirror != null;
|
||||
assert parameterValue != null;
|
||||
|
|
Loading…
Reference in New Issue