diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/ClassWriter.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/ClassWriter.java index 22c514d78db4..56987d856151 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/ClassWriter.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/ClassWriter.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Set; +import static org.hibernate.processor.util.Constants.SPRING_COMPONENT; import static org.hibernate.processor.util.TypeUtils.getGeneratedClassFullyQualifiedName; import static org.hibernate.processor.util.TypeUtils.isMemberType; @@ -94,6 +95,9 @@ private static StringBuffer generateBody(Metamodel entity, Context context) { pw.println( entity.javadoc() ); + if ( context.addComponentAnnotation() && entity.isInjectable() ) { + pw.println( writeComponentAnnotation( entity ) ); + } if ( context.addDependentAnnotation() && entity.isInjectable() ) { pw.println( writeScopeAnnotation( entity ) ); } @@ -314,6 +318,10 @@ private static String writeScopeAnnotation(Metamodel entity) { return "@" + entity.importType( entity.scope() ); } + private static String writeComponentAnnotation(Metamodel entity) { + return "@" + entity.importType( SPRING_COMPONENT ); + } + private static String writeStaticMetaModelAnnotation(Metamodel entity) { final String annotation = entity.isJakartaDataStyle() ? "jakarta.data.metamodel.StaticMetamodel" diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java index 9ef6a41e3f01..2773e242045a 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java @@ -84,6 +84,7 @@ public final class Context { private Boolean fullyXmlConfigured; private boolean addInjectAnnotation = false; private boolean addDependentAnnotation = false; + private boolean addComponentAnnotation = false; private boolean addNonnullAnnotation = false; private boolean addGeneratedAnnotation = true; private boolean addGenerationDate; @@ -92,6 +93,7 @@ public final class Context { private AccessType persistenceUnitDefaultAccessType; private boolean generateJakartaDataStaticMetamodel; private boolean quarkusInjection; + private boolean springInjection; private boolean dataEventPackageAvailable; // keep track of all classes for which model have been generated @@ -173,6 +175,14 @@ public void setAddDependentAnnotation(boolean addDependentAnnotation) { this.addDependentAnnotation = addDependentAnnotation; } + public boolean addComponentAnnotation() { + return addComponentAnnotation; + } + + public void setAddComponentAnnotation(boolean addComponentAnnotation) { + this.addComponentAnnotation = addComponentAnnotation; + } + public boolean addNonnullAnnotation() { return addNonnullAnnotation; } @@ -225,6 +235,14 @@ public void setQuarkusInjection(boolean quarkusInjection) { this.quarkusInjection = quarkusInjection; } + public boolean isSpringInjection() { + return springInjection; + } + + public void setSpringInjection(boolean springInjection) { + this.springInjection = springInjection; + } + public boolean isDataEventPackageAvailable() { return dataEventPackageAvailable; } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java index da6364466851..47d4751d89cc 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java @@ -266,6 +266,13 @@ && packagePresent(quarkusOrmPanachePackage) ) { quarkusOrmPanachePackage = quarkusReactivePanachePackage = null; } + final PackageElement springBeansPackage = + context.getProcessingEnvironment().getElementUtils() + .getPackageElement( "org.springframework.beans.factory" ); + final PackageElement springStereotypePackage = + context.getProcessingEnvironment().getElementUtils() + .getPackageElement( "org.springframework.stereotype" ); + context.setAddInjectAnnotation( packagePresent(jakartaInjectPackage) ); context.setAddNonnullAnnotation( packagePresent(jakartaAnnotationPackage) ); context.setAddGeneratedAnnotation( packagePresent(jakartaAnnotationPackage) ); @@ -275,6 +282,8 @@ && packagePresent(quarkusOrmPanachePackage) ) { context.setQuarkusInjection( packagePresent(quarkusOrmPackage) || packagePresent(quarkusReactivePackage) ); context.setUsesQuarkusOrm( packagePresent(quarkusOrmPanachePackage) ); context.setUsesQuarkusReactive( packagePresent(quarkusReactivePanachePackage) ); + context.setSpringInjection( packagePresent(springBeansPackage) ); + context.setAddComponentAnnotation( packagePresent(springStereotypePackage) ); final Map options = environment.getOptions(); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java index 943a710f5127..c661c03961fc 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractAnnotatedMethod.java @@ -63,6 +63,10 @@ String localSessionName() { return isReactiveSessionAccess() ? "_session" : sessionName; } + String getObjectCall() { + return annotationMetaEntity.isProvidedSessionAccess() ? ".getObject()" : ""; + } + @Override public List inheritedAnnotations() { if ( annotationMetaEntity.isJakartaDataRepository() ) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractCriteriaMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractCriteriaMethod.java index 26e904b75ab1..5c8eb7565da3 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractCriteriaMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractCriteriaMethod.java @@ -86,11 +86,13 @@ void createQuery(StringBuilder declaration) { declaration .append("_spec.createQuery(") .append(localSessionName()) + .append(getObjectCall()) .append(")\n"); } else { declaration .append(localSessionName()) + .append(getObjectCall()) .append(".") .append(createQueryMethod()) .append('('); @@ -138,6 +140,7 @@ private void createBuilder(StringBuilder declaration) { declaration .append("\tvar _builder = ") .append(localSessionName()) + .append(getObjectCall()) .append(".getCriteriaBuilder();\n"); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractFinderMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractFinderMethod.java index 78839411351f..79640626bd4a 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractFinderMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractFinderMethod.java @@ -170,7 +170,8 @@ void tryReturn(StringBuilder declaration) { } declaration .append("\treturn ") - .append(sessionName); + .append(sessionName) + .append(getObjectCall()); } void modifiers(StringBuilder declaration) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java index 8773d5b6a2f7..6a293543e757 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java @@ -124,6 +124,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { private final boolean managed; private boolean jakartaDataRepository; private final boolean quarkusInjection; + private final boolean springInjection; private String qualifiedName; private final boolean jakartaDataStaticModel; @@ -180,6 +181,7 @@ public AnnotationMetaEntity( this.managed = managed; this.members = new LinkedHashMap<>(); this.quarkusInjection = context.isQuarkusInjection(); + this.springInjection = context.isSpringInjection(); this.importContext = parent != null ? parent : new ImportContextImpl( getPackageName( context, element ) ); jakartaDataStaticModel = jakartaDataStaticMetamodel; importContext.importType( @@ -769,20 +771,24 @@ private void setupSession() { sessionType = addDaoConstructor( getter ); } else { - // For Panache subtypes, we look at the session type, but no DAO, we want static methods + // For Panache subtypes, we look at the session type, but no DAO, + // we want static methods sessionType = fullReturnType(getter); } } else if ( element.getKind() == ElementKind.INTERFACE && !jakartaDataRepository && ( context.usesQuarkusOrm() || context.usesQuarkusReactive() ) ) { - // if we don't have a getter, and not a JD repository, but we're in Quarkus, we know how to find the default sessions + // if we don't have a getter, and not a JD repository, but we're in Quarkus, + // we know how to find the default sessions repository = true; sessionType = setupQuarkusDaoConstructor(); } if ( !repository && jakartaDataRepository ) { repository = true; - sessionType = HIB_STATELESS_SESSION; + sessionType = springInjection + ? SPRING_STATELESS_SESSION_PROVIDER + : HIB_STATELESS_SESSION; addDaoConstructor( null ); } if ( needsDefaultConstructor() ) { @@ -819,6 +825,7 @@ void addEventBus() { boolean needsDefaultConstructor() { return jakartaDataRepository && !quarkusInjection + && !springInjection && context.addDependentAnnotation(); } @@ -863,6 +870,10 @@ public boolean isReactiveSessionAccess() { return usingReactiveSessionAccess(sessionType); } + public boolean isProvidedSessionAccess() { + return sessionType.startsWith(SPRING_OBJECT_PROVIDER); + } + private boolean isPanacheType(TypeElement type) { return context.usesQuarkusOrm() && isOrmPanacheType( type ) || context.usesQuarkusReactive() && isReactivePanacheType( type ); @@ -940,7 +951,7 @@ private String setupQuarkusDaoConstructor() { importType( Constants.QUARKUS_SESSION_OPERATIONS ); // use this getter to get the method, do not generate an injection point for its type sessionGetter = "SessionOperations.getSession()"; - return Constants.UNI_MUTINY_SESSION; + return UNI_MUTINY_SESSION; } } @@ -950,14 +961,23 @@ private String setupQuarkusDaoConstructor() { * needed return types. */ private static boolean isSessionGetter(ExecutableElement method) { - if ( method.getParameters().isEmpty() ) { - final TypeMirror returnType = method.getReturnType(); - if ( returnType.getKind() == TypeKind.DECLARED ) { - final DeclaredType declaredType = (DeclaredType) ununi(returnType); - final Element element = declaredType.asElement(); - if ( element.getKind() == ElementKind.INTERFACE ) { - final TypeElement typeElement = (TypeElement) element; - final Name name = typeElement.getQualifiedName(); + return method.getParameters().isEmpty() + && isSessionGetterType( method.getReturnType() ); + } + + private static boolean isSessionGetterType(TypeMirror returnType) { + if ( returnType.getKind() == TypeKind.DECLARED ) { + final DeclaredType declaredType = (DeclaredType) ununi( returnType ); + final Element element = declaredType.asElement(); + if ( element.getKind() == ElementKind.INTERFACE ) { + final TypeElement typeElement = (TypeElement) element; + final Name name = typeElement.getQualifiedName(); + if ( name.contentEquals(UNI) || name.contentEquals(SPRING_OBJECT_PROVIDER) ) { + final var typeArguments = declaredType.getTypeArguments(); + return typeArguments.size() == 1 + && isSessionGetterType( typeArguments.get( 0 ) ); + } + else { return name.contentEquals(HIB_SESSION) || name.contentEquals(HIB_STATELESS_SESSION) || name.contentEquals(MUTINY_SESSION) @@ -965,8 +985,13 @@ private static boolean isSessionGetter(ExecutableElement method) { || name.contentEquals(ENTITY_MANAGER); } } + else { + return false; + } + } + else { + return false; } - return false; } /** @@ -2054,7 +2079,9 @@ protected String getSessionVariableName() { private String getSessionVariableName(String sessionType) { return switch (sessionType) { - case HIB_SESSION, HIB_STATELESS_SESSION, MUTINY_SESSION, MUTINY_STATELESS_SESSION -> "session"; + case HIB_SESSION, HIB_STATELESS_SESSION, + MUTINY_SESSION, MUTINY_STATELESS_SESSION, + SPRING_STATELESS_SESSION_PROVIDER -> "session"; // case UNI_MUTINY_SESSION, UNI_MUTINY_STATELESS_SESSION -> "session"; default -> sessionGetter; }; @@ -3388,7 +3415,8 @@ private static boolean usingReactiveSession(String sessionType) { private static boolean usingStatelessSession(String sessionType) { return HIB_STATELESS_SESSION.equals(sessionType) || MUTINY_STATELESS_SESSION.equals(sessionType) - || UNI_MUTINY_STATELESS_SESSION.equals(sessionType); + || UNI_MUTINY_STATELESS_SESSION.equals(sessionType) + || SPRING_STATELESS_SESSION_PROVIDER.equals(sessionType); } private static boolean usingReactiveSessionAccess(String sessionType) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdFinderMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdFinderMethod.java index 4b347fcccb1d..fc27c6eb4405 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdFinderMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/IdFinderMethod.java @@ -179,7 +179,8 @@ else if (!nullable && !isReactive()) { .append("\treturn "); } declaration - .append(sessionName); + .append(sessionName) + .append(getObjectCall()); } private void findWithFetchProfiles(StringBuilder declaration) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java index 320faff4ab22..b054c386da15 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/LifecycleMethod.java @@ -216,11 +216,13 @@ private void delegateBlockingly(StringBuilder declaration) { declaration .append("\t\tif (") .append(sessionName) + .append(getObjectCall()) .append(".getIdentifier(") .append(parameterName) .append(") == null)\n") .append("\t\t\t") .append(sessionName) + .append(getObjectCall()) .append('.') .append("insert"); argument( declaration ); @@ -231,6 +233,7 @@ private void delegateBlockingly(StringBuilder declaration) { declaration .append("\t\t") .append(sessionName) + .append(getObjectCall()) .append('.') .append(operationName); argument( declaration ); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/QueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/QueryMethod.java index 5dd4012462f7..15770caf2c69 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/QueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/QueryMethod.java @@ -129,12 +129,14 @@ void createQuery(StringBuilder declaration) { declaration .append("_spec.createQuery(") .append(localSessionName()) + .append(getObjectCall()) .append(")\n"); } } else { declaration .append(localSessionName()) + .append(getObjectCall()) .append('.') .append(createQueryMethod()) .append("(") diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java index 696a9d43bf4d..be8ff0781d08 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/RepositoryConstructor.java @@ -115,6 +115,7 @@ public String getAttributeDeclarationString() { } declaration .append("}"); + // resource accessor method a.k.a. session getter if ( annotationMetaEntity.getSuperTypeElement() == null ) { declaration .append("\n\n"); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java index c521d28a9d62..0aecc1d5ecb7 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java @@ -158,23 +158,30 @@ public final class Constants { public static final String PANACHE_REACTIVE_REPOSITORY_BASE = "io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase"; public static final String PANACHE_REACTIVE_ENTITY_BASE = "io.quarkus.hibernate.reactive.panache.PanacheEntityBase"; + public static final String SPRING_OBJECT_PROVIDER = "org.springframework.beans.factory.ObjectProvider"; + public static final String SPRING_STATELESS_SESSION_PROVIDER = SPRING_OBJECT_PROVIDER + "<" + HIB_STATELESS_SESSION + ">"; + public static final String SPRING_COMPONENT = "org.springframework.stereotype.Component"; + public static final Map COLLECTIONS = Map.of( - COLLECTION, Constants.COLLECTION_ATTRIBUTE, - SET, Constants.SET_ATTRIBUTE, - LIST, Constants.LIST_ATTRIBUTE, - MAP, Constants.MAP_ATTRIBUTE, + COLLECTION, COLLECTION_ATTRIBUTE, + SET, SET_ATTRIBUTE, + LIST, LIST_ATTRIBUTE, + MAP, MAP_ATTRIBUTE, // Hibernate also supports the SortedSet and SortedMap interfaces - java.util.SortedSet.class.getName(), Constants.SET_ATTRIBUTE, - java.util.SortedMap.class.getName(), Constants.MAP_ATTRIBUTE + java.util.SortedSet.class.getName(), SET_ATTRIBUTE, + java.util.SortedMap.class.getName(), MAP_ATTRIBUTE ); public static final Set SESSION_TYPES = Set.of( - Constants.ENTITY_MANAGER, - Constants.HIB_SESSION, - Constants.HIB_STATELESS_SESSION, - Constants.MUTINY_SESSION, - Constants.UNI_MUTINY_SESSION + ENTITY_MANAGER, + HIB_SESSION, + HIB_STATELESS_SESSION, + MUTINY_SESSION, + MUTINY_STATELESS_SESSION, + UNI_MUTINY_SESSION, + UNI_MUTINY_STATELESS_SESSION, + SPRING_STATELESS_SESSION_PROVIDER ); //TODO: this is not even an exhaustive list of built-in basic types