From d52edeb0e5fb35fb309843934679a3b0ec6fecc1 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 25 Mar 2024 14:41:55 +0100 Subject: [PATCH] experimental support for reactive Jakarta Data repositories --- .../hibernate-jpamodelgen.gradle | 1 + .../processor/test/data/reactive/Address.java | 6 + .../processor/test/data/reactive/Author.java | 23 ++++ .../processor/test/data/reactive/Book.java | 59 ++++++++++ .../processor/test/data/reactive/Library.java | 106 ++++++++++++++++++ .../test/data/reactive/Publisher.java | 19 ++++ .../test/data/reactive/ReactiveTest.java | 36 ++++++ .../processor/test/data/reactive/Type.java | 3 + .../annotation/AbstractFinderMethod.java | 4 +- .../annotation/AbstractQueryMethod.java | 61 +++++++--- .../annotation/AnnotationMetaEntity.java | 27 ++++- .../annotation/CriteriaFinderMethod.java | 5 +- .../annotation/DefaultConstructor.java | 21 +++- .../processor/annotation/LifecycleMethod.java | 88 ++++++++++++--- .../processor/annotation/QueryMethod.java | 8 +- .../hibernate/processor/util/Constants.java | 2 + 16 files changed, 418 insertions(+), 51 deletions(-) create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Address.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Author.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Book.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Library.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Publisher.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/ReactiveTest.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Type.java diff --git a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle index 05696b4e8f..9c82c05e08 100644 --- a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle +++ b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle @@ -71,6 +71,7 @@ dependencies { quarkusOrmPanacheImplementation "io.quarkus:quarkus-hibernate-orm-panache:3.6.2" quarkusHrPanacheImplementation "io.quarkus:quarkus-hibernate-reactive-panache:3.6.2" jakartaDataImplementation "jakarta.data:jakarta.data-api:1.0.0-SNAPSHOT" + jakartaDataImplementation "org.hibernate.reactive:hibernate-reactive-core:2.2.2.Final" } // The source set gets a custom configuration which extends the normal test implementation config diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Address.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Address.java new file mode 100644 index 0000000000..2a87457e66 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Address.java @@ -0,0 +1,6 @@ +package org.hibernate.processor.test.data.reactive; + +import jakarta.persistence.Embeddable; + +@Embeddable +public record Address(String street, String city, String postcode) {} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Author.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Author.java new file mode 100644 index 0000000000..885f309bde --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Author.java @@ -0,0 +1,23 @@ +package org.hibernate.processor.test.data.reactive; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; + +import java.util.Set; + +@Entity +public class Author { + @Id + String ssn; + + @Basic(optional = false) + String name; + + Address address; + + @ManyToMany + Set books; +} + diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Book.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Book.java new file mode 100644 index 0000000000..601e30ff62 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Book.java @@ -0,0 +1,59 @@ +package org.hibernate.processor.test.data.reactive; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import org.hibernate.annotations.NaturalId; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.Set; + +@Entity +public class Book { + @Id + String isbn; + + @NaturalId + String title; + + @NaturalId + LocalDate publicationDate; + + String text; + + @Enumerated(EnumType.STRING) + @Basic(optional = false) + Type type = Type.Book; + + @ManyToOne(optional = false) + Publisher publisher; + + @ManyToMany(mappedBy = "books") + Set authors; + + @Basic(optional = false) + int pages ; + + BigDecimal price; + BigInteger quantitySold; + + public Book(String isbn, String title, String text) { + this.isbn = isbn; + this.title = title; + this.text = text; + } + + protected Book() {} + + @Override + public String toString() { + return isbn + " : " + title + " [" + type + "]"; + } +} + diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Library.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Library.java new file mode 100644 index 0000000000..7c6d66f6e4 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Library.java @@ -0,0 +1,106 @@ +package org.hibernate.processor.test.data.reactive; + +import io.smallrye.mutiny.Uni; +import jakarta.data.Limit; +import jakarta.data.Order; +import jakarta.data.Sort; +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; +import jakarta.data.repository.By; +import jakarta.data.repository.Delete; +import jakarta.data.repository.Find; +import jakarta.data.repository.Insert; +import jakarta.data.repository.OrderBy; +import jakarta.data.repository.Query; +import jakarta.data.repository.Repository; +import jakarta.data.repository.Save; +import jakarta.data.repository.Update; +import org.hibernate.reactive.mutiny.Mutiny; + +import java.time.LocalDate; +import java.util.List; + +@Repository +public interface Library { + + Mutiny.StatelessSession session(); //required + + @Find + Uni book(String isbn); + + @Find + Uni> books(@By("isbn") List isbns); + + @Find + Uni book(String title, LocalDate publicationDate); + + @Find + Uni> publications(Type type, Sort sort); + + @Find + @OrderBy("title") + Uni> booksByPublisher(String publisher_name); + + @Query("where title like :titlePattern") + @OrderBy("title") + Uni> booksByTitle(String titlePattern); + + // not required by Jakarta Data + record BookWithAuthor(Book book, Author author) {} + @Query("select b, a from Book b join b.authors a order by b.isbn, a.ssn") + Uni> booksWithAuthors(); + + @Insert + Uni create(Book book); + + @Insert + Uni create(Book[] book); + + @Update + Uni update(Book book); + + @Update + Uni update(Book[] books); + + @Delete + Uni delete(Book book); + + @Delete + Uni delete(Book[] book); + + @Save + Uni upsert(Book book); + + @Find + Uni author(String ssn); + + @Insert + Uni create(Author author); + + @Update + Uni update(Author author); + + @Insert + Uni insertAll(Publisher[] publishers); + + @Save + Uni save(Publisher publisher); + + @Delete + Uni delete(Publisher publisher); + + @Find + @OrderBy("isbn") + Uni> allBooks(PageRequest pageRequest); + + @Find + @OrderBy("name") + @OrderBy("address.city") + Uni> allAuthors(Order order, Limit limit); + + @Find + Uni> authorsByCity(@By("address.city") String city); + + @Find + Uni> authorsByCityAndPostcode(String address_city, String address_postcode); +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Publisher.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Publisher.java new file mode 100644 index 0000000000..cadf16beaa --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Publisher.java @@ -0,0 +1,19 @@ +package org.hibernate.processor.test.data.reactive; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; + +import java.util.Set; + +@Entity +public class Publisher { + @Id long id; + + @Basic(optional = false) + String name; + + @OneToMany(mappedBy = "publisher") + Set books; +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/ReactiveTest.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/ReactiveTest.java new file mode 100644 index 0000000000..ed2b18de78 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/ReactiveTest.java @@ -0,0 +1,36 @@ +/* + * 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 . + */ +package org.hibernate.processor.test.data.reactive; + +import org.hibernate.processor.test.util.CompilationTest; +import org.hibernate.processor.test.util.WithClasses; +import org.junit.Test; + +import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor; +import static org.hibernate.processor.test.util.TestUtil.getMetaModelSourceAsString; + +/** + * @author Gavin King + */ +public class ReactiveTest extends CompilationTest { + @Test + @WithClasses({ Publisher.class, Author.class, Address.class, Book.class, Library.class }) + public void test() { + System.out.println( getMetaModelSourceAsString( Author.class ) ); + System.out.println( getMetaModelSourceAsString( Book.class ) ); + System.out.println( getMetaModelSourceAsString( Author.class, true ) ); + System.out.println( getMetaModelSourceAsString( Book.class, true ) ); + System.out.println( getMetaModelSourceAsString( Library.class ) ); + assertMetamodelClassGeneratedFor( Author.class, true ); + assertMetamodelClassGeneratedFor( Book.class, true ); + assertMetamodelClassGeneratedFor( Publisher.class, true ); + assertMetamodelClassGeneratedFor( Author.class ); + assertMetamodelClassGeneratedFor( Book.class ); + assertMetamodelClassGeneratedFor( Publisher.class ); + assertMetamodelClassGeneratedFor( Library.class ); + } +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Type.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Type.java new file mode 100644 index 0000000000..1e4393b767 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/reactive/Type.java @@ -0,0 +1,3 @@ +package org.hibernate.processor.test.data.reactive; + +public enum Type { Book, Magazine, Journal } 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 de70d7ea18..66e426b431 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 @@ -7,12 +7,12 @@ package org.hibernate.processor.annotation; import org.hibernate.internal.util.StringHelper; -import org.hibernate.processor.util.Constants; import java.util.List; import java.util.Locale; import static org.hibernate.processor.util.Constants.HIB_SESSION; +import static org.hibernate.processor.util.Constants.UNI; import static org.hibernate.processor.util.StringUtil.getUpperUnderscoreCaseFromLowerCamelCase; /** @@ -194,7 +194,7 @@ public abstract class AbstractFinderMethod extends AbstractQueryMethod { private void entityType(StringBuilder declaration) { if ( isReactive() ) { declaration - .append(annotationMetaEntity.importType(Constants.UNI)) + .append(annotationMetaEntity.importType(UNI)) .append('<'); } declaration diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java index c8c2355960..b64e34d575 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AbstractQueryMethod.java @@ -180,11 +180,13 @@ public abstract class AbstractQueryMethod implements MetaAttribute { } boolean isUsingStatelessSession() { - return HIB_STATELESS_SESSION.equals(sessionType); + return HIB_STATELESS_SESSION.equals(sessionType) + || MUTINY_STATELESS_SESSION.equals(sessionType); } boolean isReactive() { return MUTINY_SESSION.equals(sessionType) + || MUTINY_STATELESS_SESSION.equals(sessionType) || UNI_MUTINY_SESSION.equals(sessionType); } @@ -440,7 +442,7 @@ public abstract class AbstractQueryMethod implements MetaAttribute { declaration .append('\t'); if ( isJakartaCursoredPage(containerType) - || isJakartaPage(containerType) ) { + || isJakartaPage(containerType) && !isReactive() ) { if ( returnTypeName != null && isUsingEntityManager() ) { // this is necessary to avoid losing the type // after unwrapping the Query object @@ -467,18 +469,24 @@ public abstract class AbstractQueryMethod implements MetaAttribute { private void totalResults(StringBuilder declaration, List paramTypes) { declaration - .append("\tlong _totalResults = \n\t\t\t\t") - .append(parameterName(JD_PAGE_REQUEST, paramTypes, paramNames)) - .append(".requestTotal()\n\t\t\t\t\t\t? "); - createQuery( declaration ); - setParameters( declaration, paramTypes, "\t\t\t\t\t"); - if ( isUsingEntityManager() ) { - declaration - .append("\t\t\t\t\t"); + .append("\tlong _totalResults = \n\t\t\t\t"); + if ( isReactive() ) { + declaration.append("-1;\n"); //TODO: add getResultCount() to HR + } + else { + declaration + .append(parameterName(JD_PAGE_REQUEST, paramTypes, paramNames)) + .append(".requestTotal()\n\t\t\t\t\t\t? "); + createQuery( declaration ); + setParameters( declaration, paramTypes, "\t\t\t\t\t"); + if ( isUsingEntityManager() ) { + declaration + .append("\t\t\t\t\t"); + } + unwrapQuery( declaration, !isUsingEntityManager() ); + declaration + .append("\t\t\t\t\t\t\t\t.getResultCount()\n\t\t\t\t\t\t: -1;\n"); } - unwrapQuery( declaration, !isUsingEntityManager() ); - declaration - .append("\t\t\t\t\t\t\t\t.getResultCount()\n\t\t\t\t\t\t: -1;\n"); } void collectOrdering(StringBuilder declaration, List paramTypes) { @@ -646,13 +654,28 @@ public abstract class AbstractQueryMethod implements MetaAttribute { .append(");"); break; case JD_PAGE: + if ( isReactive() ) { + declaration + .append("\t\t\t.getResultList()\n") + .append("\t\t\t.map(_results -> "); + } + else { + declaration + .append("\t\t\t.getResultList();\n") + .append("\t\treturn "); + } declaration - .append("\t\t\t.getResultList();\n") - .append("\t\treturn new ") + .append("new ") .append(annotationMetaEntity.importType("jakarta.data.page.impl.PageRecord")) .append('(') .append(parameterName(JD_PAGE_REQUEST, paramTypes, paramNames)) - .append(", _results, _totalResults);"); + .append(", _results, _totalResults)"); + if ( isReactive() ) { + declaration + .append(')'); + } + declaration + .append(';'); break; case JD_CURSORED_PAGE: if ( returnTypeName == null ) { @@ -716,4 +739,10 @@ public abstract class AbstractQueryMethod implements MetaAttribute { && containerType.startsWith("org.hibernate"); } + boolean isUnifiableReturnType(@Nullable String containerType) { + return containerType == null + || LIST.equals(containerType) + || JD_PAGE.equals(containerType) + || JD_CURSORED_PAGE.equals(containerType); + } } 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 c94472c1a9..cae7480279 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 @@ -668,6 +668,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { return name.contentEquals(HIB_SESSION) || name.contentEquals(HIB_STATELESS_SESSION) || name.contentEquals(MUTINY_SESSION) + || name.contentEquals(MUTINY_STATELESS_SESSION) || name.contentEquals(ENTITY_MANAGER); } } @@ -1149,8 +1150,9 @@ public class AnnotationMetaEntity extends AnnotationMeta { } private void addLifecycleMethod(ExecutableElement method) { - final TypeMirror returnType = method.getReturnType(); - if ( !HIB_STATELESS_SESSION.equals(sessionType) ) { + final TypeMirror returnType = ununi(method.getReturnType()); + if ( !HIB_STATELESS_SESSION.equals(sessionType) + && !MUTINY_STATELESS_SESSION.equals(sessionType) ) { context.message( method, "repository must be backed by a 'StatelessSession'", Diagnostic.Kind.ERROR ); @@ -1167,7 +1169,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { Diagnostic.Kind.ERROR ); } else { - final boolean returnArgument = returnType.getKind() != TypeKind.VOID; + final boolean returnArgument = !isVoid(returnType); final String operation = lifecycleOperation( method ); final VariableElement parameter = method.getParameters().get(0); final TypeMirror declaredParameterType = parameter.asType(); @@ -1203,6 +1205,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { methodName, parameter.getSimpleName().toString(), getSessionVariableName(), + sessionType, operation, context.addNonnullAnnotation(), declaredType != parameterType, @@ -1213,6 +1216,19 @@ public class AnnotationMetaEntity extends AnnotationMeta { } } + private static boolean isVoid(TypeMirror returnType) { + switch (returnType.getKind()) { + case VOID: + return true; + case DECLARED: + final DeclaredType declaredType = (DeclaredType) returnType; + final TypeElement typeElement = (TypeElement) declaredType.asElement(); + return typeElement.getQualifiedName().contentEquals(Void.class.getName()); + default: + return false; + } + } + private @Nullable DeclaredType entityType(TypeMirror parameterType) { final Types types = context.getTypeUtils(); switch ( parameterType.getKind() ) { @@ -1576,6 +1592,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { case HIB_SESSION: case HIB_STATELESS_SESSION: case MUTINY_SESSION: + case MUTINY_STATELESS_SESSION: return "session"; default: return sessionGetter; @@ -2604,10 +2621,12 @@ public class AnnotationMetaEntity extends AnnotationMeta { private boolean usingReactiveSession(String sessionType) { return MUTINY_SESSION.equals(sessionType) + || MUTINY_STATELESS_SESSION.equals(sessionType) || UNI_MUTINY_SESSION.equals(sessionType); } private boolean usingStatelessSession(String sessionType) { - return HIB_STATELESS_SESSION.equals(sessionType); + return HIB_STATELESS_SESSION.equals(sessionType) + || MUTINY_STATELESS_SESSION.equals(sessionType); } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaFinderMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaFinderMethod.java index dc6fa68fcc..add7d65af0 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaFinderMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CriteriaFinderMethod.java @@ -12,8 +12,6 @@ import org.hibernate.processor.util.Constants; import java.util.List; -import static org.hibernate.processor.util.Constants.LIST; - /** * @author Gavin King */ @@ -110,8 +108,7 @@ public class CriteriaFinderMethod extends AbstractCriteriaMethod { type.append(annotationMetaEntity.importType(returnTypeName)).append("[]"); } else { - boolean returnsUni = isReactive() - && (containerType == null || LIST.equals(containerType)); + final boolean returnsUni = isReactive() && isUnifiableReturnType(containerType); if ( returnsUni ) { type.append(annotationMetaEntity.importType(Constants.UNI)).append('<'); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java index 32e8936900..9637f67b4a 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/DefaultConstructor.java @@ -13,6 +13,10 @@ import org.hibernate.processor.util.Constants; import static org.hibernate.processor.util.Constants.ENTITY_MANAGER_FACTORY; import static org.hibernate.processor.util.Constants.HIB_SESSION_FACTORY; +import static org.hibernate.processor.util.Constants.MUTINY_SESSION; +import static org.hibernate.processor.util.Constants.MUTINY_SESSION_FACTORY; +import static org.hibernate.processor.util.Constants.MUTINY_STATELESS_SESSION; +import static org.hibernate.processor.util.Constants.UNI_MUTINY_SESSION; /** * Used by the container to instantiate a Jakarta Data repository. @@ -45,6 +49,12 @@ public class DefaultConstructor implements MetaAttribute { this.addInjectAnnotation = addInjectAnnotation; } + private boolean isReactive() { + return MUTINY_SESSION.equals(sessionTypeName) + || MUTINY_STATELESS_SESSION.equals(sessionTypeName) + || UNI_MUTINY_SESSION.equals(sessionTypeName); + } + @Override public boolean hasTypedAttribute() { return true; @@ -84,9 +94,14 @@ public class DefaultConstructor implements MetaAttribute { .append(" = ") .append(sessionVariableName) .append("Factory.unwrap(") - .append(annotationMetaEntity.importType(HIB_SESSION_FACTORY)) - .append(".class).openStatelessSession();") - .append("\n}\n\n"); + .append(annotationMetaEntity.importType(isReactive() ? MUTINY_SESSION_FACTORY : HIB_SESSION_FACTORY)) + .append(".class).openStatelessSession()"); + if ( isReactive() ) { + declaration + .append(".await().indefinitely()"); + } + declaration + .append(";\n}\n\n"); declaration.append('@') .append(annotationMetaEntity.importType("jakarta.annotation.PreDestroy")) .append("\nprivate void closeSession() {") 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 c20a29a15d..4d13c47877 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 @@ -9,12 +9,18 @@ package org.hibernate.processor.annotation; import org.hibernate.processor.model.MetaAttribute; import org.hibernate.processor.model.Metamodel; +import static org.hibernate.processor.util.Constants.MUTINY_SESSION; +import static org.hibernate.processor.util.Constants.MUTINY_STATELESS_SESSION; +import static org.hibernate.processor.util.Constants.UNI; +import static org.hibernate.processor.util.Constants.UNI_MUTINY_SESSION; + 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 sessionType; private final String operationName; private final boolean addNonnullAnnotation; private final boolean iterateParameter; @@ -26,6 +32,7 @@ public class LifecycleMethod implements MetaAttribute { String methodName, String parameterName, String sessionName, + String sessionType, String operationName, boolean addNonnullAnnotation, boolean iterateParameter, @@ -35,6 +42,7 @@ public class LifecycleMethod implements MetaAttribute { this.methodName = methodName; this.parameterName = parameterName; this.sessionName = sessionName; + this.sessionType = sessionType; this.operationName = operationName; this.addNonnullAnnotation = addNonnullAnnotation; this.iterateParameter = iterateParameter; @@ -79,39 +87,66 @@ public class LifecycleMethod implements MetaAttribute { private void returnArgument(StringBuilder declaration) { if ( returnArgument ) { - declaration - .append("\t\treturn ") + if ( isReactive() ) { + declaration + .append(".replaceWith(") .append(parameterName) + .append(")"); + } + else { + declaration + .append("\t\treturn ") + .append(parameterName); + } + declaration .append(";\n"); } + else { + if ( isReactive() ) { + declaration + .append(";\n"); + } + } } private void delegateCall(StringBuilder declaration) { - if ( iterateParameter ) { + if ( isReactive() ) { declaration - .append("\t\tfor (var entity : ") + .append("\t\treturn ") + .append(sessionName) + .append('.') + .append(operationName) + .append('(') .append(parameterName) - .append(") {\n\t"); + .append(')'); } - declaration - .append("\t\t") - .append(sessionName) - .append('.') - .append(operationName) - .append('(') - .append(iterateParameter ? "entity" : parameterName) - .append(')') - .append(";\n"); - if ( iterateParameter ) { + else { + if ( iterateParameter ) { + declaration + .append("\t\tfor (var _entity : ") + .append(parameterName) + .append(") {\n\t"); + } declaration - .append("\t\t}\n"); + .append("\t\t") + .append(sessionName) + .append('.') + .append(operationName) + .append('(') + .append(iterateParameter ? "_entity" : parameterName) + .append(')') + .append(";\n"); + if ( iterateParameter ) { + declaration + .append("\t\t}\n"); + } } } private void preamble(StringBuilder declaration) { declaration .append("\n@Override\npublic ") - .append(returnArgument ? annotationMetaEntity.importType(entity) : "void") + .append(returnType()) .append(' ') .append(methodName) .append('('); @@ -124,6 +159,19 @@ public class LifecycleMethod implements MetaAttribute { .append(" {\n"); } + private String returnType() { + final String entityType = annotationMetaEntity.importType(entity); + if ( isReactive() ) { + return annotationMetaEntity.importType(UNI) + + '<' + (returnArgument ? entityType : "Void") + '>'; + } + else { + return returnArgument + ? entityType + : "void"; + } + } + private void nullCheck(StringBuilder declaration) { declaration .append("\tif (") @@ -177,4 +225,10 @@ public class LifecycleMethod implements MetaAttribute { public Metamodel getHostingEntity() { return annotationMetaEntity; } + + private boolean isReactive() { + return MUTINY_SESSION.equals(sessionType) + || MUTINY_STATELESS_SESSION.equals(sessionType) + || UNI_MUTINY_SESSION.equals(sessionType); + } } 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 05fffc402d..558a0a3840 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 @@ -9,12 +9,11 @@ package org.hibernate.processor.annotation; import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.AssertionFailure; import org.hibernate.internal.util.StringHelper; -import org.hibernate.processor.util.Constants; import java.util.List; -import static org.hibernate.processor.util.Constants.LIST; import static org.hibernate.processor.util.Constants.QUERY; +import static org.hibernate.processor.util.Constants.UNI; import static org.hibernate.processor.util.StringUtil.getUpperUnderscoreCaseFromLowerCamelCase; /** @@ -215,10 +214,9 @@ public class QueryMethod extends AbstractQueryMethod { type.append(annotationMetaEntity.importType(returnTypeName)).append("[]"); } else { - boolean returnsUni = isReactive() - && (containerType == null || LIST.equals(containerType)); + final boolean returnsUni = isReactive() && isUnifiableReturnType(containerType); if ( returnsUni ) { - type.append(annotationMetaEntity.importType(Constants.UNI)).append('<'); + type.append(annotationMetaEntity.importType(UNI)).append('<'); } if ( containerType != null ) { type.append(annotationMetaEntity.importType(containerType)); 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 a00e5df64e..de645e9004 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 @@ -98,7 +98,9 @@ public final class Constants { public static final String HIB_SESSION = "org.hibernate.Session"; public static final String HIB_SESSION_FACTORY = "org.hibernate.SessionFactory"; public static final String HIB_STATELESS_SESSION = "org.hibernate.StatelessSession"; + public static final String MUTINY_SESSION_FACTORY = "org.hibernate.reactive.mutiny.Mutiny.SessionFactory"; public static final String MUTINY_SESSION = "org.hibernate.reactive.mutiny.Mutiny.Session"; + public static final String MUTINY_STATELESS_SESSION = "org.hibernate.reactive.mutiny.Mutiny.StatelessSession"; public static final String QUARKUS_SESSION_OPERATIONS = "io.quarkus.hibernate.reactive.panache.common.runtime.SessionOperations"; public static final String TUPLE = "jakarta.persistence.Tuple";