diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 475c094faa18a..39d00b0d1060c 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -1414,6 +1414,16 @@ quarkus-panacheql ${project.version} + + io.quarkus + quarkus-hibernate-panache + ${project.version} + + + io.quarkus + quarkus-hibernate-panache-deployment + ${project.version} + io.quarkus quarkus-hibernate-orm-panache diff --git a/devtools/bom-descriptor-json/pom.xml b/devtools/bom-descriptor-json/pom.xml index 228a7d8a9e9d0..1f79ec71c69c8 100644 --- a/devtools/bom-descriptor-json/pom.xml +++ b/devtools/bom-descriptor-json/pom.xml @@ -960,6 +960,19 @@ + + io.quarkus + quarkus-hibernate-panache + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-hibernate-reactive diff --git a/docs/pom.xml b/docs/pom.xml index e01b5ab245062..54509835700c6 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -924,6 +924,19 @@ + + io.quarkus + quarkus-hibernate-panache-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-hibernate-reactive-deployment diff --git a/docs/src/main/asciidoc/hibernate-panache.adoc b/docs/src/main/asciidoc/hibernate-panache.adoc new file mode 100644 index 0000000000000..c2a86096a626c --- /dev/null +++ b/docs/src/main/asciidoc/hibernate-panache.adoc @@ -0,0 +1,968 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Simplified Hibernate with Panache +include::_attributes.adoc[] +:categories: data +:summary: Hibernate is the de facto Jakarta Persistence implementation and offers you the full breadth of an Object Relational Mapper. It makes complex mappings possible, but it does not make simple and common mappings trivial. Panache focuses on making your entities trivial and fun to write. +:config-file: application.properties +:topics: data,hibernate-orm,hibernate-reactive,panache,sql,jdbc +:extensions: io.quarkus:quarkus-hibernate-panache,io.quarkus:quarkus-hibernate-orm,io.quarkus:quarkus-hibernate-reactive + +Hibernate with Panache is the perfect solution if you want to get started using Hibernate in Quarkus. + +== Walk-through + +Let's take a progressive approach to learning how to use Hibernate with Panache, and start with a simple entity. + +This guide is not going to go into the specifics of using either Hibernate, or its underlying specification JPA, +because both already have great documentation that you can and should use for more in-depth knowledge. + +== Importing the extension, and configuration + +```xml + + + io.quarkus + quarkus-hibernate-panache + + + + io.quarkus + quarkus-jdbc-postgresql + +``` + +Make sure you configure your datasource in your `application.properties` (although you don't have to in DEV +mode: if you don't, a dev service will be provided for you): + +```properties +# FIXME +``` + +== Our first entity + +What this guide will focus on is the basics of how to use Hibernate with Panache, so let's start with how to +create an entity: + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import jakarta.persistence.Entity; + +import java.time.LocalDate; + +@Entity +public class Cat extends PanacheEntity { + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } +} +``` + +In order to map a Java class to a Database table, just: + +- Write a class +- Annotate it with `@Entity` +- Make it extend `PanacheEntity` +- Make its fields `public` + +And that's it, now you can start creating instances of your entity, persist it to your database, after which point +all changes to its fields will be automatically sent to the database (no need for any explicit `update` instruction), +and even remove it from the database: + +```java +import jakarta.transaction.Transactional; + +import java.time.LocalDate; +import java.util.List; + +public class Code { + + @Transactional <1> + public void method() { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Persist the cat + cat.persist(); + + // Make a change, no need to update it + cat.name = "Luckynou"; + + // Delete our cat + cat.delete(); + } +} +``` +<1> This is required on any method which interacts with the database, in order to run the operations in a transaction. + +== Our first repository + +Now that we know how to create, update and delete our entities, let's see how we can query our database to find them, +or even run delete queries. + +Because query operations do not belong on instances of our entities, we will place these operations on another type +called a `Repository`, and because those queries are intimately tied with the entities they operate on, we recommend +placing them on an interface nested in the entity: + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import jakarta.persistence.Entity; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import java.time.LocalDate; +import java.util.List; + +@Entity +public class Cat extends PanacheEntity { + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } + + public interface Repo extends PanacheRepository { + @Find + Cat findByName(String name); + + @HQL("where breed = CUTE") + List findCute(); + + @HQL("delete from Cat where name = :name") + long deleteByName(String name); + + @HQL("delete from Cat where breed = HAIRLESS") + long deleteHairless(); + } +} +``` + +A Hibernate with Panache repository is: + +- An interface nested in the entity (though it could be a toplevel interface if you prefer) +- Which extends the `PanacheRepository` interface, with the entity in question as type parameter +- And contains query methods, annotated with either `@Find` or `@HQL` or even `@SQL` (for native queries), or + `default` methods with implementation of queries + +You can use `@Find` methods to find single instances, or collections of entities, by using the parameters of the +method to build a query. The Hibernate documentation has +https://docs.jboss.org/hibernate/orm/7.1/introduction/html_single/Hibernate_Introduction.html#generated-finder-methods[all the information you need about this]. + +TODO: common examples + +Alternately, you can use `@HQL` or `@SQL` queries, which support HQL and SQL queries. Once again +the Hibernate documentation has https://docs.jboss.org/hibernate/orm/7.1/introduction/html_single/Hibernate_Introduction.html#generated-query-methods[all the information you need]. + +TODO: common examples. + +The beauty of generated finder and query methods is that they are type-checked at build time: they will be validated +and guarantee that you did not make any typo in the entity names, their fields, the HQL/SQL syntax, or the name of +their parameters. + +=== Using the repository + +Using the repository can be done by simply injecting it where you intend to use it: + +```java +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + +import java.time.LocalDate; +import java.util.List; + +public class Code { + + @Inject + Cat.Repo repo; + + @Transactional + public void method() { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Persist the cat + cat.persist(); + + // Make a change, no need to update it + cat.name = "Luckynou"; + + // Find our cat + cat = repo.findByName("Luckynou"); + + // Find cute cats + List cuteCats = repo.findCute(); + + // Delete our cat + cat.delete(); + + // Delete queries + repo.deleteByName("Lucky"); + repo.deleteHairless(); + } +} +``` + +Now, as an added benefit, you can also access the repository using a handy shortcut generated static method on the +https://docs.jboss.org/hibernate/orm/7.1/introduction/html_single/Hibernate_Introduction.html#static-metamodel[generated Metamodel] +of your entity, which is super useful for discovery of operations (just type `Cat_.repo().` and see all your methods) +and to avoid making a detour outside of your method to add an injected field, such as in this startup code that +will delete all your cats (don't do this in production!!): + +```java +import io.quarkus.runtime.Startup; +import jakarta.transaction.Transactional; + +public class OnStart { + @Startup + @Transactional + public void startupMethod() { + Cat_.repo().deleteAll(); + } +} +``` + +All the repositories you define as nested interfaces of your entity will be available under their generated metamodel +class under accessor methods of the same name as the repository. This is strictly equivalent to injecting the repository +type, and in fact uses CDI under the hood to look up the repository. + +== The `PanacheRepository` super type + +Just like in previous versions of Hibernate ORM and Hibernate Reactive with Panache, the `PanacheRepository` type comes +packed with most of the operations you need to work on your entity, such as, out of the box: + +```java +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import org.hibernate.Session; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public class Code { + + @Inject + Cat.Repo repo; + + @Transactional + public void repositoryOperations(Cat cat) { + // entity operations + repo.persist(cat); + repo.delete(cat); + boolean isPersistent = repo.isPersistent(cat); + // operations on all entities + long count = repo.count(); + long deleted = repo.deleteAll(); + List allCats = repo.listAll(); + repo.streamAll().forEach(kitty -> kitty.name = kitty.name.toUpperCase()); + PanacheBlockingQuery catQuery = repo.findAll(); + // operations on the Hibernate session + repo.flush(); + Session session = repo.getSession(); + // ID-related operations + boolean wasDeleted = repo.deleteById(cat.id); + Cat foundCat = repo.findById(cat.id); + Optional optionalCat = repo.findByIdOptional(cat.id); + } +} +``` + +== Non-type-safe queries + +With generated finder and query methods, as we've previously shown, everything is validated at build-time, but if +you want to write non-type-safe queries, you can always use the provided methods of `PanacheRepository`: + +```java +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import org.hibernate.Session; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public class Code { + + @Inject + Cat.Repo repo; + + @Transactional + public void nonTypeSafeQueries() { + // Find a cat by name + Cat cat = repo.find(Cat_.NAME, "Lucky").singleResult(); + // All cute cats + List cuteCats = repo.list(Cat_.BREED, Cat.Breed.CUTE); + // Cats with no known birth date + repo.stream(Cat_.BIRTH+" is null").forEach(kitty -> System.err.println(kitty)); + // Get rid of non-cute cats + long deleted = repo.delete(Cat_.BREED, Cat.Breed.HAIRLESS); + // Count ugly cats with no name + repo.count("breed = ?1 and name is null", Cat.Breed.HAIRLESS); + // Make every cat cute + repo.update("breed = CUTE"); + } +} +``` + +== About entity identifiers + +In the example above, we extended the `PanacheEntity` type, and did not define any database identifier, that's +because `PanacheEntity` comes with a default generated database identifier, so you don't have to worry about it. It +does this by extending the `WithId.AutoLong` class, which provides a generated database identifier of type `Long`. + +You can choose to extend the `WithId.AutoString` for a `String` identifier, or `WithId.AutoUUID` for a `UUID` +identifier, or even extend `WithId` to automatically get an attribute of the form: + +```java +@Id +@GeneratedValue +public IdType id; +``` + +Naturally, you can also provide your own database identifier explicitly and implement the `PanacheEntity.Managed` +interface in your entity, as well as use the `PanacheRepository.Managed` interface for your repository, in order to +specify you database identifier type: + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import java.time.LocalDate; +import java.util.List; + +@Entity +public class CatWithId implements PanacheEntity.Managed { + @Id + public String id; + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } + + public interface Repo extends PanacheRepository.Managed { + @Find + CatWithId findByName(String name); + + @HQL("where breed = CUTE") + List findCute(); + + @HQL("delete from Cat where name = :name") + long deleteByName(String name); + + @HQL("delete from Cat where breed = HAIRLESS") + long deleteHairless(); + } +} +``` + +Here is a list of entity supertypes you can use and the ID type they provide, but keep in mind you do not have to +extend any of these types if you define you own ID: + +[cols="1,1,1"] +|=== +|ID type|Supertype|Shortcut type + +|`Long` +|`WithId.AutoLong` +|`PanacheEntity` + +|`UUID` +|`WithId.AutoUUID` +| + +|`String` +|`WithId.String` +| + +|`T` +|`WithId +| + +|=== + +== Use stateless sessions + +Out of the box, your subtype of `PanacheEntity` will be managed by Hibernate ORM, and every change to the entity will +be automatically sent to the database without requiring any explicit `update` operation. + +If on the other hand, you wish to make every `update` operation explicit, then you need to use what Hibernate ORM calls +a _stateless session_. + +In this case, you need to extend `WithId.AutoLong` (or any other ID class or provide your own ID), and implement +the `PanacheEntity.Stateless` and `PanacheRepository.Stateless` interfaces: + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import io.quarkus.hibernate.panache.WithId; +import jakarta.persistence.Entity; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import java.time.LocalDate; +import java.util.List; + +@Entity +public class Cat extends WithId.AutoLong implements PanacheEntity.Stateless { + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } + + public interface Repo extends PanacheRepository.Stateless { + @Find + Cat findByName(String name); + + @HQL("where breed = CUTE") + List findCute(); + + @HQL("delete from Cat where name = :name") + long deleteByName(String name); + + @HQL("delete from Cat where breed = HAIRLESS") + long deleteHairless(); + } +} +``` + +As you can see, your entity definition is exactly the same, besides the two interfaces. But now, your entities will +not be _managed_, so every update operation has to be explicit: + +```java +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import org.hibernate.StatelessSession; + +import java.time.LocalDate; +import java.util.List; + +public class Code { + + @Inject + Cat.Repo repo; + + @Transactional + public void method() { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Persist the cat + cat.insert(); + + // Make a change, we need to update it + cat.name = "Luckynou"; + cat.update(); + + // Find our cat + cat = repo.findByName("Luckynou"); + + // Find cute cats + List cuteCats = repo.findCute(); + + // Delete our cat + cat.delete(); + + // Delete queries + repo.deleteByName("Lucky"); + repo.deleteHairless(); + } +} +``` + +As you can see, the only differences with a managed entity are: + +- Any change to the entity instance fields must be explicitly pushed to the database by calling `update()` either on +the entity or its repository +- You need to call `insert()` instead of `persist()` to insert the entity in the database +- Your session will be of type `StatelessSession` instead of `Session`. + +But that's mostly it. Everything else stays the same, in particular for queries and how to obtain entities. + +== Let's get reactive + +You want to start using your entities in a reactive application? Let's start by importing Hibernate Reactive in your +`pom.xml` as well as a data source for your database: + +```xml + + + io.quarkus + quarkus-hibernate-reactive + + + + io.quarkus + quarkus-reactive-pg-client + + + + io.quarkus + quarkus-hibernate-reactive-panache-common + +``` + +Make sure you configure your reactive datasource in your `application.properties` (although you don't have to in DEV +mode: if you don't, a dev service will be provided for you): + +```properties +# FIXME +``` + +Now, in your code, the only differences with a regular managed session entity are: + +- Your entity implements `PanacheEntity.Reactive` +- Your repository extends `PanacheRepository.Reactive` +- All the operations return a `Uni` instead of `T`, which is the standard Mutiny reactive type + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import io.quarkus.hibernate.panache.WithId; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.Entity; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import java.time.LocalDate; +import java.util.List; + +@Entity +public class Cat extends WithId.AutoLong implements PanacheEntity.Reactive { + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } + + public interface Repo extends PanacheRepository.Reactive { + @Find + Uni findByName(String name); + + @HQL("where breed = CUTE") + Uni> findCute(); + + @HQL("delete from Cat where name = :name") + Uni deleteByName(String name); + + @HQL("delete from Cat where breed = HAIRLESS") + Uni deleteHairless(); + } +} +``` + +And now when you want to use your entity or its repository, besides using `@WithTransaction` in place of +`@Transactional`, you have to compose operations as you normally do in reactive code with Mutiny, but the operations are +exactly the same otherwise: + +```java +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; +import jakarta.inject.Inject; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public class Code { + + @Inject + Cat.Repo repo; + + @WithTransaction + public Uni method() { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Persist the cat + return cat.persist() + // Make a change, no need to update it + .invoke(() -> cat.name = "Luckynou") + // Find our cat + .chain(() -> repo.findByName("Luckynou")) + // Find cute cats + .chain((Cat foundCat) -> repo.findCute()) + // Delete our cat + .chain((List cuteCats) -> cat.delete()) + // Delete queries + .chain(v -> repo.deleteByName("Lucky")) + .chain((Integer deletedCount) -> repo.deleteHairless()) + .replaceWithVoid(); + } +} +``` + +== Reactive and stateless + +If you want to manage manually your entity changes, then you can use a stateless session by switching to +the `PanacheEntity.Reactive.Stateless` for your entity, and `PanacheRepository.Reactive.Stateless`: + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import io.quarkus.hibernate.panache.WithId; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.Entity; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import java.time.LocalDate; +import java.util.List; + +@Entity +public class Cat extends WithId.AutoLong implements PanacheEntity.Reactive.Stateless { + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } + + public interface Repo extends PanacheRepository.Reactive.Stateless { + @Find + Uni findByName(String name); + + @HQL("where breed = CUTE") + Uni> findCute(); + + @HQL("delete from Cat where name = :name") + Uni deleteByName(String name); + + @HQL("delete from Cat where breed = HAIRLESS") + Uni deleteHairless(); + } +} +``` + +And for your usage code, the only change is the standard use of manually calling `update()`, and using `insert()` +instead of `persist()`: + +```java +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; +import jakarta.inject.Inject; + +import java.time.LocalDate; +import java.util.List; + +public class Code { + + @Inject + Cat.Repo repo; + + @WithTransaction + public Uni method() { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Persist the cat + return cat.insert() + // Make a change, we need to update it + .chain(() -> { + cat.name = "Luckynou"; + return cat.update(); + }) + // Find our cat + .chain(() -> repo.findByName("Luckynou")) + // Find cute cats + .chain((Cat foundCat) -> repo.findCute()) + // Delete our cat + .chain((List cuteCats) -> cat.delete()) + // Delete queries + .chain(v -> repo.deleteByName("Lucky")) + .chain((Integer deletedCount) -> repo.deleteHairless()) + .replaceWithVoid(); + } +} +``` + +== Combining blocking, reactive, managed, stateless code + +Let's say you want to use an entity in a blocking managed session code, but also in a reactive stateless code: this +is possible no matter which supertype of your entity you picked. You should pick the supertype that represents your +majority of use-cases, but any one you pick, you can always obtain the alternative operations by using the +`.statelessReactive()` method on the entity: + +```java +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; +import jakarta.transaction.Transactional; + +import java.time.LocalDate; + +public class Code { + + @Transactional + public void blockingManagedMethod() { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Persist the cat + cat.persist(); + + // Make a change, no need to update it + cat.name = "Luckynou"; + } + + @WithTransaction + public Uni reactiveStatelessMethod(Long catId) { + Cat cat = new Cat(); + cat.name = "Lucky"; + cat.birth = LocalDate.of(2015, 01, 12); + cat.breed = Cat.Breed.CUTE; + + // Insert the cat + return cat.statelessReactive().insert() + .chain(() -> { + // Make a change, we need to update it + cat.name = "Luckynou"; + return cat.statelessReactive().update(); + }); + + } +} +``` + +All the alternative entity operations are available from these methods: + +[cols="1,1,1"] +|=== +|Session type|Entity accessor|Equivalent entity type + +|`Session` +|`.managedBlocking()` +|`PanacheEntity.Managed` + +|`StatelessSession` +|`.statelessBlocking()` +|`PanacheEntity.Stateless` + +|`Mutiny.Session` +|`.statelessManaged()` +|`PanacheEntity.Reactive` + +|`Mutiny.StatelessSession` +|`.statelessReactive()` +|`PanacheEntity.Reactive.Stateless` + +|=== + +Similarly, for all the repository operations, you can obtain alternate repositories for your entity from the +generated metamodel accessors, or by injecting them with `@Inject`: + +[cols="1,1,1"] +|=== +|Session type|Metamodel accessor|Repository type + +|`Session` +|`Cat_.managedBlocking()` +|`PanacheRepository.Managed` + +|`StatelessSession` +|`Cat_.statelessBlocking()` +|`PanacheRepository.Stateless` + +|`Mutiny.Session` +|`Cat_.statelessManaged()` +|`PanacheRepository.Reactive` + +|`Mutiny.StatelessSession` +|`Cat_.statelessReactive()` +|`PanacheRepository.Reactive.Stateless` + +|=== + +But you can also define any number of repositories for your custom operations within your entity: + +```java +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import io.smallrye.mutiny.Uni; +import jakarta.persistence.Entity; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import java.time.LocalDate; +import java.util.List; + +@Entity +public class Cat extends PanacheEntity { + public String name; + public LocalDate birth; + public Breed breed; + + public enum Breed { + CUTE, HAIRLESS; + } + + public interface Repo extends PanacheRepository { + @Find + Cat findByName(String name); + } + + public interface TheOtherRepo extends PanacheRepository.Reactive.Stateless { + @Find + Uni findByName(String name); + } +} +``` + +And then you can inject both types of repositories: + +```java +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + +public class Code { + + @Inject + Cat.Repo repo; + + @Inject + Cat.TheOtherRepo theOtherRepo; + + @Transactional + public void blockingManagedMethod() { + Cat cat = repo.findByName("Lucky"); + } + + @WithTransaction + public Uni reactiveStatelessMethod() { + return theOtherRepo.findByName("Lucky") + .invoke(cat -> System.err.println(cat)) + .replaceWithVoid(); + } +} +``` + +NOTE: you can of course also use the generated metamodel accessors instead of injecting these repositories, with +`Cat_.repo()` and `Cat_.theOtherRepo()`. + +== Recap + +Here is a table listing all the options you have for your entity and repository supertypes, depending on which +model of operation you prefer, and which ID type you want. Remember you don't have to extend `WithId` if you +define your own ID entity field: + +[cols="1,1,1,1,1"] +|=== +|Session type|ID type|Entity superclass|Entity superinterface|Repository supertype + +|`Session` (managed, blocking) +|`Long` +|`PanacheEntity` +| +|`PanacheRepository` + +|`Session` (managed, blocking) +|`Id` +|`WithId` +|`PanacheEntity.Managed` +|`PanacheRepository.Managed` + +|`StatelessSession` (stateless, blocking) +|`Id` +|`WithId` +|`PanacheEntity.Stateless` +|`PanacheRepository.Stateless` + +|`Mutiny.Session` (managed, reactive) +|`Id` +|`WithId` +|`PanacheEntity.Reactive` +|`PanacheRepository.Reactive` + + +|`Mutiny.StatelessSession` (stateless, reactive) +|`Id` +|`WithId` +|`PanacheEntity.Reactive.Stateless` +|`PanacheRepository.Reactive.Stateless` + +|=== + +== Startup code + +For blocking operations, invoking startup code is pretty trivial: + +```java +import io.quarkus.runtime.Startup; +import jakarta.transaction.Transactional; + +public class OnStart { + @Startup + @Transactional + public void startupMethod() { + Cat_.repo().deleteAll(); + } +} +``` + +For reactive operations, it is a little more complex at the moment, because you need a regular `@Startup` method, +from which you invoke your reactive `@WithTransaction` method from within a call to +`VertxContextSupport.subscribeAndAwait` (don't worry, we are working on making this easier): + +```java +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.quarkus.runtime.Startup; +import io.quarkus.vertx.VertxContextSupport; +import io.smallrye.mutiny.Uni; + +public class OnStart { + @Startup + public void start() throws Throwable { + VertxContextSupport.subscribeAndAwait(() -> runit()); + } + + @WithTransaction + Uni runit(){ + return Cat_.repo().deleteAll().replaceWithVoid(); + } +} +``` + +== Jakarta Data + +TODO + +== HQL, JDQL, Panache-QL + +TODO: explain them here diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractJpaOperations.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractJpaOperations.java index 055c9365fdb27..023d584de0e99 100644 --- a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractJpaOperations.java +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractJpaOperations.java @@ -6,12 +6,11 @@ import java.util.Map.Entry; import java.util.stream.Stream; -import jakarta.persistence.EntityManager; -import jakarta.persistence.LockModeType; import jakarta.transaction.SystemException; import jakarta.transaction.TransactionManager; import org.hibernate.Session; +import org.hibernate.SharedSessionContract; import org.hibernate.query.CommonQueryContract; import org.hibernate.query.MutationQuery; import org.hibernate.query.SelectionQuery; @@ -24,7 +23,11 @@ import io.quarkus.panache.common.Sort; import io.quarkus.panache.hibernate.common.runtime.PanacheJpaUtil; -public abstract class AbstractJpaOperations { +public abstract class AbstractJpaOperations { + + // + // Static + private static volatile Map entityToPersistenceUnit = Collections.emptyMap(); private static volatile boolean entityToPersistenceUnitIsIncomplete = true; @@ -33,28 +36,46 @@ public static void setEntityToPersistenceUnit(Map map, boolean i entityToPersistenceUnitIsIncomplete = incomplete; } - protected abstract PanacheQueryType createPanacheQuery(Session session, String query, String originalQuery, String orderBy, + private static volatile Map, Class> repositoryClassToEntityClass = Collections.emptyMap(); + + public static void setRepositoryClassesToEntityClasses(Map, Class> map) { + repositoryClassToEntityClass = Collections.unmodifiableMap(map); + } + + public static Class getRepositoryEntityClass( + // FIXME: if we move this to JpaOperations we can add a type constraint on the repo class + Class repositoryImplementationClass) { + Class ret = repositoryClassToEntityClass.get(repositoryImplementationClass); + if (ret == null) { + throw new RuntimeException("Your repository class " + repositoryImplementationClass + + " was not properly detected and assigned an entity type"); + } + return (Class) ret; + } + + // + // Instance + + private Class sessionType; + + protected AbstractJpaOperations(Class sessionType) { + this.sessionType = sessionType; + } + + protected abstract PanacheQueryType createPanacheQuery(SessionType session, String query, String originalQuery, + String orderBy, Object paramsArrayOrMap); public abstract List list(PanacheQueryType query); public abstract Stream stream(PanacheQueryType query); - /** - * Returns the {@link EntityManager} for the given {@link Class entity} - * - * @return {@link EntityManager} - */ - public EntityManager getEntityManager(Class clazz) { - return getSession(clazz); - } - /** * Returns the {@link Session} for the given {@link Class entity} * * @return {@link Session} */ - public Session getSession(Class clazz) { + public SessionType getSession(Class clazz) { String clazzName = clazz.getName(); String persistentUnitName = entityToPersistenceUnit.get(clazzName); if (persistentUnitName == null) { @@ -63,7 +84,7 @@ public Session getSession(Class clazz) { // so we'll just return the default PU and hope for the best. // The error will be thrown later by Hibernate ORM if necessary; // it will be a bit less clear, but this is an edge case. - Session session = getSession(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME); + SessionType session = getSession(PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME); if (session != null) { return session; } @@ -76,71 +97,23 @@ public Session getSession(Class clazz) { return getSession(persistentUnitName); } - public Session getSession(String persistentUnitName) { + public SessionType getSession(String persistentUnitName) { ArcContainer arcContainer = Arc.container(); if (persistentUnitName == null || PersistenceUnitUtil.isDefaultPersistenceUnit(persistentUnitName)) { - return arcContainer.instance(Session.class).get(); + return arcContainer.instance(sessionType).get(); } else { - return arcContainer.instance(Session.class, + return arcContainer.instance(sessionType, new PersistenceUnit.PersistenceUnitLiteral(persistentUnitName)) .get(); } } - public Session getSession() { + public SessionType getSession() { return getSession(DEFAULT_PERSISTENCE_UNIT_NAME); } // // Instance methods - public void persist(Object entity) { - Session session = getSession(entity.getClass()); - persist(session, entity); - } - - public void persist(Session session, Object entity) { - if (!session.contains(entity)) { - session.persist(entity); - } - } - - public void persist(Iterable entities) { - for (Object entity : entities) { - persist(getSession(entity.getClass()), entity); - } - } - - public void persist(Object firstEntity, Object... entities) { - persist(firstEntity); - for (Object entity : entities) { - persist(entity); - } - } - - public void persist(Stream entities) { - entities.forEach(entity -> persist(entity)); - } - - public void delete(Object entity) { - Session session = getSession(entity.getClass()); - session.remove(session.contains(entity) ? entity : session.getReference(entity)); - } - - public boolean isPersistent(Object entity) { - return getSession(entity.getClass()).contains(entity); - } - - public void flush() { - getSession().flush(); - } - - public void flush(Object entity) { - getSession(entity.getClass()).flush(); - } - - public void flush(Class clazz) { - getSession(clazz).flush(); - } // // Private stuff @@ -177,32 +150,12 @@ public int paramCount(Map params) { // // Queries - public Object findById(Class entityClass, Object id) { - return getSession(entityClass).find(entityClass, id); - } - - public Object findById(Class entityClass, Object id, LockModeType lockModeType) { - return getSession(entityClass).find(entityClass, id, lockModeType); - } - - public Optional findByIdOptional(Class entityClass, Object id) { - return Optional.ofNullable(findById(entityClass, id)); - } - - public Optional findByIdOptional(Class entityClass, Object id, LockModeType lockModeType) { - return Optional.ofNullable(findById(entityClass, id, lockModeType)); - } - - public List findByIds(Class entityClass, List ids) { - return getSession(entityClass).findMultiple(entityClass, ids); - } - public PanacheQueryType find(Class entityClass, String query, Object... params) { return find(entityClass, query, null, params); } public PanacheQueryType find(Class entityClass, String panacheQuery, Sort sort, Object... params) { - Session session = getSession(entityClass); + SessionType session = getSession(entityClass); if (PanacheJpaUtil.isNamedQuery(panacheQuery)) { String namedQuery = panacheQuery.substring(1); if (sort != null) { @@ -223,7 +176,7 @@ public PanacheQueryType find(Class entityClass, String panacheQuery, Map entityClass, String panacheQuery, Sort sort, Map params) { - Session session = getSession(entityClass); + SessionType session = getSession(entityClass); if (PanacheJpaUtil.isNamedQuery(panacheQuery)) { String namedQuery = panacheQuery.substring(1); if (sort != null) { @@ -297,13 +250,13 @@ public Stream stream(Class entityClass, String panacheQuery, Sort sort, Pa public PanacheQueryType findAll(Class entityClass) { String query = "FROM " + PanacheJpaUtil.getEntityName(entityClass); - Session session = getSession(entityClass); + SessionType session = getSession(entityClass); return createPanacheQuery(session, query, null, null, null); } public PanacheQueryType findAll(Class entityClass, Sort sort) { String query = "FROM " + PanacheJpaUtil.getEntityName(entityClass); - Session session = getSession(entityClass); + SessionType session = getSession(entityClass); return createPanacheQuery(session, query, null, PanacheJpaUtil.toOrderBy(sort), null); } @@ -402,17 +355,6 @@ public long deleteAll(Class entityClass) { .executeUpdate(); } - public boolean deleteById(Class entityClass, Object id) { - // Impl note : we load the entity then delete it because it's the only implementation generic enough for any model, - // and correct in all cases (composite key, graph of entities, ...). HQL cannot be directly used for these reasons. - Object entity = findById(entityClass, id); - if (entity == null) { - return false; - } - getSession(entityClass).remove(entity); - return true; - } - public long delete(Class entityClass, String panacheQuery, Object... params) { if (PanacheJpaUtil.isNamedQuery(panacheQuery)) { return bindParameters(extractNamedMutationQuery(entityClass, panacheQuery), params).executeUpdate(); diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractManagedJpaOperations.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractManagedJpaOperations.java new file mode 100644 index 0000000000000..0ba25196686d8 --- /dev/null +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractManagedJpaOperations.java @@ -0,0 +1,114 @@ +package io.quarkus.hibernate.orm.panache.common.runtime; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; + +import org.hibernate.Session; + +public abstract class AbstractManagedJpaOperations extends AbstractJpaOperations { + + protected AbstractManagedJpaOperations() { + super(Session.class); + } + + /** + * Returns the {@link EntityManager} for the given {@link Class entity} + * + * @return {@link EntityManager} + */ + public EntityManager getEntityManager(Class clazz) { + return getSession(clazz); + } + + // This is here for binary compatibility for callers who expect Session as a return type + @Override + public Session getSession(Class clazz) { + return super.getSession(clazz); + } + + public void persist(Object entity) { + Session session = getSession(entity.getClass()); + persist(session, entity); + } + + public void persist(Session session, Object entity) { + if (!session.contains(entity)) { + session.persist(entity); + } + } + + public void persist(Iterable entities) { + for (Object entity : entities) { + persist(getSession(entity.getClass()), entity); + } + } + + public void persist(Object firstEntity, Object... entities) { + persist(firstEntity); + for (Object entity : entities) { + persist(entity); + } + } + + public void persist(Stream entities) { + entities.forEach(entity -> persist(entity)); + } + + public void delete(Object entity) { + Session session = getSession(entity.getClass()); + session.remove(session.contains(entity) ? entity : session.getReference(entity)); + } + + public boolean isPersistent(Object entity) { + return getSession(entity.getClass()).contains(entity); + } + + public void flush() { + getSession().flush(); + } + + public void flush(Object entity) { + getSession(entity.getClass()).flush(); + } + + public void flush(Class clazz) { + getSession(clazz).flush(); + } + + // Query methods + + public List findByIds(Class klass, List ids) { + return getSession(klass).findMultiple(klass, ids); + } + + public Object findById(Class entityClass, Object id) { + return getSession(entityClass).find(entityClass, id); + } + + public Object findById(Class entityClass, Object id, LockModeType lockModeType) { + return getSession(entityClass).find(entityClass, id, lockModeType); + } + + public Optional findByIdOptional(Class entityClass, Object id) { + return Optional.ofNullable(findById(entityClass, id)); + } + + public Optional findByIdOptional(Class entityClass, Object id, LockModeType lockModeType) { + return Optional.ofNullable(findById(entityClass, id, lockModeType)); + } + + public boolean deleteById(Class entityClass, Object id) { + // Impl note : we load the entity then delete it because it's the only implementation generic enough for any model, + // and correct in all cases (composite key, graph of entities, ...). HQL cannot be directly used for these reasons. + Object entity = findById(entityClass, id); + if (entity == null) { + return false; + } + getSession(entityClass).remove(entity); + return true; + } +} diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractStatelessJpaOperations.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractStatelessJpaOperations.java new file mode 100644 index 0000000000000..01cb3b6db43b7 --- /dev/null +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/AbstractStatelessJpaOperations.java @@ -0,0 +1,92 @@ +package io.quarkus.hibernate.orm.panache.common.runtime; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.LockMode; +import org.hibernate.StatelessSession; + +public abstract class AbstractStatelessJpaOperations + extends AbstractJpaOperations { + + protected AbstractStatelessJpaOperations() { + super(StatelessSession.class); + } + + public void insert(Object entity) { + StatelessSession session = getSession(entity.getClass()); + insert(session, entity); + } + + public void insert(StatelessSession session, Object entity) { + session.insert(entity); + } + + public void insert(Iterable entities) { + for (Object entity : entities) { + insert(getSession(entity.getClass()), entity); + } + } + + public void insert(Object firstEntity, Object... entities) { + insert(firstEntity); + for (Object entity : entities) { + insert(entity); + } + } + + public void insert(Stream entities) { + entities.forEach(entity -> insert(entity)); + } + + public void update(Object entity) { + StatelessSession session = getSession(entity.getClass()); + update(session, entity); + } + + public void update(StatelessSession session, Object entity) { + session.update(entity); + } + + public void delete(Object entity) { + StatelessSession session = getSession(entity.getClass()); + session.delete(entity); + } + + // Query methods + + public List findByIds(Class klass, List ids) { + return getSession(klass).getMultiple(klass, ids); + } + + public Object findById(Class entityClass, Object id) { + return getSession(entityClass).get(entityClass, id); + } + + public Object findById(Class entityClass, Object id, LockModeType lockModeType) { + return getSession(entityClass).get(entityClass, id, LockMode.fromJpaLockMode(lockModeType)); + } + + public Optional findByIdOptional(Class entityClass, Object id) { + return Optional.ofNullable(findById(entityClass, id)); + } + + public Optional findByIdOptional(Class entityClass, Object id, LockModeType lockModeType) { + return Optional.ofNullable(findById(entityClass, id, lockModeType)); + } + + public boolean deleteById(Class entityClass, Object id) { + // FIXME: not sure this is true for stateless sessions, ask Yoann + // Impl note : we load the entity then delete it because it's the only implementation generic enough for any model, + // and correct in all cases (composite key, graph of entities, ...). HQL cannot be directly used for these reasons. + Object entity = findById(entityClass, id); + if (entity == null) { + return false; + } + getSession(entityClass).delete(entity); + return true; + } +} diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java index 98c61ed7f3e33..261e4d6d37546 100644 --- a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java @@ -16,7 +16,7 @@ import jakarta.persistence.LockModeType; import org.hibernate.Filter; -import org.hibernate.Session; +import org.hibernate.SharedSessionContract; import org.hibernate.query.SelectionQuery; import org.hibernate.query.spi.SqmQuery; @@ -57,7 +57,7 @@ public void close() { */ protected String customCountQueryForSpring; private String orderBy; - private Session session; + private SharedSessionContract session; private Page page; private Long count; @@ -70,7 +70,7 @@ public void close() { private Map> filters; private Class projectionType; - public CommonPanacheQueryImpl(Session session, String query, String originalQuery, String orderBy, + public CommonPanacheQueryImpl(SharedSessionContract session, String query, String originalQuery, String orderBy, Object paramsArrayOrMap) { this.session = session; this.query = query; diff --git a/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/runtime/KotlinJpaOperations.kt b/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/runtime/KotlinJpaOperations.kt index d50184e1b2c01..6b851576dcf3b 100644 --- a/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/runtime/KotlinJpaOperations.kt +++ b/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/runtime/KotlinJpaOperations.kt @@ -1,9 +1,9 @@ package io.quarkus.hibernate.orm.panache.kotlin.runtime -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations import org.hibernate.Session -class KotlinJpaOperations : AbstractJpaOperations>() { +class KotlinJpaOperations : AbstractManagedJpaOperations>() { override fun createPanacheQuery( session: Session, hqlQuery: String, diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java index f677e0d0b586e..d78780b9d7c89 100644 --- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java +++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java @@ -43,6 +43,7 @@ import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem; import io.quarkus.panache.hibernate.common.deployment.HibernateEnhancersRegisteredBuildItem; import io.quarkus.panache.hibernate.common.deployment.PanacheJpaEntityOperationsEnhancer; +import io.quarkus.panache.hibernate.common.deployment.PanacheJpaRepositoryEnhancer; public final class PanacheHibernateResourceProcessor { @@ -163,5 +164,4 @@ ValidationPhaseBuildItem.ValidationErrorBuildItem validate(ValidationPhaseBuildI } return null; } - } diff --git a/extensions/panache/hibernate-orm-panache/runtime/pom.xml b/extensions/panache/hibernate-orm-panache/runtime/pom.xml index 54b0d96bc2f23..6c1d7031748e4 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/pom.xml +++ b/extensions/panache/hibernate-orm-panache/runtime/pom.xml @@ -28,6 +28,12 @@ io.quarkus quarkus-panache-common + + + jakarta.data + jakarta.data-api + true + jakarta.json.bind jakarta.json.bind-api diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java index d5d6dc6c41b28..7408df58f4dd1 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java @@ -124,7 +124,7 @@ default void flush() { */ @GenerateBridge(targetReturnTypeErased = true) default Entity findById(Id id) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -136,7 +136,7 @@ default Entity findById(Id id) { */ @GenerateBridge(targetReturnTypeErased = true) default Entity findById(Id id, LockModeType lockModeType) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -147,7 +147,7 @@ default Entity findById(Id id, LockModeType lockModeType) { */ @GenerateBridge default Optional findByIdOptional(Id id) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -158,7 +158,7 @@ default Optional findByIdOptional(Id id) { */ @GenerateBridge default Optional findByIdOptional(Id id, LockModeType lockModeType) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -187,7 +187,7 @@ default List findByIds(List ids) { */ @GenerateBridge default PanacheQuery find(String query, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -205,7 +205,7 @@ default PanacheQuery find(String query, Object... params) { */ @GenerateBridge default PanacheQuery find(String query, Sort sort, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -222,7 +222,7 @@ default PanacheQuery find(String query, Sort sort, Object... params) { */ @GenerateBridge default PanacheQuery find(String query, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -240,7 +240,7 @@ default PanacheQuery find(String query, Map params) { */ @GenerateBridge default PanacheQuery find(String query, Sort sort, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -257,7 +257,7 @@ default PanacheQuery find(String query, Sort sort, Map p */ @GenerateBridge default PanacheQuery find(String query, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -275,7 +275,7 @@ default PanacheQuery find(String query, Parameters params) { */ @GenerateBridge default PanacheQuery find(String query, Sort sort, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -288,7 +288,7 @@ default PanacheQuery find(String query, Sort sort, Parameters params) { */ @GenerateBridge default PanacheQuery findAll() { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -302,7 +302,7 @@ default PanacheQuery findAll() { */ @GenerateBridge default PanacheQuery findAll(Sort sort) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -320,7 +320,7 @@ default PanacheQuery findAll(Sort sort) { */ @GenerateBridge default List list(String query, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -339,7 +339,7 @@ default List list(String query, Object... params) { */ @GenerateBridge default List list(String query, Sort sort, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -357,7 +357,7 @@ default List list(String query, Sort sort, Object... params) { */ @GenerateBridge default List list(String query, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -376,7 +376,7 @@ default List list(String query, Map params) { */ @GenerateBridge default List list(String query, Sort sort, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -394,7 +394,7 @@ default List list(String query, Sort sort, Map params) { */ @GenerateBridge default List list(String query, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -413,7 +413,7 @@ default List list(String query, Parameters params) { */ @GenerateBridge default List list(String query, Sort sort, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -427,7 +427,7 @@ default List list(String query, Sort sort, Parameters params) { */ @GenerateBridge default List listAll() { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -442,7 +442,7 @@ default List listAll() { */ @GenerateBridge default List listAll(Sort sort) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -462,7 +462,7 @@ default List listAll(Sort sort) { */ @GenerateBridge default Stream stream(String query, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -483,7 +483,7 @@ default Stream stream(String query, Object... params) { */ @GenerateBridge default Stream stream(String query, Sort sort, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -503,7 +503,7 @@ default Stream stream(String query, Sort sort, Object... params) { */ @GenerateBridge default Stream stream(String query, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -524,7 +524,7 @@ default Stream stream(String query, Map params) { */ @GenerateBridge default Stream stream(String query, Sort sort, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -544,7 +544,7 @@ default Stream stream(String query, Sort sort, Map param */ @GenerateBridge default Stream stream(String query, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -565,7 +565,7 @@ default Stream stream(String query, Parameters params) { */ @GenerateBridge default Stream stream(String query, Sort sort, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -581,7 +581,7 @@ default Stream stream(String query, Sort sort, Parameters params) { */ @GenerateBridge default Stream streamAll(Sort sort) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -597,7 +597,7 @@ default Stream streamAll(Sort sort) { */ @GenerateBridge default Stream streamAll() { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -610,7 +610,7 @@ default Stream streamAll() { */ @GenerateBridge default long count() { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -625,7 +625,7 @@ default long count() { */ @GenerateBridge default long count(String query, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -640,7 +640,7 @@ default long count(String query, Object... params) { */ @GenerateBridge default long count(String query, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -655,7 +655,7 @@ default long count(String query, Map params) { */ @GenerateBridge default long count(String query, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -671,7 +671,7 @@ default long count(String query, Parameters params) { */ @GenerateBridge default long deleteAll() { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -682,7 +682,7 @@ default long deleteAll() { */ @GenerateBridge default boolean deleteById(Id id) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -700,7 +700,7 @@ default boolean deleteById(Id id) { */ @GenerateBridge default long delete(String query, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -718,7 +718,7 @@ default long delete(String query, Object... params) { */ @GenerateBridge default long delete(String query, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -736,7 +736,7 @@ default long delete(String query, Map params) { */ @GenerateBridge default long delete(String query, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -786,7 +786,7 @@ default void persist(Entity firstEntity, @SuppressWarnings("unchecked") Entity.. */ @GenerateBridge default int update(String query, Object... params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -800,7 +800,7 @@ default int update(String query, Object... params) { */ @GenerateBridge default int update(String query, Map params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } /** @@ -814,6 +814,6 @@ default int update(String query, Map params) { */ @GenerateBridge default int update(String query, Parameters params) { - throw INSTANCE.implementationInjectionMissing(); + throw implementationInjectionMissing(); } } diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/AdditionalJpaOperations.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/AdditionalJpaOperations.java index c88a526fa70cb..f03f74e15d781 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/AdditionalJpaOperations.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/AdditionalJpaOperations.java @@ -20,7 +20,7 @@ import org.hibernate.query.SelectionQuery; import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; import io.quarkus.panache.common.Parameters; import io.quarkus.panache.common.Sort; @@ -29,7 +29,7 @@ public class AdditionalJpaOperations { @SuppressWarnings("rawtypes") - public static PanacheQuery find(AbstractJpaOperations jpaOperations, Class entityClass, String query, + public static PanacheQuery find(AbstractManagedJpaOperations jpaOperations, Class entityClass, String query, String countQuery, Sort sort, Map params) { String findQuery = createFindQuery(entityClass, query, jpaOperations.paramCount(params)); Session session = jpaOperations.getSession(entityClass); @@ -38,13 +38,13 @@ public static PanacheQuery find(AbstractJpaOperations jpaOperations, Class return new CustomCountPanacheQuery(session, hibernateQuery, countQuery, params); } - public static PanacheQuery find(AbstractJpaOperations jpaOperations, Class entityClass, String query, + public static PanacheQuery find(AbstractManagedJpaOperations jpaOperations, Class entityClass, String query, String countQuery, Sort sort, Parameters parameters) { return find(jpaOperations, entityClass, query, countQuery, sort, parameters.map()); } @SuppressWarnings("rawtypes") - public static PanacheQuery find(AbstractJpaOperations jpaOperations, Class entityClass, String query, + public static PanacheQuery find(AbstractManagedJpaOperations jpaOperations, Class entityClass, String query, String countQuery, Sort sort, Object... params) { String findQuery = createFindQuery(entityClass, query, jpaOperations.paramCount(params)); Session session = jpaOperations.getSession(entityClass); @@ -53,7 +53,7 @@ public static PanacheQuery find(AbstractJpaOperations jpaOperations, Class return new CustomCountPanacheQuery(session, hibernateQuery, countQuery, params); } - public static long deleteAllWithCascade(AbstractJpaOperations jpaOperations, Class entityClass) { + public static long deleteAllWithCascade(AbstractManagedJpaOperations jpaOperations, Class entityClass) { Session session = jpaOperations.getSession(entityClass); //detecting the case where there are cascade-delete associations, and do the bulk delete query otherwise. if (deleteOnCascadeDetected(jpaOperations, entityClass)) { @@ -76,7 +76,7 @@ public static long deleteAllWithCascade(AbstractJpaOperations jpaOperations, * @param entityClass * @return true if cascading delete is needed. False otherwise */ - private static boolean deleteOnCascadeDetected(AbstractJpaOperations jpaOperations, Class entityClass) { + private static boolean deleteOnCascadeDetected(AbstractManagedJpaOperations jpaOperations, Class entityClass) { Session session = jpaOperations.getSession(entityClass); Metamodel metamodel = session.getMetamodel(); EntityType entity1 = metamodel.entity(entityClass); @@ -94,7 +94,7 @@ private static boolean deleteOnCascadeDetected(AbstractJpaOperations jpaOpera } - public static long deleteWithCascade(AbstractJpaOperations jpaOperations, + public static long deleteWithCascade(AbstractManagedJpaOperations jpaOperations, Class entityClass, String query, Object... params) { Session session = jpaOperations.getSession(entityClass); if (deleteOnCascadeDetected(jpaOperations, entityClass)) { @@ -109,7 +109,7 @@ public static long deleteWithCascade(AbstractJpaOperations long deleteWithCascade(AbstractJpaOperations jpaOperations, + public static long deleteWithCascade(AbstractManagedJpaOperations jpaOperations, Class entityClass, String query, Map params) { Session session = jpaOperations.getSession(entityClass); @@ -125,7 +125,7 @@ public static long deleteWithCascade(AbstractJpaOperations jpaOperations, Class entityClass, String query, + public static long deleteWithCascade(AbstractManagedJpaOperations jpaOperations, Class entityClass, String query, Parameters params) { return deleteWithCascade(jpaOperations, entityClass, query, params.map()); } diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java index 352948742fc4d..d7255512df8e2 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaOperations.java @@ -5,9 +5,9 @@ import org.hibernate.Session; -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; -public class JpaOperations extends AbstractJpaOperations> { +public class JpaOperations extends AbstractManagedJpaOperations> { /** * Provides the default implementations for quarkus to wire up. Should not be used by third party developers. */ diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaStatelessOperations.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaStatelessOperations.java new file mode 100644 index 0000000000000..f0080546f7577 --- /dev/null +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/JpaStatelessOperations.java @@ -0,0 +1,33 @@ +package io.quarkus.hibernate.orm.panache.runtime; + +import java.util.List; +import java.util.stream.Stream; + +import org.hibernate.StatelessSession; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractStatelessJpaOperations; + +public class JpaStatelessOperations extends AbstractStatelessJpaOperations> { + /** + * Provides the default implementations for quarkus to wire up. Should not be used by third party developers. + */ + public static final JpaStatelessOperations INSTANCE = new JpaStatelessOperations(); + + @Override + protected PanacheQueryImpl createPanacheQuery(StatelessSession session, String query, String originalQuery, + String orderBy, + Object paramsArrayOrMap) { + return new PanacheQueryImpl<>(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + @Override + public List list(PanacheQueryImpl query) { + return query.list(); + } + + @Override + public Stream stream(PanacheQueryImpl query) { + return query.stream(); + } + +} diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/PanacheQueryImpl.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/PanacheQueryImpl.java index c8d79ad02feae..659897475478f 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/PanacheQueryImpl.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/runtime/PanacheQueryImpl.java @@ -8,7 +8,7 @@ import jakarta.persistence.LockModeType; -import org.hibernate.Session; +import org.hibernate.SharedSessionContract; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl; @@ -19,7 +19,8 @@ public class PanacheQueryImpl implements PanacheQuery { private CommonPanacheQueryImpl delegate; - PanacheQueryImpl(Session session, String query, String originalQuery, String orderBy, Object paramsArrayOrMap) { + PanacheQueryImpl(SharedSessionContract session, String query, String originalQuery, String orderBy, + Object paramsArrayOrMap) { this.delegate = new CommonPanacheQueryImpl<>(session, query, originalQuery, orderBy, paramsArrayOrMap); } diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/test/java/io/quarkus/hibernate/orm/panache/MetamodelTest.java b/extensions/panache/hibernate-orm-panache/runtime/src/test/java/io/quarkus/hibernate/orm/panache/MetamodelTest.java new file mode 100644 index 0000000000000..1e7202f365c00 --- /dev/null +++ b/extensions/panache/hibernate-orm-panache/runtime/src/test/java/io/quarkus/hibernate/orm/panache/MetamodelTest.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.orm.panache; + +public class MetamodelTest { + // this only tests that we generated the metamodel, no need to run anything. + public void test() { + String orm = PanacheEntity_.ID; + String jd = _PanacheEntity.ID; + } +} diff --git a/extensions/panache/hibernate-panache/TODO b/extensions/panache/hibernate-panache/TODO new file mode 100644 index 0000000000000..ce1c6e197c92d --- /dev/null +++ b/extensions/panache/hibernate-panache/TODO @@ -0,0 +1,9 @@ +- When using @Repository and Panache Repository supertypes, it complains about not having a primary entity on the repo + +- Ajouter un API pour renvoyer autre chose que le type de l'entité par une query, genre "select avg()". PanacheQuery.cast? Ou prendre le type de retour comme arg optionnel? + +- API +-- Test out PanacheEntity and PanacheRepository and nests +-- Perhaps PanacheEntitySwitcher and PanacheEntityMarker should be a single interface? +-- Perhaps split PanacheEntitySwitcher.managedBlocking() into .managed() and .blocking()? +--- That or make blocking the default diff --git a/extensions/panache/hibernate-panache/deployment/pom.xml b/extensions/panache/hibernate-panache/deployment/pom.xml new file mode 100644 index 0000000000000..db8c9b4927aaa --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/pom.xml @@ -0,0 +1,229 @@ + + + + quarkus-hibernate-panache-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-hibernate-panache-deployment + Quarkus - Hibernate with Panache - Deployment + + + vertx-reactive:postgresql://localhost:5431/hibernate_orm_test + jdbc:postgresql://localhost:5431/hibernate_orm_test + + + + + io.quarkus + quarkus-hibernate-orm-deployment + + + io.quarkus + quarkus-hibernate-orm-panache-common-deployment + + + io.quarkus + quarkus-hibernate-reactive-panache-common-deployment + true + + + io.quarkus + quarkus-panache-hibernate-common-deployment + + + io.quarkus + quarkus-hibernate-panache + + + org.ow2.asm + asm + + + org.ow2.asm + asm-tree + + + org.ow2.asm + asm-analysis + + + + + io.quarkus + quarkus-test-vertx + test + + + io.quarkus + quarkus-junit5-internal + test + + + io.quarkus + quarkus-jdbc-postgresql-deployment + test + + + io.quarkus + quarkus-reactive-pg-client-deployment + test + + + org.assertj + assertj-core + test + + + + + + + src/test/resources + true + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + default-testCompile + + + + org.hibernate.orm + hibernate-jpamodelgen + + + + + + + + maven-surefire-plugin + + true + + + + + + + + test-postgresql + + + test-containers + + + + + + maven-surefire-plugin + + false + + + + + + + + docker-postgresql + + + start-containers + + + + + + io.fabric8 + docker-maven-plugin + + + + ${postgres.image} + postgresql + + + hibernate_orm_test + hibernate_orm_test + hibernate_orm_test + + + 5431:5432 + + + + + (?s)ready to accept connections.*ready to accept connections + + + + + + true + + + + docker-start + process-test-classes + + stop + start + + + + docker-stop + post-integration-test + + stop + + + + + + org.codehaus.mojo + exec-maven-plugin + + + docker-prune + generate-resources + + exec + + + ${docker-prune.location} + + + + + + + + + + diff --git a/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/EntityToPersistenceUnitBuildItem.java b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/EntityToPersistenceUnitBuildItem.java new file mode 100644 index 0000000000000..3495c94cb9309 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/EntityToPersistenceUnitBuildItem.java @@ -0,0 +1,33 @@ +package io.quarkus.hibernate.panache.deployment; + +import io.quarkus.builder.item.MultiBuildItem; + +// FIXME: duplicate with ORM and probably HR +/** + * Used to record that a specific JPA entity is associated with a specific persistence unit + */ +public final class EntityToPersistenceUnitBuildItem extends MultiBuildItem { + + private final String entityClass; + private final String persistenceUnitName; + private final String reactivePersistenceUnitName; + + public EntityToPersistenceUnitBuildItem(String entityClass, String persistenceUnitName, + String reactivePersistenceUnitName) { + this.entityClass = entityClass; + this.persistenceUnitName = persistenceUnitName; + this.reactivePersistenceUnitName = reactivePersistenceUnitName; + } + + public String getEntityClass() { + return entityClass; + } + + public String getPersistenceUnitName() { + return persistenceUnitName; + } + + public String getReactivePersistenceUnitName() { + return reactivePersistenceUnitName; + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/EntityToPersistenceUnitUtil.java b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/EntityToPersistenceUnitUtil.java new file mode 100644 index 0000000000000..60a8b0067b2bb --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/EntityToPersistenceUnitUtil.java @@ -0,0 +1,107 @@ +package io.quarkus.hibernate.panache.deployment; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; + +import io.quarkus.hibernate.orm.deployment.JpaModelPersistenceUnitMappingBuildItem; +import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; + +//FIXME: duplicate with ORM and probably HR +public final class EntityToPersistenceUnitUtil { + + @FunctionalInterface + public interface TriConsumer { + void consume(String entity, String persistenceUnit, String reactivePersistenceUnit); + } + + private EntityToPersistenceUnitUtil() { + } + + /** + * Given the candidate entities, return a map with the persistence unit that single contains them + * or throw an exception if any of the candidates is part of more than one persistence unit + */ + public static void determineEntityPersistenceUnits( + Optional jpaModelPersistenceUnitMapping, + List descriptors, + Set candidates, String source, + TriConsumer consumer) { + if (jpaModelPersistenceUnitMapping.isEmpty()) { + return; + } + Map result = new HashMap<>(); + Map> collectedEntityToPersistenceUnits = jpaModelPersistenceUnitMapping.get() + .getEntityToPersistenceUnits(); + + Map> descriptorsMap = new HashMap<>(); + for (PersistenceUnitDescriptorBuildItem descriptor : descriptors) { + descriptorsMap.computeIfAbsent(descriptor.getPersistenceUnitName(), k -> new ArrayList<>()); + descriptorsMap.get(descriptor.getPersistenceUnitName()).add(descriptor); + } + + Map> violatingEntities = new TreeMap<>(); + + for (Map.Entry> entry : collectedEntityToPersistenceUnits.entrySet()) { + String entityName = entry.getKey(); + Set selectedPersistenceUnits = entry.getValue(); + boolean isCandidate = candidates.contains(entityName); + + if (!isCandidate) { + continue; + } + + String persistenceUnit = null; + String reactivePersistenceUnit = null; + if (selectedPersistenceUnits.size() > 0) { + boolean error = false; + // collect at most one of blocking/reactive PU + for (String selectedPersistenceUnit : selectedPersistenceUnits) { + List matchingDescriptors = descriptorsMap.get(selectedPersistenceUnit); + if (matchingDescriptors == null) { + throw new IllegalStateException(String.format("Cannot find descriptor unit named %s for entity %s", + selectedPersistenceUnit, entityName)); + } + for (PersistenceUnitDescriptorBuildItem descriptor : matchingDescriptors) { + if (descriptor.isReactive()) { + if (reactivePersistenceUnit != null) { + error = true; + break; + } else { + reactivePersistenceUnit = selectedPersistenceUnit; + } + } else { + if (persistenceUnit != null) { + error = true; + break; + } else { + persistenceUnit = selectedPersistenceUnit; + } + } + } + } + if (error) { + violatingEntities.put(entityName, selectedPersistenceUnits); + } else if (persistenceUnit != null || reactivePersistenceUnit != null) { + // ignore the entity if it belongs to no PU, though I doubt that happens + consumer.consume(entityName, persistenceUnit, reactivePersistenceUnit); + } + } + } + + if (violatingEntities.size() > 0) { + StringBuilder message = new StringBuilder( + String.format("%s entities do not support being attached to several persistence units:\n", source)); + for (Map.Entry> violatingEntityEntry : violatingEntities + .entrySet()) { + message.append("\t- ").append(violatingEntityEntry.getKey()).append(" is attached to: ") + .append(String.join(",", violatingEntityEntry.getValue())); + throw new IllegalStateException(message.toString()); + } + } + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/PanacheEntityClassBuildItem.java b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/PanacheEntityClassBuildItem.java new file mode 100644 index 0000000000000..e9a01160b8f1f --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/PanacheEntityClassBuildItem.java @@ -0,0 +1,23 @@ +package io.quarkus.hibernate.panache.deployment; + +import org.jboss.jandex.ClassInfo; + +import io.quarkus.builder.item.MultiBuildItem; + +//FIXME: duplicate with ORM and probably HR +/** + * Represents a regular Panache entity class. + */ +public final class PanacheEntityClassBuildItem extends MultiBuildItem { + + private final ClassInfo entityClass; + + public PanacheEntityClassBuildItem(ClassInfo entityClass) { + this.entityClass = entityClass; + } + + public ClassInfo get() { + return entityClass; + } + +} diff --git a/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/PanacheHibernateResourceProcessor.java b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/PanacheHibernateResourceProcessor.java new file mode 100644 index 0000000000000..12a80c9dd85dd --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/main/java/io/quarkus/hibernate/panache/deployment/PanacheHibernateResourceProcessor.java @@ -0,0 +1,239 @@ +package io.quarkus.hibernate.panache.deployment; + +import static io.quarkus.hibernate.panache.deployment.EntityToPersistenceUnitUtil.determineEntityPersistenceUnits; + +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import org.hibernate.Session; +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.Type.Kind; + +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; +import io.quarkus.arc.deployment.ValidationPhaseBuildItem; +import io.quarkus.arc.deployment.staticmethods.InterceptedStaticMethodsTransformersRegisteredBuildItem; +import io.quarkus.builder.BuildException; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Consume; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.util.JandexUtil; +import io.quarkus.hibernate.orm.deployment.JpaModelPersistenceUnitMappingBuildItem; +import io.quarkus.hibernate.orm.deployment.PersistenceUnitDescriptorBuildItem; +import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem; +import io.quarkus.hibernate.panache.PanacheEntityMarker; +import io.quarkus.hibernate.panache.PanacheRepositoryQueries; +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingEntity; +import io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveEntity; +import io.quarkus.hibernate.panache.runtime.PanacheHibernateRecorder; +import io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingEntity; +import io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveEntity; +import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem; +import io.quarkus.panache.hibernate.common.deployment.HibernateEnhancersRegisteredBuildItem; + +public final class PanacheHibernateResourceProcessor { + + static final DotName DOTNAME_PANACHE_REPOSITORY_QUERIES = DotName.createSimple(PanacheRepositoryQueries.class.getName()); + + static final DotName DOTNAME_PANACHE_MANAGED_BLOCKING_ENTITY = DotName + .createSimple(PanacheManagedBlockingEntity.class.getName()); + static final DotName DOTNAME_PANACHE_MANAGED_REACTIVE_ENTITY = DotName + .createSimple(PanacheManagedReactiveEntity.class.getName()); + static final DotName DOTNAME_PANACHE_STATELESS_BLOCKING_ENTITY = DotName + .createSimple(PanacheStatelessBlockingEntity.class.getName()); + static final DotName DOTNAME_PANACHE_STATELESS_REACTIVE_ENTITY = DotName + .createSimple(PanacheStatelessReactiveEntity.class.getName()); + static final DotName DOTNAME_PANACHE_ENTITY_MARKER = DotName.createSimple(PanacheEntityMarker.class.getName()); + + private static final DotName DOTNAME_SESSION = DotName.createSimple(Session.class.getName()); + + private static final DotName DOTNAME_ID = DotName.createSimple(Id.class.getName()); + + @BuildStep + FeatureBuildItem featureBuildItem() { + return new FeatureBuildItem(Feature.HIBERNATE_ORM_PANACHE); // FIXME + } + + @BuildStep + void produceModel(BuildProducer models) { + // only useful for the index resolution: hibernate will register it to be transformed, but BuildMojo + // only transforms classes from the application jar, so we do our own transforming + models.produce( + new AdditionalJpaModelBuildItem(DOTNAME_PANACHE_MANAGED_BLOCKING_ENTITY.toString())); + models.produce( + new AdditionalJpaModelBuildItem(DOTNAME_PANACHE_MANAGED_REACTIVE_ENTITY.toString())); + models.produce( + new AdditionalJpaModelBuildItem(DOTNAME_PANACHE_STATELESS_BLOCKING_ENTITY.toString())); + models.produce( + new AdditionalJpaModelBuildItem(DOTNAME_PANACHE_STATELESS_REACTIVE_ENTITY.toString())); + } + + @BuildStep + UnremovableBeanBuildItem ensureBeanLookupAvailable() { + // FIXME: look for mutiny sessions too? + return new UnremovableBeanBuildItem(new UnremovableBeanBuildItem.BeanTypeExclusion(DOTNAME_SESSION)); + } + + @BuildStep + void collectEntityClasses(CombinedIndexBuildItem index, BuildProducer entityClasses) { + // NOTE: we don't skip abstract/generic entities because they still need accessors + for (ClassInfo panacheEntityBaseSubclass : index.getIndex().getAllKnownImplementations(DOTNAME_PANACHE_ENTITY_MARKER)) { + // FIXME: should we really skip PanacheEntity or all MappedSuperClass? + if (!panacheEntityBaseSubclass.name().equals(DOTNAME_PANACHE_MANAGED_BLOCKING_ENTITY) + && !panacheEntityBaseSubclass.name().equals(DOTNAME_PANACHE_MANAGED_REACTIVE_ENTITY) + && !panacheEntityBaseSubclass.name().equals(DOTNAME_PANACHE_STATELESS_BLOCKING_ENTITY) + && !panacheEntityBaseSubclass.name().equals(DOTNAME_PANACHE_STATELESS_REACTIVE_ENTITY)) { + entityClasses.produce(new PanacheEntityClassBuildItem(panacheEntityBaseSubclass)); + } + } + } + + @BuildStep + @Consume(HibernateEnhancersRegisteredBuildItem.class) + @Consume(InterceptedStaticMethodsTransformersRegisteredBuildItem.class) + void build( + CombinedIndexBuildItem index, + BuildProducer transformers, + List entityClasses, + Optional jpaModelPersistenceUnitMapping, + List descriptors, + List methodCustomizersBuildItems, + BuildProducer entityToPersistenceUnit) { + + Set panacheEntities = new HashSet<>(); + for (ClassInfo classInfo : index.getIndex().getAllKnownImplementations(DOTNAME_PANACHE_REPOSITORY_QUERIES)) { + // we don't want to add methods to abstract/generic entities/repositories: they get added to bottom types + // which can't be either + if (Modifier.isAbstract(classInfo.flags()) + || !classInfo.typeParameters().isEmpty()) + continue; + List typeParameters = JandexUtil + .resolveTypeParameters(classInfo.name(), DOTNAME_PANACHE_REPOSITORY_QUERIES, index.getIndex()); + // FIXME: this may be a Uni for reactive repos + panacheEntities.add(typeParameters.get(0).name().toString()); + } + + Set modelClasses = new HashSet<>(); + for (PanacheEntityClassBuildItem entityClass : entityClasses) { + String entityClassName = entityClass.get().name().toString(); + modelClasses.add(entityClassName); + } + + panacheEntities.addAll(modelClasses); + + determineEntityPersistenceUnits(jpaModelPersistenceUnitMapping, descriptors, panacheEntities, "Panache", (e, pu, + reactivePU) -> entityToPersistenceUnit.produce(new EntityToPersistenceUnitBuildItem(e, pu, reactivePU))); + } + + @BuildStep + @Record(ExecutionTime.STATIC_INIT) + void recordEntityToPersistenceUnit(Optional jpaModelPersistenceUnitMapping, + List items, + CombinedIndexBuildItem index, + PanacheHibernateRecorder recorder) throws ClassNotFoundException { + // PU + // FIXME: for now, this ignores the reactive PUs, but reactive PUs are not supported yet by Panache Reactive + Map map = new HashMap<>(); + for (EntityToPersistenceUnitBuildItem item : items) { + map.put(item.getEntityClass(), item.getPersistenceUnitName()); + } + recorder.setEntityToPersistenceUnit(map, + jpaModelPersistenceUnitMapping.map(JpaModelPersistenceUnitMappingBuildItem::isIncomplete) + // This happens if there is no persistence unit, in which case we definitely know this metadata is complete. + .orElse(false)); + // Panache 2 repos + Map, Class> repositoryClassesToEntityClasses = new HashMap<>(); + for (ClassInfo classInfo : index.getIndex().getAllKnownImplementations(DOTNAME_PANACHE_REPOSITORY_QUERIES)) { + // Only keep concrete classes + if (classInfo.isInterface() || classInfo.isAbstract()) { + continue; + } + List typeParameters = JandexUtil + .resolveTypeParameters(classInfo.name(), DOTNAME_PANACHE_REPOSITORY_QUERIES, index.getIndex()); + if (typeParameters.get(0).kind() == Kind.CLASS) { + String entityClassName = typeParameters.get(0).name().toString(); + Class entityClass = Class.forName(entityClassName, false, Thread.currentThread().getContextClassLoader()); + Class repositoryClass = Class.forName(classInfo.name().toString(), false, + Thread.currentThread().getContextClassLoader()); + repositoryClassesToEntityClasses.put(repositoryClass, entityClass); + } else { + throw new RuntimeException("Failed to find entity linked to repository: " + classInfo + ", it appears to be: " + + typeParameters.get(0) + " but we don't know what to do with it, it should be an entity type"); + } + } + recorder.setRepositoryClassesToEntityClasses(repositoryClassesToEntityClasses); + } + + @BuildStep + void makePanache2ReposUnremovable( + CombinedIndexBuildItem index, + BuildProducer unremovableBeanBuildItems) throws ClassNotFoundException { + Set unremovable = new HashSet<>(); + // Find Panache 2 repos, by listing every entity, to find query interfaces with no supertype + for (AnnotationInstance annotation : index.getIndex().getAnnotations(Entity.class)) { + AnnotationTarget target = annotation.target(); + if (target.kind() != AnnotationTarget.Kind.CLASS) { + continue; + } + ClassInfo entityClass = target.asClass(); + // find its member interfaces + for (DotName memberClassName : entityClass.memberClasses()) { + ClassInfo memberClass = index.getIndex().getClassByName(memberClassName); + if (!memberClass.isInterface()) { + continue; + } + for (ClassInfo implementingBean : index.getIndex().getAllKnownImplementations(memberClassName)) { + unremovable.add(implementingBean.name()); + } + } + } + // Now, some entities that do not explicitely add stateless/managed query repos will get some generated, so + // either we figure out who they are, or we blanket add them all + index.getIndex().getKnownClasses(); + for (ClassInfo classInfo : index.getIndex().getAllKnownImplementations(DOTNAME_PANACHE_REPOSITORY_QUERIES)) { + // Only keep concrete classes + if (classInfo.isInterface() || classInfo.isAbstract()) { + continue; + } + unremovable.add(classInfo.name()); + } + unremovableBeanBuildItems.produce(UnremovableBeanBuildItem.beanTypes(unremovable)); + } + + @BuildStep + ValidationPhaseBuildItem.ValidationErrorBuildItem validate(ValidationPhaseBuildItem validationPhase, + CombinedIndexBuildItem index) throws BuildException { + // we verify that no ID fields are defined (via @Id) when extending PanacheEntity + for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(DOTNAME_ID)) { + ClassInfo info = JandexUtil.getEnclosingClass(annotationInstance); + if (JandexUtil.isSubclassOf(index.getIndex(), info, DOTNAME_PANACHE_MANAGED_BLOCKING_ENTITY) + || JandexUtil.isSubclassOf(index.getIndex(), info, DOTNAME_PANACHE_MANAGED_REACTIVE_ENTITY) + || JandexUtil.isSubclassOf(index.getIndex(), info, DOTNAME_PANACHE_STATELESS_BLOCKING_ENTITY) + || JandexUtil.isSubclassOf(index.getIndex(), info, DOTNAME_PANACHE_STATELESS_REACTIVE_ENTITY)) { + BuildException be = new BuildException("You provide a JPA identifier via @Id inside '" + info.name() + + "' but one is already provided by PanacheEntity, " + + "your class should extend PanacheEntityBase instead, or use the id provided by PanacheEntity", + Collections.emptyList()); + return new ValidationPhaseBuildItem.ValidationErrorBuildItem(be); + } + } + return null; + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/FirstTest.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/FirstTest.java new file mode 100644 index 0000000000000..c4ea9f974bad0 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/FirstTest.java @@ -0,0 +1,114 @@ +package io.quarkus.hibernate.panache.deployment.test; + +import jakarta.transaction.Transactional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class FirstTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("application-test.properties", "application.properties") + .addClasses(MyEntity.class, MyEntity_.class, MyEntity_.ManagedBlockingQueries_.class)); + + @Transactional + void createOne() { + Assertions.assertEquals(0, MyEntity_.managedBlocking().count()); + + MyEntity entity = new MyEntity(); + entity.foo = "bar"; + entity.persist(); + + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + } + + @Transactional + void modifyOne() { + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + + MyEntity entity = MyEntity_.managedBlocking().listAll().get(0); + Assertions.assertEquals("bar", entity.foo); + entity.foo = "gee"; + + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + } + + @Transactional + void modifyOneCheck() { + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + + MyEntity entity = MyEntity_.managedBlocking().listAll().get(0); + Assertions.assertEquals("gee", entity.foo); + + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + } + + @Transactional + void modifyOneStatelessNoUpdate() { + Assertions.assertEquals(1, MyEntity_.statelessBlocking().count()); + + MyEntity entity = MyEntity_.statelessBlocking().listAll().get(0); + Assertions.assertEquals("gee", entity.foo); + // should be ignored: not managed and no update called + entity.foo = "fu"; + + Assertions.assertEquals(1, MyEntity_.statelessBlocking().count()); + } + + @Transactional + void modifyOneStateless() { + MyEntity_.statelessBlocking().listAll(); + + Assertions.assertEquals(1, MyEntity_.statelessBlocking().count()); + + MyEntity entity = MyEntity_.statelessBlocking().listAll().get(0); + // still the old value + Assertions.assertEquals("gee", entity.foo); + entity.foo = "fu"; + // make sure we call update + entity.statelessBlocking().update(); + + Assertions.assertEquals(1, MyEntity_.statelessBlocking().count()); + } + + @Transactional + void modifyOneStatelessCheck() { + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + + MyEntity entity = MyEntity_.managedBlocking().listAll().get(0); + Assertions.assertEquals("fu", entity.foo); + + Assertions.assertEquals(1, MyEntity_.managedBlocking().count()); + } + + @Transactional + void clear() { + MyEntity_.managedBlocking().deleteAll(); + } + + @Transactional + void runQueries() { + MyEntity_.managedBlocking().find("foo = 2"); + Assertions.assertEquals(1, MyEntity_.managedBlockingQueries().findFoos("fu").size()); + Assertions.assertEquals(1, MyEntity_.managedBlockingQueries().findFoosHQL("fu").size()); + Assertions.assertEquals(1, MyEntity_.managedBlockingQueries().findFoosFind("fu").size()); + } + + @Test + void testRepositories() { + clear(); + createOne(); + modifyOne(); + modifyOneCheck(); + modifyOneStatelessNoUpdate(); + modifyOneStateless(); + modifyOneStatelessCheck(); + runQueries(); + } + +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/MyEntity.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/MyEntity.java new file mode 100644 index 0000000000000..d02af50a3f4df --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/MyEntity.java @@ -0,0 +1,29 @@ +package io.quarkus.hibernate.panache.deployment.test; + +import java.util.List; + +import jakarta.persistence.Entity; + +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; + +@Entity +public class MyEntity extends PanacheEntity { + public String foo; + public String bar; + + interface ManagedBlockingQueries extends PanacheRepository { + default List findFoos(String val) { + return list("foo", val); + } + + @HQL("where foo = :val") + List findFoosHQL(String val); + + @Find + List findFoosFind(String foo); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/MyReactiveEntity.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/MyReactiveEntity.java new file mode 100644 index 0000000000000..92e90ce30e5d5 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/MyReactiveEntity.java @@ -0,0 +1,32 @@ +package io.quarkus.hibernate.panache.deployment.test; + +import java.util.List; + +import jakarta.persistence.Entity; + +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.PanacheRepository; +import io.quarkus.hibernate.panache.WithId; +import io.smallrye.mutiny.Uni; + +@Entity +public class MyReactiveEntity extends WithId.AutoLong implements PanacheEntity.Reactive { + + public String foo; + public String bar; + + interface ManagedReactiveQueries extends PanacheRepository.Reactive { + default Uni> findFoos(String val) { + return list("foo", val); + } + + @HQL("where foo = :val") + Uni> findFoosHQL(String val); + + @Find + Uni> findFoosFind(String foo); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/ReactiveTest.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/ReactiveTest.java new file mode 100644 index 0000000000000..0784d5a8762c5 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/ReactiveTest.java @@ -0,0 +1,133 @@ +package io.quarkus.hibernate.panache.deployment.test; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.vertx.RunOnVertxContext; +import io.quarkus.test.vertx.UniAsserter; +import io.smallrye.mutiny.Uni; + +public class ReactiveTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource("application-test.properties", "application.properties") + .addClasses(MyReactiveEntity.class, MyReactiveEntity_.class, + MyReactiveEntity_.ManagedReactiveQueries_.class)); + + @WithTransaction + Uni createOne() { + return MyReactiveEntity_.managedReactive().count() + .flatMap(count -> { + Assertions.assertEquals(0l, count); + MyReactiveEntity entity = new MyReactiveEntity(); + entity.foo = "bar"; + return entity.persist(); + }).flatMap(v -> MyReactiveEntity_.managedReactive().count()) + .onItem().invoke(count -> Assertions.assertEquals(1l, count)) + .replaceWithVoid(); + } + + @WithTransaction + Uni modifyOne() { + return MyReactiveEntity_.managedReactive().listAll() + .onItem().invoke(list -> { + Assertions.assertEquals(1, list.size()); + MyReactiveEntity entity = list.get(0); + Assertions.assertEquals("bar", entity.foo); + entity.foo = "gee"; + }) + .replaceWithVoid(); + } + + @WithTransaction + Uni modifyOneCheck() { + return MyReactiveEntity_.managedReactive().listAll() + .flatMap(list -> { + Assertions.assertEquals(1, list.size()); + MyReactiveEntity entity = list.get(0); + Assertions.assertEquals("gee", entity.foo); + return MyReactiveEntity_.managedReactive().count(); + }) + .replaceWithVoid(); + } + + @WithTransaction(stateless = true) + Uni modifyOneStatelessNoUpdate() { + return MyReactiveEntity_.statelessReactive().listAll() + .onItem().invoke(list -> { + Assertions.assertEquals(1, list.size()); + MyReactiveEntity entity = list.get(0); + Assertions.assertEquals("gee", entity.foo); + // should be ignored: not managed and no update called + entity.foo = "fu"; + }) + .replaceWithVoid(); + } + + @WithTransaction(stateless = true) + Uni modifyOneStateless() { + return MyReactiveEntity_.statelessReactive().listAll() + .flatMap(list -> { + Assertions.assertEquals(1, list.size()); + MyReactiveEntity entity = list.get(0); + // last update to fu should have been ignored + Assertions.assertEquals("gee", entity.foo); + entity.foo = "fu"; + // not ignored this time + return entity.statelessReactive().update(); + }) + .replaceWithVoid(); + } + + @WithTransaction(stateless = true) + Uni modifyOneStatelessCheck() { + return MyReactiveEntity_.statelessReactive().listAll() + .onItem().invoke(list -> { + Assertions.assertEquals(1, list.size()); + MyReactiveEntity entity = list.get(0); + Assertions.assertEquals("fu", entity.foo); + }) + .replaceWithVoid(); + } + + @WithTransaction + Uni clear() { + return MyReactiveEntity_.managedReactive().deleteAll().replaceWithVoid(); + } + + @WithTransaction + Uni runQueries() { + return MyReactiveEntity_.managedReactiveQueries().findFoos("fu") + .flatMap(list -> { + Assertions.assertEquals(1, list.size()); + return MyReactiveEntity_.managedReactiveQueries().findFoosHQL("fu"); + }) + .flatMap(list -> { + Assertions.assertEquals(1, list.size()); + return MyReactiveEntity_.managedReactiveQueries().findFoosFind("fu"); + }) + .onItem().invoke(list -> { + Assertions.assertEquals(1, list.size()); + }) + .replaceWithVoid(); + } + + @RunOnVertxContext + @Test + void testRepositories(UniAsserter asserter) { + asserter.execute(() -> clear()); + asserter.execute(() -> createOne()); + asserter.execute(() -> modifyOne()); + asserter.execute(() -> modifyOneCheck()); + asserter.execute(() -> modifyOneStatelessNoUpdate()); + asserter.execute(() -> modifyOneStateless()); + asserter.execute(() -> modifyOneStatelessCheck()); + asserter.execute(() -> runQueries()); + } + +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/Panache2Book.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/Panache2Book.java new file mode 100644 index 0000000000000..9230a1fa63250 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/Panache2Book.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package io.quarkus.hibernate.panache.deployment.test.processor.hr; + +import java.util.List; + +import jakarta.persistence.Entity; + +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.WithId; +import io.smallrye.mutiny.Uni; + +@Entity +public class Panache2Book extends WithId.AutoLong implements PanacheEntity.Reactive { + public @NaturalId String isbn; + public @NaturalId String title; + public @NaturalId String author; + public String text; + public int pages; + + public interface Queries { + @Find + Uni> findBook(String isbn); + + @HQL("WHERE isbn = :isbn") + Uni> hqlBook(String isbn); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/Panache2BookCustomId.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/Panache2BookCustomId.java new file mode 100644 index 0000000000000..fbb6ca96150b2 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/Panache2BookCustomId.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package io.quarkus.hibernate.panache.deployment.test.processor.hr; + +import java.util.List; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.processing.Find; + +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveRepositoryBase; +import io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveRepositoryBase; +import io.smallrye.mutiny.Uni; + +@Entity +public class Panache2BookCustomId implements PanacheEntity.Reactive { + public @NaturalId String isbn; + public @NaturalId String title; + public @NaturalId String author; + public String text; + public int pages; + public @Id String myid; + + public interface DoesThisWork { + @Find + Uni> findBook(String isbn); + } + + public interface ManagedQueries extends PanacheManagedReactiveRepositoryBase { + @Find + Uni> findBook(String isbn); + } + + public interface StatelessQueries extends PanacheStatelessReactiveRepositoryBase { + @Find + Uni> findBook(String isbn); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/QuarkusHrPanache2Test.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/QuarkusHrPanache2Test.java new file mode 100644 index 0000000000000..5ef92cb0dcdbc --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/hr/QuarkusHrPanache2Test.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package io.quarkus.hibernate.panache.deployment.test.processor.hr; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.hibernate.panache.WithId; + +public class QuarkusHrPanache2Test { + @Test + public void testPanacheEntityMetamodel() throws Exception { + // Panache entity + Class entityClass = Panache2Book_.class; + Assertions.assertNotNull(entityClass); + + // Make sure it has the proper supertype + Class superclass = entityClass.getSuperclass(); + if (superclass != null) { + Assertions.assertEquals(WithId.class.getName() + "_", superclass.getName()); + } + + // Nested repo accessor + Method method = entityClass.getDeclaredMethod("queries"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2Book.Queries.class, method.getReturnType()); + + // Predefined repo accessors + method = entityClass.getDeclaredMethod("managedReactive"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(entityClass.getName() + "$PanacheManagedReactiveRepository", method.getReturnType().getName()); + + method = entityClass.getDeclaredMethod("statelessReactive"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(entityClass.getName() + "$PanacheStatelessReactiveRepository", + method.getReturnType().getName()); + } + + @Test + public void testPanacheEntityCustomIdMetamodel() throws Exception { + // Panache entity + Class entityClass = Panache2BookCustomId_.class; + Assertions.assertNotNull(entityClass); + + // Nested repo accessor + Method method = entityClass.getDeclaredMethod("managedQueries"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.ManagedQueries.class, method.getReturnType()); + + // Nested repo accessor + method = entityClass.getDeclaredMethod("statelessQueries"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.StatelessQueries.class, method.getReturnType()); + + // Predefined repo accessors + method = entityClass.getDeclaredMethod("managedReactive"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.ManagedQueries.class, method.getReturnType()); + + method = entityClass.getDeclaredMethod("statelessReactive"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.StatelessQueries.class, method.getReturnType()); + + Class managedQueriesClass = Panache2BookCustomId_.ManagedQueries_.class; + Assertions.assertNotNull(managedQueriesClass); + // make sure it's a repository + Assertions.assertFalse(Modifier.isAbstract(managedQueriesClass.getModifiers())); + Class[] interfaces = managedQueriesClass.getInterfaces(); + Assertions.assertEquals(1, interfaces.length); + Assertions.assertEquals(Panache2BookCustomId.ManagedQueries.class.getName(), interfaces[0].getName()); + + Constructor constructor = managedQueriesClass.getConstructor(); + Assertions.assertNotNull(constructor); + + Class statelessQueriesClass = Panache2BookCustomId_.StatelessQueries_.class; + Assertions.assertNotNull(statelessQueriesClass); + // make sure it's a repository + Assertions.assertFalse(Modifier.isAbstract(statelessQueriesClass.getModifiers())); + interfaces = statelessQueriesClass.getInterfaces(); + Assertions.assertEquals(1, interfaces.length); + Assertions.assertEquals(Panache2BookCustomId.StatelessQueries.class.getName(), interfaces[0].getName()); + + constructor = statelessQueriesClass.getConstructor(); + Assertions.assertNotNull(constructor); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/Panache2Book.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/Panache2Book.java new file mode 100644 index 0000000000000..31fa0812f575c --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/Panache2Book.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package io.quarkus.hibernate.panache.deployment.test.processor.orm; + +import java.util.List; + +import jakarta.persistence.Entity; + +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; + +import io.quarkus.hibernate.panache.PanacheEntity; + +@Entity +public class Panache2Book extends PanacheEntity { + public @NaturalId String isbn; + public @NaturalId String title; + public @NaturalId String author; + public String text; + public int pages; + + public interface Queries { + @Find + List findBook(String isbn); + + @HQL("WHERE isbn = :isbn") + List hqlBook(String isbn); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/Panache2BookCustomId.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/Panache2BookCustomId.java new file mode 100644 index 0000000000000..b214c2788ed51 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/Panache2BookCustomId.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package io.quarkus.hibernate.panache.deployment.test.processor.orm; + +import java.util.List; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import org.hibernate.annotations.NaturalId; +import org.hibernate.annotations.processing.Find; + +import io.quarkus.hibernate.panache.PanacheEntity; +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingRepositoryBase; +import io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingRepositoryBase; + +@Entity +public class Panache2BookCustomId implements PanacheEntity.Managed { + public @NaturalId String isbn; + public @NaturalId String title; + public @NaturalId String author; + public String text; + public int pages; + public @Id String myid; + + public interface DoesThisWork { + @Find + List findBook(String isbn); + } + + public interface ManagedQueries extends PanacheManagedBlockingRepositoryBase { + @Find + List findBook(String isbn); + } + + public interface StatelessQueries extends PanacheStatelessBlockingRepositoryBase { + @Find + List findBook(String isbn); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/QuarkusOrmPanache2Test.java b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/QuarkusOrmPanache2Test.java new file mode 100644 index 0000000000000..c187ae63b5291 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/java/io/quarkus/hibernate/panache/deployment/test/processor/orm/QuarkusOrmPanache2Test.java @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package io.quarkus.hibernate.panache.deployment.test.processor.orm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.hibernate.panache.WithId; + +public class QuarkusOrmPanache2Test { + @Test + public void testPanacheEntityMetamodel() throws Exception { + // Panache entity + Class entityClass = Panache2Book_.class; + Assertions.assertNotNull(entityClass); + + // Make sure it has the proper supertype + Class superclass = entityClass.getSuperclass(); + if (superclass != null) { + Assertions.assertEquals(WithId.class.getName() + "_", superclass.getName()); + } + + // Nested repo accessor + Method method = entityClass.getDeclaredMethod("queries"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2Book.Queries.class, method.getReturnType()); + + // Predefined repo accessors + method = entityClass.getDeclaredMethod("managedBlocking"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(entityClass.getName() + "$PanacheManagedBlockingRepository", method.getReturnType().getName()); + + method = entityClass.getDeclaredMethod("statelessBlocking"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(entityClass.getName() + "$PanacheStatelessBlockingRepository", + method.getReturnType().getName()); + } + + @Test + public void testPanacheEntityCustomIdMetamodel() throws Exception { + // Panache entity + Class entityClass = Panache2BookCustomId_.class; + Assertions.assertNotNull(entityClass); + + // Nested repo accessor + Method method = entityClass.getDeclaredMethod("managedQueries"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.ManagedQueries.class, method.getReturnType()); + + // Nested repo accessor + method = entityClass.getDeclaredMethod("statelessQueries"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.StatelessQueries.class, method.getReturnType()); + + // Predefined repo accessors + method = entityClass.getDeclaredMethod("managedBlocking"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.ManagedQueries.class, method.getReturnType()); + + method = entityClass.getDeclaredMethod("statelessBlocking"); + Assertions.assertNotNull(method); + Assertions.assertTrue(Modifier.isStatic(method.getModifiers())); + Assertions.assertEquals(Panache2BookCustomId.StatelessQueries.class, method.getReturnType()); + + Class managedQueriesClass = Panache2BookCustomId_.ManagedQueries_.class; + Assertions.assertNotNull(managedQueriesClass); + // make sure it's a repository + Assertions.assertFalse(Modifier.isAbstract(managedQueriesClass.getModifiers())); + Class[] interfaces = managedQueriesClass.getInterfaces(); + Assertions.assertEquals(1, interfaces.length); + Assertions.assertEquals(Panache2BookCustomId.ManagedQueries.class.getName(), interfaces[0].getName()); + + Constructor constructor = managedQueriesClass.getConstructor(Session.class); + Assertions.assertNotNull(constructor); + + Class statelessQueriesClass = Panache2BookCustomId_.StatelessQueries_.class; + Assertions.assertNotNull(statelessQueriesClass); + // make sure it's a repository + Assertions.assertFalse(Modifier.isAbstract(statelessQueriesClass.getModifiers())); + interfaces = statelessQueriesClass.getInterfaces(); + Assertions.assertEquals(1, interfaces.length); + Assertions.assertEquals(Panache2BookCustomId.StatelessQueries.class.getName(), interfaces[0].getName()); + + constructor = statelessQueriesClass.getConstructor(StatelessSession.class); + Assertions.assertNotNull(constructor); + } +} diff --git a/extensions/panache/hibernate-panache/deployment/src/test/resources/application-test.properties b/extensions/panache/hibernate-panache/deployment/src/test/resources/application-test.properties new file mode 100644 index 0000000000000..5ce30d4ffbb92 --- /dev/null +++ b/extensions/panache/hibernate-panache/deployment/src/test/resources/application-test.properties @@ -0,0 +1,9 @@ +quarkus.datasource.reactive.url=${postgres.reactive.url} +quarkus.datasource.jdbc.url=${postgres.blocking.url} + +quarkus.datasource.db-kind=postgresql +quarkus.datasource.username=hibernate_orm_test +quarkus.datasource.password=hibernate_orm_test +quarkus.datasource.reactive=true +#quarkus.hibernate-orm.log.sql=true +quarkus.hibernate-orm.database.generation=drop-and-create diff --git a/extensions/panache/hibernate-panache/pom.xml b/extensions/panache/hibernate-panache/pom.xml new file mode 100644 index 0000000000000..66aa6224fa73b --- /dev/null +++ b/extensions/panache/hibernate-panache/pom.xml @@ -0,0 +1,22 @@ + + + + quarkus-panache-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-hibernate-panache-parent + Quarkus - Hibernate with Panache + pom + + deployment + runtime + + + + diff --git a/extensions/panache/hibernate-panache/runtime/pom.xml b/extensions/panache/hibernate-panache/runtime/pom.xml new file mode 100644 index 0000000000000..9e5bf68a2929c --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/pom.xml @@ -0,0 +1,96 @@ + + + + quarkus-hibernate-panache-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-hibernate-panache + Quarkus - Hibernate with Panache - Runtime + Simplify your persistence code for Hibernate ORM via the active record or the repository pattern + + + io.quarkus + quarkus-hibernate-orm + + + io.quarkus + quarkus-hibernate-orm-panache-common + + + io.quarkus + quarkus-hibernate-reactive-panache-common + true + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-panache-common + + + + jakarta.data + jakarta.data-api + true + + + jakarta.json.bind + jakarta.json.bind-api + true + + + io.quarkus + quarkus-jackson + true + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + + + + + maven-compiler-plugin + + + + org.hibernate.orm + hibernate-jpamodelgen + + + + + + io.quarkus + quarkus-extension-maven-plugin + + + + io.smallrye + jandex-maven-plugin + + + make-index + + jandex + + + + + + + diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/Panache.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/Panache.java new file mode 100644 index 0000000000000..a545abb82fa92 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/Panache.java @@ -0,0 +1,142 @@ +package io.quarkus.hibernate.panache; + +/** + * Utility class for Panache. + * + * @author Stéphane Épardaud + */ +public class Panache { + // FIXME + // /** + // * Returns the default {@link EntityManager} + // * + // * @return {@link EntityManager} + // */ + // public static EntityManager getEntityManager() { + // return JpaOperations.INSTANCE.getSession(); + // } + // + // /** + // * Returns the default {@link Session} + // * + // * @return {@link Session} + // */ + // public static Session getSession() { + // return JpaOperations.INSTANCE.getSession(); + // } + // + // /** + // * Returns the {@link EntityManager} for the given {@link Class entity} + // * + // * @param clazz the entity class corresponding to the entity manager persistence unit. + // * @return {@link EntityManager} + // */ + // public static EntityManager getEntityManager(Class clazz) { + // return JpaOperations.INSTANCE.getSession(clazz); + // } + // + // /** + // * Returns the {@link Session} for the given {@link Class entity} + // * + // * @param clazz the entity class corresponding to the session persistence unit. + // * @return {@link Session} + // */ + // public static Session getSession(Class clazz) { + // return JpaOperations.INSTANCE.getSession(clazz); + // } + // + // /** + // * Returns the {@link EntityManager} for the given persistence unit + // * + // * @param persistenceUnit the persistence unit for this entity manager. + // * @return {@link EntityManager} + // */ + // public static EntityManager getEntityManager(String persistenceUnit) { + // return JpaOperations.INSTANCE.getSession(persistenceUnit); + // } + // + // /** + // * Returns the {@link Session} for the given persistence unit + // * + // * @param persistenceUnit the persistence unit for this session. + // * @return {@link Session} + // */ + // public static Session getSession(String persistenceUnit) { + // return JpaOperations.INSTANCE.getSession(persistenceUnit); + // } + // + // /** + // * Returns the current {@link TransactionManager} + // * + // * @return the current {@link TransactionManager} + // */ + // public static TransactionManager getTransactionManager() { + // return AbstractJpaOperations.getTransactionManager(); + // } + // + // /** + // * Executes a database update operation and return the number of rows operated on. + // * + // * @param query a normal HQL query + // * @param params optional list of indexed parameters + // * @return the number of rows operated on. + // */ + // public static int executeUpdate(String query, Object... params) { + // return JpaOperations.INSTANCE.executeUpdate(query, params); + // } + // + // /** + // * Executes a database update operation and return the number of rows operated on. + // * + // * @param query a normal HQL query + // * @param params {@link Map} of named parameters + // * @return the number of rows operated on. + // */ + // public static int executeUpdate(String query, Map params) { + // return JpaOperations.INSTANCE.executeUpdate(query, params); + // } + // + // /** + // * Executes a database update operation and return the number of rows operated on. + // * + // * @param query a normal HQL query + // * @param params {@link Parameters} of named parameters + // * @return the number of rows operated on. + // */ + // public static int executeUpdate(String query, Parameters params) { + // return JpaOperations.INSTANCE.executeUpdate(query, params.map()); + // } + // + // /** + // * Marks the current transaction as "rollback-only", which means that it will not be + // * committed: it will be rolled back at the end of this transaction lifecycle. + // */ + // public static void setRollbackOnly() { + // AbstractJpaOperations.setRollbackOnly(); + // } + // + // /** + // * Flushes all pending changes to the database using the default entity manager. + // */ + // public static void flush() { + // getSession().flush(); + // } + // + // /** + // * Flushes all pending changes to the database using the entity manager for the given {@link Class entity} + // * + // * @param clazz the entity class corresponding to the entity manager persistence unit. + // */ + // public static void flush(Class clazz) { + // getSession(clazz).flush(); + // } + // + // /** + // * Flushes all pending changes to the database using the entity manager for the given persistence unit + // * + // * @param persistenceUnit the persistence unit for this entity manager. + // */ + // public static void flush(String persistenceUnit) { + // getSession(persistenceUnit).flush(); + // } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntity.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntity.java new file mode 100644 index 0000000000000..3b16005afd6fc --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntity.java @@ -0,0 +1,36 @@ +package io.quarkus.hibernate.panache; + +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingEntity; +import io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveEntity; +import io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingEntity; +import io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveEntity; + +/** + * This is just an alias for {@link PanacheManagedBlockingEntity} and {@link WithId.AutoLong} + * for people coming from Panache 1 + */ +public abstract class PanacheEntity extends WithId.AutoLong implements PanacheManagedBlockingEntity { + + /** + * Represents an entity with managed blocking operations. + */ + public interface Managed extends PanacheManagedBlockingEntity { + } + + /** + * Represents an entity with stateless blocking operations. + */ + public interface Stateless extends PanacheStatelessBlockingEntity { + } + + /** + * Represents an entity with managed reactive operations. + */ + public interface Reactive extends PanacheManagedReactiveEntity { + /** + * Represents an entity with stateless reactive operations. + */ + public interface Stateless extends PanacheStatelessReactiveEntity { + } + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntityBase.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntityBase.java new file mode 100644 index 0000000000000..2212b3ca1a66e --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntityBase.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.panache; + +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingEntity; + +/** + * This is just an alias for {@link PanacheManagedBlockingEntity} for people coming from Panache 1 + */ +public class PanacheEntityBase implements PanacheManagedBlockingEntity { +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntityMarker.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntityMarker.java new file mode 100644 index 0000000000000..58838c8864677 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheEntityMarker.java @@ -0,0 +1,170 @@ +package io.quarkus.hibernate.panache; + +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingEntity; +import io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveEntity; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingEntity; +import io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveEntity; +import io.smallrye.mutiny.Uni; + +public interface PanacheEntityMarker { + default PanacheManagedBlockingEntity managedBlocking() { + if (this instanceof PanacheManagedBlockingEntity) { + return (PanacheManagedBlockingEntity) this; + } else { + return new PanacheManagedBlockingEntityOperationsImpl(this); + } + } + + default PanacheManagedReactiveEntity managedReactive() { + if (this instanceof PanacheManagedReactiveEntity) { + return (PanacheManagedReactiveEntity) this; + } else { + return new PanacheManagedReactiveEntityOperationsImpl(this); + } + } + + default PanacheStatelessReactiveEntity statelessReactive() { + if (this instanceof PanacheStatelessReactiveEntity) { + return (PanacheStatelessReactiveEntity) this; + } else { + return new PanacheStatelessReactiveEntityOperationsImpl(this); + } + } + + default PanacheStatelessBlockingEntity statelessBlocking() { + if (this instanceof PanacheStatelessBlockingEntity) { + return (PanacheStatelessBlockingEntity) this; + } else { + return new PanacheStatelessBlockingEntityOperationsImpl(this); + } + } + + // FIXME: move to runtime + static class PanacheManagedBlockingEntityOperationsImpl implements PanacheManagedBlockingEntity { + + private Object entity; + + public PanacheManagedBlockingEntityOperationsImpl(Object entity) { + this.entity = entity; + } + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingManaged(); + } + + @Override + public Void persist() { + return operations().persist(entity); + } + + @Override + public Void persistAndFlush() { + return operations().persistAndFlush(entity); + } + + @Override + public Void delete() { + return operations().delete(entity); + } + + @Override + public Boolean isPersistent() { + return operations().isPersistent(entity); + } + } + + // FIXME: move to runtime + static class PanacheManagedReactiveEntityOperationsImpl implements PanacheManagedReactiveEntity { + + private Object entity; + + public PanacheManagedReactiveEntityOperationsImpl(Object entity) { + this.entity = entity; + } + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveManaged(); + } + + @Override + public Uni persist() { + return operations().persist(entity); + } + + @Override + public Uni persistAndFlush() { + return operations().persistAndFlush(entity); + } + + @Override + public Uni delete() { + return operations().delete(entity); + } + + @Override + public Uni isPersistent() { + return operations().isPersistent(entity); + } + } + + // FIXME: move to runtime + static class PanacheStatelessReactiveEntityOperationsImpl implements PanacheStatelessReactiveEntity { + + private Object entity; + + public PanacheStatelessReactiveEntityOperationsImpl(Object entity) { + this.entity = entity; + } + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveStateless(); + } + + @Override + public Uni insert() { + return operations().insert(entity); + } + + @Override + public Uni update() { + return operations().update(entity); + } + + @Override + public Uni delete() { + return operations().delete(entity); + } + } + + // FIXME: move to runtime + static class PanacheStatelessBlockingEntityOperationsImpl implements PanacheStatelessBlockingEntity { + + private Object entity; + + public PanacheStatelessBlockingEntityOperationsImpl(Object entity) { + this.entity = entity; + } + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingStateless(); + } + + @Override + public Void insert() { + return operations().insert(entity); + } + + @Override + public Void update() { + return operations().update(entity); + } + + @Override + public Void delete() { + return operations().delete(entity); + } + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheQuery.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheQuery.java new file mode 100644 index 0000000000000..775cf9d06a5ad --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheQuery.java @@ -0,0 +1,258 @@ +package io.quarkus.hibernate.panache; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; +import jakarta.persistence.NoResultException; +import jakarta.persistence.NonUniqueResultException; + +import org.hibernate.Session; +import org.hibernate.annotations.Filter; +import org.hibernate.annotations.FilterDef; +import org.hibernate.query.Page; + +/** + *

+ * Interface representing an entity query, which abstracts the use of paging, getting the number of results, and + * operating on {@link List} or {@link Stream}. + *

+ *

+ * Instances of this interface cannot mutate the query itself or its parameters: only paging information can be + * modified, and instances of this interface can be reused to obtain multiple pages of results. + *

+ * + * @author Stéphane Épardaud + * @param The entity type being queried + */ +public interface PanacheQuery { + + // Builder + + /** + * Sets the current page. + * + * @param page the new page + * @return this query, modified + * @see #page(int, int) + * @see #page() + */ + public PanacheQuery page(Page page); + + /** + * Sets the current page. + * + * @param pageIndex the page index (0-based) + * @param pageSize the page size + * @return this query, modified + * @see #page(Page) + * @see #page() + */ + public PanacheQuery page(int pageIndex, int pageSize); + + /** + * Sets the current page to the next page + * + * @return this query, modified + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #previousPage() + */ + public PanacheQuery nextPage(); + + /** + * Sets the current page to the previous page (or the first page if there is no previous page) + * + * @return this query, modified + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #nextPage() + */ + public PanacheQuery previousPage(); + + /** + * Sets the current page to the first page + * + * @return this query, modified + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #lastPage() + */ + public PanacheQuery firstPage(); + + /** + * Sets the current page to the last page. This will cause reading of the entity count. + * + * @return this query, modified + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #firstPage() + * @see #count() + */ + public PanacheQuery lastPage(); + + /** + * Returns true if there is another page to read after the current one. + * This will cause reading of the entity count. + * + * @return true if there is another page to read + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #hasPreviousPage() + * @see #count() + */ + public Confirmation hasNextPage(); + + /** + * Returns true if there is a page to read before the current one. + * + * @return true if there is a previous page to read + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #hasNextPage() + */ + public Confirmation hasPreviousPage(); + + /** + * Returns the total number of pages to be read using the current page size. + * This will cause reading of the entity count. + * + * @return the total number of pages to be read using the current page size. + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + */ + public Count pageCount(); + + /** + * Returns the current page. + * + * @return the current page + * @throws UnsupportedOperationException if a page hasn't been set or if a range is already set + * @see #page(Page) + * @see #page(int,int) + */ + public Page page(); + + /** + * Switch the query to use a fixed range (start index - last index) instead of a page. + * As the range is fixed, subsequent pagination of the query is not possible. + * + * @param startIndex the index of the first element, starting at 0 + * @param lastIndex the index of the last element + * @return this query, modified + */ + public PanacheQuery range(int startIndex, int lastIndex); + + /** + * Define the locking strategy used for this query. + * + * @param lockModeType the locking strategy to be used for this query. + * @return this query, modified + */ + public PanacheQuery withLock(LockModeType lockModeType); + + /** + * Set a query property or hint on the underlying JPA Query. + * + * @param hintName name of the property or hint. + * @param value value for the property or hint. + * @return this query, modified + */ + public PanacheQuery withHint(String hintName, Object value); + + /** + *

+ * Enables a Hibernate filter during fetching of results for this query. Your filter must be declared + * with {@link FilterDef} on your entity or package, and enabled with {@link Filter} on your entity. + *

+ * WARNING: setting filters can only be done on the underlying Hibernate {@link Session} and so this + * will modify the session's filters for the duration of obtaining the results (not while building + * the query). Enabled filters will be removed from the session afterwards, but no effort is made to + * preserve filters enabled on the session outside of this API. + * + * @param filterName The name of the filter to enable + * @param parameters The set of parameters for the filter, if the filter requires parameters + * @return this query, modified + */ + public PanacheQuery filter(String filterName, Map parameters); + + /** + *

+ * Enables a Hibernate filter during fetching of results for this query. Your filter must be declared + * with {@link FilterDef} on your entity or package, and enabled with {@link Filter} on your entity. + *

+ * WARNING: setting filters can only be done on the underlying Hibernate {@link Session} and so this + * will modify the session's filters for the duration of obtaining the results (not while building + * the query). Enabled filters will be removed from the session afterwards, but no effort is made to + * preserve filters enabled on the session outside of this API. + * + * @param filterName The name of the filter to enable + * @return this query, modified + */ + public PanacheQuery filter(String filterName); + + // Results + + /** + * Reads and caches the total number of entities this query operates on. This causes a database + * query with SELECT COUNT(*) and a query equivalent to the current query, minus + * ordering. + * + * @return the total number of entities this query operates on, cached. + */ + public Count count(); + + /** + * Returns the current page of results as a {@link List}. + * + * @return the current page of results as a {@link List}. + * @see #stream() + * @see #page(Page) + * @see #page() + */ + public EntityList list(); + + /** + * Returns the current page of results as a {@link Stream}. + * + * @return the current page of results as a {@link Stream}. + * @see #list() + * @see #page(Page) + * @see #page() + */ + // FIXME: down + // public Stream stream(); + + /** + * Returns the first result of the current page index. This ignores the current page size to fetch + * a single result. + * + * @return the first result of the current page index, or null if there are no results. + * @see #singleResult() + */ + public Entity firstResult(); + + /** + * Returns the first result of the current page index. This ignores the current page size to fetch + * a single result. + * + * @return if found, an optional containing the entity, else Optional.empty(). + * @see #singleResultOptional() + */ + // FIXME: down + // public Optional firstResultOptional(); + + /** + * Executes this query for the current page and return a single result. + * + * @return the single result (throws if there is not exactly one) + * @throws NoResultException if there is no result + * @throws NonUniqueResultException if there are more than one result + * @see #firstResult() + */ + public Entity singleResult(); + + /** + * Executes this query for the current page and return a single result. + * + * @return if found, an optional containing the entity, else Optional.empty(). + * @throws NonUniqueResultException if there are more than one result + * @see #firstResultOptional() + */ + // FIXME: down + // public Optional singleResultOptional(); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepository.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepository.java new file mode 100644 index 0000000000000..308c91dfcad43 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepository.java @@ -0,0 +1,20 @@ +package io.quarkus.hibernate.panache; + +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingRepositoryBase; +import io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveRepositoryBase; +import io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingRepositoryBase; +import io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveRepositoryBase; + +public interface PanacheRepository extends PanacheManagedBlockingRepositoryBase { + public interface Managed extends PanacheManagedBlockingRepositoryBase { + } + + public interface Stateless extends PanacheStatelessBlockingRepositoryBase { + } + + public interface Reactive extends PanacheManagedReactiveRepositoryBase { + + public interface Stateless extends PanacheStatelessReactiveRepositoryBase { + } + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepositoryQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepositoryQueries.java new file mode 100644 index 0000000000000..1fcc024235618 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepositoryQueries.java @@ -0,0 +1,301 @@ +package io.quarkus.hibernate.panache; + +import java.util.Map; + +import jakarta.persistence.LockModeType; + +import io.quarkus.panache.common.Parameters; +import io.quarkus.panache.common.Sort; + +public interface PanacheRepositoryQueries, Count, Confirmation, Id> { + + // Queries + + /** + * Find an entity of this type by ID. + * + * @param id the ID of the entity to find. + * @return the entity found, or null if not found. + */ + Entity findById(Id id); + + /** + * Find an entity of this type by ID and lock it. + * + * @param id the ID of the entity to find. + * @param lockModeType the locking strategy to be used when retrieving the entity. + * @return the entity found, or null if not found. + */ + Entity findById(Id id, LockModeType lockModeType); + + /** + * Find entities using a query, with optional indexed parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params optional sequence of indexed parameters + * @return a new {@link PanacheQuery} instance for the given query + * @see #find(String, Sort, Object...) + * @see #find(String, Map) + * @see #find(String, Parameters) + * @see #list(String, Object...) + * @see #stream(String, Object...) + */ + Query find(String query, Object... params); + + /** + * Find entities using a query and the given sort options, with optional indexed parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param sort the sort strategy to use + * @param params optional sequence of indexed parameters + * @return a new {@link PanacheQuery} instance for the given query + * @see #find(String, Object...) + * @see #find(String, Sort, Map) + * @see #find(String, Sort, Parameters) + * @see #list(String, Sort, Object...) + * @see #stream(String, Sort, Object...) + */ + Query find(String query, Sort sort, Object... params); + + /** + * Find entities using a query, with named parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params {@link Map} of named parameters + * @return a new {@link PanacheQuery} instance for the given query + * @see #find(String, Sort, Map) + * @see #find(String, Object...) + * @see #find(String, Parameters) + * @see #list(String, Map) + * @see #stream(String, Map) + */ + Query find(String query, Map params); + + /** + * Find entities using a query and the given sort options, with named parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param sort the sort strategy to use + * @param params {@link Map} of indexed parameters + * @return a new {@link PanacheQuery} instance for the given query + * @see #find(String, Map) + * @see #find(String, Sort, Object...) + * @see #find(String, Sort, Parameters) + * @see #list(String, Sort, Map) + * @see #stream(String, Sort, Map) + */ + Query find(String query, Sort sort, Map params); + + /** + * Find all entities of this type. + * + * @return a new {@link PanacheQuery} instance to find all entities of this type. + * @see #findAll(Sort) + * @see #listAll() + * @see #streamAll() + */ + Query findAll(); + + /** + * Find all entities of this type, in the given order. + * + * @param sort the sort order to use + * @return a new {@link PanacheQuery} instance to find all entities of this type. + * @see #findAll() + * @see #listAll(Sort) + * @see #streamAll(Sort) + */ + Query findAll(Sort sort); + + /** + * Find entities matching a query, with optional indexed parameters. + * This method is a shortcut for find(query, params).list(). + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params optional sequence of indexed parameters + * @return a {@link List} containing all results, without paging + * @see #list(String, Sort, Object...) + * @see #list(String, Map) + * @see #list(String, Parameters) + * @see #find(String, Object...) + * @see #stream(String, Object...) + */ + EntityList list(String query, Object... params); + + /** + * Find entities matching a query and the given sort options, with optional indexed parameters. + * This method is a shortcut for find(query, sort, params).list(). + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param sort the sort strategy to use + * @param params optional sequence of indexed parameters + * @return a {@link List} containing all results, without paging + * @see #list(String, Object...) + * @see #list(String, Sort, Map) + * @see #list(String, Sort, Parameters) + * @see #find(String, Sort, Object...) + * @see #stream(String, Sort, Object...) + */ + EntityList list(String query, Sort sort, Object... params); + + /** + * Find entities matching a query, with named parameters. + * This method is a shortcut for find(query, params).list(). + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params {@link Map} of named parameters + * @return a {@link List} containing all results, without paging + * @see #list(String, Sort, Map) + * @see #list(String, Object...) + * @see #list(String, Parameters) + * @see #find(String, Map) + * @see #stream(String, Map) + */ + EntityList list(String query, Map params); + + /** + * Find entities matching a query and the given sort options, with named parameters. + * This method is a shortcut for find(query, sort, params).list(). + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param sort the sort strategy to use + * @param params {@link Map} of indexed parameters + * @return a {@link List} containing all results, without paging + * @see #list(String, Map) + * @see #list(String, Sort, Object...) + * @see #list(String, Sort, Parameters) + * @see #find(String, Sort, Map) + * @see #stream(String, Sort, Map) + */ + EntityList list(String query, Sort sort, Map params); + + /** + * Find all entities of this type. + * This method is a shortcut for findAll().list(). + * + * @return a {@link List} containing all results, without paging + * @see #listAll(Sort) + * @see #findAll() + * @see #streamAll() + */ + EntityList listAll(); + + /** + * Find all entities of this type, in the given order. + * This method is a shortcut for findAll(sort).list(). + * + * @param sort the sort order to use + * @return a {@link List} containing all results, without paging + * @see #listAll() + * @see #findAll(Sort) + * @see #streamAll(Sort) + */ + EntityList listAll(Sort sort); + + /** + * Counts the number of this type of entity in the database. + * + * @return the number of this type of entity in the database. + * @see #count(String, Object...) + * @see #count(String, Map) + * @see #count(String, Parameters) + */ + Count count(); + + /** + * Counts the number of this type of entity matching the given query, with optional indexed parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params optional sequence of indexed parameters + * @return the number of entities counted. + * @see #count() + * @see #count(String, Map) + * @see #count(String, Parameters) + */ + Count count(String query, Object... params); + + /** + * Counts the number of this type of entity matching the given query, with named parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params {@link Map} of named parameters + * @return the number of entities counted. + * @see #count() + * @see #count(String, Object...) + * @see #count(String, Parameters) + */ + Count count(String query, Map params); + + /** + * Delete all entities of this type from the database. + * + * WARNING: the default implementation of this method uses a bulk delete query and ignores + * cascading rules from the JPA model. + * + * @return the number of entities deleted. + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #delete(String, Parameters) + */ + Count deleteAll(); + + /** + * Delete an entity of this type by ID. + * + * @param id the ID of the entity to delete. + * @return false if the entity was not deleted (not found). + */ + Confirmation deleteById(Id id); + + /** + * Delete all entities of this type matching the given query, with optional indexed parameters. + * + * WARNING: the default implementation of this method uses a bulk delete query and ignores + * cascading rules from the JPA model. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params optional sequence of indexed parameters + * @return the number of entities deleted. + * @see #deleteAll() + * @see #delete(String, Map) + * @see #delete(String, Parameters) + */ + Count delete(String query, Object... params); + + /** + * Delete all entities of this type matching the given query, with named parameters. + * + * WARNING: the default implementation of this method uses a bulk delete query and ignores + * cascading rules from the JPA model. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params {@link Map} of named parameters + * @return the number of entities deleted. + * @see #deleteAll() + * @see #delete(String, Object...) + * @see #delete(String, Parameters) + */ + Count delete(String query, Map params); + + /** + * Update all entities of this type matching the given query, with optional indexed parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params optional sequence of indexed parameters + * @return the number of entities updated. + * @see #update(String, Map) + * @see #update(String, Parameters) + */ + Count update(String query, Object... params); + + /** + * Update all entities of this type matching the given query, with named parameters. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params {@link Map} of named parameters + * @return the number of entities updated. + * @see #update(String, Object...) + * @see #update(String, Parameters) + */ + Count update(String query, Map params); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepositorySwitcher.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepositorySwitcher.java new file mode 100644 index 0000000000000..05fb56be29e79 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/PanacheRepositorySwitcher.java @@ -0,0 +1,29 @@ +package io.quarkus.hibernate.panache; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingRepositoryBase; +import io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveRepositoryBase; +import io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingRepositoryBase; +import io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveRepositoryBase; + +public interface PanacheRepositorySwitcher { + default PanacheManagedBlockingRepositoryBase managedBlocking() { + // FIXME: generate in impl + throw AbstractJpaOperations.implementationInjectionMissing(); + } + + default PanacheManagedReactiveRepositoryBase managedReactive() { + // FIXME: generate in impl + throw AbstractJpaOperations.implementationInjectionMissing(); + } + + default PanacheStatelessBlockingRepositoryBase statelessBlocking() { + // FIXME: generate in impl + throw AbstractJpaOperations.implementationInjectionMissing(); + } + + default PanacheStatelessReactiveRepositoryBase statelessReactive() { + // FIXME: generate in impl + throw AbstractJpaOperations.implementationInjectionMissing(); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/WithId.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/WithId.java new file mode 100644 index 0000000000000..4bb53fff62617 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/WithId.java @@ -0,0 +1,33 @@ +package io.quarkus.hibernate.panache; + +import java.util.UUID; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public class WithId { + /** + * The auto-generated ID field. This field is set by Hibernate ORM when this entity + * is persisted. + */ + @Id + @GeneratedValue + public IdType id; + + @Override + public java.lang.String toString() { + return this.getClass().getSimpleName() + "<" + id + ">"; + } + + public abstract static class AutoLong extends WithId { + } + + public abstract static class AutoString extends WithId { + } + + public abstract static class AutoUUID extends WithId { + } + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/blocking/PanacheBlockingQuery.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/blocking/PanacheBlockingQuery.java new file mode 100644 index 0000000000000..7869246555c32 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/blocking/PanacheBlockingQuery.java @@ -0,0 +1,66 @@ +package io.quarkus.hibernate.panache.blocking; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.NonUniqueResultException; + +import io.quarkus.hibernate.panache.PanacheQuery; + +public interface PanacheBlockingQuery extends PanacheQuery, Boolean, Long> { + + /** + * Defines a projection class. This will transform the returned values into instances of the given type using the following + * mapping rules: + *

    + *
  • If your query already selects some specific columns (starts with select distinct? a, b, c…) then we + * transform + * it into a query of the form: select distinct? new ProjectionClass(a, b, c)…. There must be a matching + * constructor + * that accepts the selected column types, in the right order.
  • + *
  • If your query does not select any specific column (starts with from…) then we transform it into a query + * of the form: + * select new ProjectionClass(a, b, c…) from… where we fetch the list of selected columns from your projection + * class' + * single constructor, using its parameter names (or their {@link ProjectedFieldName} annotations), in the same order as the + * constructor.
  • + *
  • If this is already a project query of the form select distinct? new…, we throw a + * {@link PanacheQueryException}
  • + * + * @param type the projected class type + * @return a new query with the same state as the previous one (params, page, range, lockMode, hints, ...) but a projected + * result of the type + * type + * @throws PanacheQueryException if this represents an already-projected query + */ + public PanacheQuery, Boolean, Long> project(Class type); + + /** + * Returns the current page of results as a {@link Stream}. + * + * @return the current page of results as a {@link Stream}. + * @see #list() + * @see #page(Page) + * @see #page() + */ + public Stream stream(); + + /** + * Returns the first result of the current page index. This ignores the current page size to fetch + * a single result. + * + * @return if found, an optional containing the entity, else Optional.empty(). + * @see #singleResultOptional() + */ + public Optional firstResultOptional(); + + /** + * Executes this query for the current page and return a single result. + * + * @return if found, an optional containing the entity, else Optional.empty(). + * @throws NonUniqueResultException if there are more than one result + * @see #firstResultOptional() + */ + public Optional singleResultOptional(); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/blocking/PanacheRepositoryBlockingQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/blocking/PanacheRepositoryBlockingQueries.java new file mode 100644 index 0000000000000..038c2cb1dbcc4 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/blocking/PanacheRepositoryBlockingQueries.java @@ -0,0 +1,130 @@ +package io.quarkus.hibernate.panache.blocking; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import io.quarkus.hibernate.panache.PanacheRepositoryQueries; +import io.quarkus.panache.common.Parameters; +import io.quarkus.panache.common.Sort; + +public interface PanacheRepositoryBlockingQueries + extends PanacheRepositoryQueries, PanacheBlockingQuery, Long, Boolean, Id> { + + // Queries + + /** + * Find an entity of this type by ID. + * + * @param id the ID of the entity to find. + * @return if found, an optional containing the entity, else Optional.empty(). + */ + Optional findByIdOptional(Id id); + + /** + * Find an entity of this type by ID. + * + * @param id the ID of the entity to find. + * @return if found, an optional containing the entity, else Optional.empty(). + */ + Optional findByIdOptional(Id id, LockModeType lockModeType); + + /** + * Find entities matching a query, with optional indexed parameters. + * This method is a shortcut for find(query, params).stream(). + * It requires a transaction to work. + * Without a transaction, the underlying cursor can be closed before the end of the stream. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params optional sequence of indexed parameters + * @return a {@link Stream} containing all results, without paging + * @see #stream(String, Sort, Object...) + * @see #stream(String, Map) + * @see #stream(String, Parameters) + * @see #find(String, Object...) + * @see #list(String, Object...) + */ + Stream stream(String query, Object... params); + + /** + * Find entities matching a query and the given sort options, with optional indexed parameters. + * This method is a shortcut for find(query, sort, params).stream(). + * It requires a transaction to work. + * Without a transaction, the underlying cursor can be closed before the end of the stream. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param sort the sort strategy to use + * @param params optional sequence of indexed parameters + * @return a {@link Stream} containing all results, without paging + * @see #stream(String, Object...) + * @see #stream(String, Sort, Map) + * @see #stream(String, Sort, Parameters) + * @see #find(String, Sort, Object...) + * @see #list(String, Sort, Object...) + */ + Stream stream(String query, Sort sort, Object... params); + + /** + * Find entities matching a query, with named parameters. + * This method is a shortcut for find(query, params).stream(). + * It requires a transaction to work. + * Without a transaction, the underlying cursor can be closed before the end of the stream. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param params {@link Map} of named parameters + * @return a {@link Stream} containing all results, without paging + * @see #stream(String, Sort, Map) + * @see #stream(String, Object...) + * @see #stream(String, Parameters) + * @see #find(String, Map) + * @see #list(String, Map) + */ + Stream stream(String query, Map params); + + /** + * Find entities matching a query and the given sort options, with named parameters. + * This method is a shortcut for find(query, sort, params).stream(). + * It requires a transaction to work. + * Without a transaction, the underlying cursor can be closed before the end of the stream. + * + * @param query a {@link io.quarkus.hibernate.panache query string} + * @param sort the sort strategy to use + * @param params {@link Map} of indexed parameters + * @return a {@link Stream} containing all results, without paging + * @see #stream(String, Map) + * @see #stream(String, Sort, Object...) + * @see #stream(String, Sort, Parameters) + * @see #find(String, Sort, Map) + * @see #list(String, Sort, Map) + */ + Stream stream(String query, Sort sort, Map params); + + /** + * Find all entities of this type. + * This method is a shortcut for findAll().stream(). + * It requires a transaction to work. + * Without a transaction, the underlying cursor can be closed before the end of the stream. + * + * @return a {@link Stream} containing all results, without paging + * @see #streamAll(Sort) + * @see #findAll() + * @see #listAll() + */ + Stream streamAll(Sort sort); + + /** + * Find all entities of this type, in the given order. + * This method is a shortcut for findAll().stream(). + * It requires a transaction to work. + * Without a transaction, the underlying cursor can be closed before the end of the stream. + * + * @return a {@link Stream} containing all results, without paging + * @see #streamAll() + * @see #findAll(Sort) + * @see #listAll(Sort) + */ + Stream streamAll(); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/PanacheManagedEntityOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/PanacheManagedEntityOperations.java new file mode 100644 index 0000000000000..142ab0eb83927 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/PanacheManagedEntityOperations.java @@ -0,0 +1,58 @@ +package io.quarkus.hibernate.panache.managed; + +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.json.bind.annotation.JsonbTransient; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import io.quarkus.hibernate.panache.PanacheEntityMarker; +import io.quarkus.panache.common.Parameters; + +public interface PanacheManagedEntityOperations extends PanacheEntityMarker { + /** + * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set. + * + * @see #isPersistent() + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + public Completion persist(); + + /** + * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set. + * Then flushes all pending changes to the database. + * + * @see #isPersistent() + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + public Completion persistAndFlush(); + + /** + * Delete this entity from the database, if it is already persisted. + * + * @see #isPersistent() + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #delete(String, Parameters) + * @see #deleteAll() + */ + public Completion delete(); + + /** + * Returns true if this entity is persistent in the database. If yes, all modifications to + * its persistent fields will be automatically committed to the database at transaction + * commit time. + * + * @return true if this entity is persistent in the database. + */ + @JsonbTransient + // @JsonIgnore is here to avoid serialization of this property with jackson + @JsonIgnore + public Confirmation isPersistent(); + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/PanacheManagedRepositoryOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/PanacheManagedRepositoryOperations.java new file mode 100644 index 0000000000000..cf2713c78a310 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/PanacheManagedRepositoryOperations.java @@ -0,0 +1,95 @@ +package io.quarkus.hibernate.panache.managed; + +import java.util.Map; +import java.util.stream.Stream; + +public interface PanacheManagedRepositoryOperations { + + // Operations + + /** + * Returns the {@link Session} for the entity class for extra operations (eg. CriteriaQueries) + * + * @return the {@link Session} for the entity class + */ + Session getSession(); + + /** + * Persist the given entity in the database, if not already persisted. + * + * @param entity the entity to persist. + * @see #isPersistent(Object) + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + Completion persist(Entity entity); + + /** + * Persist the given entity in the database, if not already persisted. + * Then flushes all pending changes to the database. + * + * @param entity the entity to persist. + * @see #isPersistent(Object) + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + Completion persistAndFlush(Entity entity); + + /** + * Delete the given entity from the database, if it is already persisted. + * + * @param entity the entity to delete. + * @see #isPersistent(Object) + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #deleteAll() + */ + Completion delete(Entity entity); + + /** + * Returns true if the given entity is persistent in the database. If yes, all modifications to + * its persistent fields will be automatically committed to the database at transaction + * commit time. + * + * @param entity the entity to check + * @return true if the entity is persistent in the database. + */ + Confirmation isPersistent(Entity entity); + + /** + * Flushes all pending changes to the database using the Session for the entity class. + */ + Completion flush(); + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Stream) + * @see #persist(Object,Object...) + */ + Completion persist(Iterable entities); + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Iterable) + * @see #persist(Object,Object...) + */ + Completion persist(Stream entities); + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Stream) + * @see #persist(Iterable) + */ + Completion persist(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingEntity.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingEntity.java new file mode 100644 index 0000000000000..c130f6eecb17e --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingEntity.java @@ -0,0 +1,77 @@ +package io.quarkus.hibernate.panache.managed.blocking; + +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.json.bind.annotation.JsonbTransient; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import io.quarkus.hibernate.panache.managed.PanacheManagedEntityOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.panache.common.Parameters; + +public interface PanacheManagedBlockingEntity extends PanacheManagedEntityOperations { + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingManaged(); + } + + /** + * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set. + * + * @see #isPersistent() + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + @Override + public default Void persist() { + return operations().persist(this); + } + + /** + * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set. + * Then flushes all pending changes to the database. + * + * @see #isPersistent() + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + @Override + public default Void persistAndFlush() { + return operations().persistAndFlush(this); + } + + /** + * Delete this entity from the database, if it is already persisted. + * + * @see #isPersistent() + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #delete(String, Parameters) + * @see #deleteAll() + */ + @Override + public default Void delete() { + return operations().delete(this); + } + + /** + * Returns true if this entity is persistent in the database. If yes, all modifications to + * its persistent fields will be automatically committed to the database at transaction + * commit time. + * + * @return true if this entity is persistent in the database. + */ + @JsonbTransient + // @JsonIgnore is here to avoid serialization of this property with jackson + @JsonIgnore + @Override + public default Boolean isPersistent() { + return operations().isPersistent(this); + } + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepository.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepository.java new file mode 100644 index 0000000000000..ef00cd271e932 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepository.java @@ -0,0 +1,5 @@ +package io.quarkus.hibernate.panache.managed.blocking; + +public interface PanacheManagedBlockingRepository extends PanacheManagedBlockingRepositoryBase { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryBase.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryBase.java new file mode 100644 index 0000000000000..cefbf8b6a76b2 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryBase.java @@ -0,0 +1,10 @@ +package io.quarkus.hibernate.panache.managed.blocking; + +import io.quarkus.hibernate.panache.PanacheRepositorySwitcher; + +public interface PanacheManagedBlockingRepositoryBase + extends PanacheManagedBlockingRepositoryOperations, + PanacheManagedBlockingRepositoryQueries, + PanacheRepositorySwitcher { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryOperations.java new file mode 100644 index 0000000000000..39aa3af302401 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryOperations.java @@ -0,0 +1,129 @@ +package io.quarkus.hibernate.panache.managed.blocking; + +import java.util.Map; +import java.util.stream.Stream; + +import org.hibernate.Session; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.managed.PanacheManagedRepositoryOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; + +public interface PanacheManagedBlockingRepositoryOperations + extends PanacheManagedRepositoryOperations { + + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingManaged(); + } + + // Operations + + /** + * Returns the {@link Session} for the entity class for extra operations (eg. CriteriaQueries) + * + * @return the {@link Session} for the entity class + */ + default Session getSession() { + return operations().getSession(getEntityClass()); + } + + /** + * Persist the given entity in the database, if not already persisted. + * + * @param entity the entity to persist. + * @see #isPersistent(Object) + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + default Void persist(Entity entity) { + return operations().persist(entity); + } + + /** + * Persist the given entity in the database, if not already persisted. + * Then flushes all pending changes to the database. + * + * @param entity the entity to persist. + * @see #isPersistent(Object) + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + default Void persistAndFlush(Entity entity) { + return operations().persistAndFlush(entity); + } + + /** + * Delete the given entity from the database, if it is already persisted. + * + * @param entity the entity to delete. + * @see #isPersistent(Object) + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #deleteAll() + */ + default Void delete(Entity entity) { + return operations().delete(entity); + } + + /** + * Returns true if the given entity is persistent in the database. If yes, all modifications to + * its persistent fields will be automatically committed to the database at transaction + * commit time. + * + * @param entity the entity to check + * @return true if the entity is persistent in the database. + */ + default Boolean isPersistent(Entity entity) { + return operations().isPersistent(entity); + } + + /** + * Flushes all pending changes to the database using the Session for the entity class. + */ + default Void flush() { + return operations().flush(getEntityClass()); + } + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Stream) + * @see #persist(Object,Object...) + */ + default Void persist(Iterable entities) { + return operations().persist(entities); + } + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Iterable) + * @see #persist(Object,Object...) + */ + default Void persist(Stream entities) { + return operations().persist(entities); + } + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Stream) + * @see #persist(Iterable) + */ + default Void persist(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) { + return operations().persist(firstEntity, entities); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryQueries.java new file mode 100644 index 0000000000000..0bc6e2ae498dc --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/blocking/PanacheManagedBlockingRepositoryQueries.java @@ -0,0 +1,180 @@ +package io.quarkus.hibernate.panache.managed.blocking; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import io.quarkus.hibernate.panache.blocking.PanacheRepositoryBlockingQueries; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.panache.common.Sort; + +public interface PanacheManagedBlockingRepositoryQueries extends PanacheRepositoryBlockingQueries { + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingManaged(); + } + + @Override + default Entity findById(Id id) { + return (Entity) operations().findById(getEntityClass(), id); + } + + @Override + default Entity findById(Id id, LockModeType lockModeType) { + return (Entity) operations().findById(getEntityClass(), id, lockModeType); + } + + @Override + default Optional findByIdOptional(Id id) { + return (Optional) operations().findByIdOptional(getEntityClass(), id); + } + + @Override + default Optional findByIdOptional(Id id, LockModeType lockModeType) { + return (Optional) operations().findByIdOptional(getEntityClass(), id, lockModeType); + } + + @Override + default PanacheBlockingQuery find(String query, Object... params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheBlockingQuery find(String query, Sort sort, Object... params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheBlockingQuery find(String query, Map params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheBlockingQuery find(String query, Sort sort, Map params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheBlockingQuery findAll() { + return (PanacheBlockingQuery) operations().findAll(getEntityClass()); + } + + @Override + default PanacheBlockingQuery findAll(Sort sort) { + return (PanacheBlockingQuery) operations().findAll(getEntityClass(), sort); + } + + @Override + default List list(String query, Object... params) { + return (List) operations().list(getEntityClass(), query, params); + } + + @Override + default List list(String query, Sort sort, Object... params) { + return (List) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default List list(String query, Map params) { + return (List) operations().list(getEntityClass(), query, params); + } + + @Override + default List list(String query, Sort sort, Map params) { + return (List) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default List listAll() { + return (List) operations().listAll(getEntityClass()); + } + + @Override + default List listAll(Sort sort) { + return (List) operations().listAll(getEntityClass(), sort); + } + + @Override + default Stream stream(String query, Object... params) { + return (Stream) operations().stream(getEntityClass(), query, params); + } + + @Override + default Stream stream(String query, Sort sort, Object... params) { + return (Stream) operations().stream(getEntityClass(), query, sort, params); + } + + @Override + default Stream stream(String query, Map params) { + return (Stream) operations().stream(getEntityClass(), query, params); + } + + @Override + default Stream stream(String query, Sort sort, Map params) { + return (Stream) operations().stream(getEntityClass(), query, sort, params); + } + + @Override + default Stream streamAll(Sort sort) { + return (Stream) operations().streamAll(getEntityClass(), sort); + } + + @Override + default Stream streamAll() { + return (Stream) operations().streamAll(getEntityClass()); + } + + @Override + default Long count() { + return operations().count(getEntityClass()); + } + + @Override + default Long count(String query, Object... params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Long count(String query, Map params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Long deleteAll() { + return operations().deleteAll(getEntityClass()); + } + + @Override + default Boolean deleteById(Id id) { + return operations().deleteById(getEntityClass(), id); + } + + @Override + default Long delete(String query, Object... params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Long delete(String query, Map params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Long update(String query, Object... params) { + return operations().update(getEntityClass(), query, params); + } + + @Override + default Long update(String query, Map params) { + return operations().update(getEntityClass(), query, params); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveEntity.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveEntity.java new file mode 100644 index 0000000000000..e12d953104bc2 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveEntity.java @@ -0,0 +1,78 @@ +package io.quarkus.hibernate.panache.managed.reactive; + +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.json.bind.annotation.JsonbTransient; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import io.quarkus.hibernate.panache.managed.PanacheManagedEntityOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.panache.common.Parameters; +import io.smallrye.mutiny.Uni; + +public interface PanacheManagedReactiveEntity extends PanacheManagedEntityOperations, Uni> { + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveManaged(); + } + + /** + * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set. + * + * @see #isPersistent() + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + @Override + public default Uni persist() { + return operations().persist(this); + } + + /** + * Persist this entity in the database, if not already persisted. This will set your ID field if it is not already set. + * Then flushes all pending changes to the database. + * + * @see #isPersistent() + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + @Override + public default Uni persistAndFlush() { + return operations().persistAndFlush(this); + } + + /** + * Delete this entity from the database, if it is already persisted. + * + * @see #isPersistent() + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #delete(String, Parameters) + * @see #deleteAll() + */ + @Override + public default Uni delete() { + return operations().delete(this); + } + + /** + * Returns true if this entity is persistent in the database. If yes, all modifications to + * its persistent fields will be automatically committed to the database at transaction + * commit time. + * + * @return true if this entity is persistent in the database. + */ + @JsonbTransient + // @JsonIgnore is here to avoid serialization of this property with jackson + @JsonIgnore + @Override + public default Uni isPersistent() { + return operations().isPersistent(this); + } + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepository.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepository.java new file mode 100644 index 0000000000000..c7127e8887e58 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepository.java @@ -0,0 +1,4 @@ +package io.quarkus.hibernate.panache.managed.reactive; + +public interface PanacheManagedReactiveRepository extends PanacheManagedReactiveRepositoryBase { +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryBase.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryBase.java new file mode 100644 index 0000000000000..c14ac4ecaaf00 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryBase.java @@ -0,0 +1,10 @@ +package io.quarkus.hibernate.panache.managed.reactive; + +import io.quarkus.hibernate.panache.PanacheRepositorySwitcher; + +public interface PanacheManagedReactiveRepositoryBase + extends PanacheManagedReactiveRepositoryOperations, + PanacheManagedReactiveRepositoryQueries, + PanacheRepositorySwitcher { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryOperations.java new file mode 100644 index 0000000000000..d5c4bb816c013 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryOperations.java @@ -0,0 +1,130 @@ +package io.quarkus.hibernate.panache.managed.reactive; + +import java.util.Map; +import java.util.stream.Stream; + +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.managed.PanacheManagedRepositoryOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.smallrye.mutiny.Uni; + +public interface PanacheManagedReactiveRepositoryOperations + extends PanacheManagedRepositoryOperations, Uni, Uni, Id> { + + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveManaged(); + } + + // Operations + + /** + * Returns the {@link Mutiny.Session} for the entity class for extra operations (eg. CriteriaQueries) + * + * @return the {@link Mutiny.Session} for the entity class + */ + default Uni getSession() { + return operations().getSession(getEntityClass()); + } + + /** + * Persist the given entity in the database, if not already persisted. + * + * @param entity the entity to persist. + * @see #isPersistent(Object) + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + default Uni persist(Entity entity) { + return operations().persist(entity); + } + + /** + * Persist the given entity in the database, if not already persisted. + * Then flushes all pending changes to the database. + * + * @param entity the entity to persist. + * @see #isPersistent(Object) + * @see #persist(Iterable) + * @see #persist(Stream) + * @see #persist(Object, Object...) + */ + default Uni persistAndFlush(Entity entity) { + return operations().persistAndFlush(entity); + } + + /** + * Delete the given entity from the database, if it is already persisted. + * + * @param entity the entity to delete. + * @see #isPersistent(Object) + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #deleteAll() + */ + default Uni delete(Entity entity) { + return operations().delete(entity); + } + + /** + * Returns true if the given entity is persistent in the database. If yes, all modifications to + * its persistent fields will be automatically committed to the database at transaction + * commit time. + * + * @param entity the entity to check + * @return true if the entity is persistent in the database. + */ + default Uni isPersistent(Entity entity) { + return operations().isPersistent(entity); + } + + /** + * Flushes all pending changes to the database using the Session for the entity class. + */ + default Uni flush() { + return operations().flush(getEntityClass()); + } + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Stream) + * @see #persist(Object,Object...) + */ + default Uni persist(Iterable entities) { + return operations().persist(entities); + } + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Iterable) + * @see #persist(Object,Object...) + */ + default Uni persist(Stream entities) { + return operations().persist(entities); + } + + /** + * Persist all given entities. + * + * @param entities the entities to persist + * @see #persist(Object) + * @see #persist(Stream) + * @see #persist(Iterable) + */ + default Uni persist(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) { + return operations().persist(firstEntity, entities); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryQueries.java new file mode 100644 index 0000000000000..33e30f8be7b32 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/managed/reactive/PanacheManagedReactiveRepositoryQueries.java @@ -0,0 +1,139 @@ +package io.quarkus.hibernate.panache.managed.reactive; + +import java.util.List; +import java.util.Map; + +import jakarta.persistence.LockModeType; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.panache.reactive.PanacheRepositoryReactiveQueries; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.panache.common.Sort; +import io.smallrye.mutiny.Uni; + +public interface PanacheManagedReactiveRepositoryQueries extends PanacheRepositoryReactiveQueries { + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveManaged(); + } + + @Override + default Entity findById(Id id) { + return (Entity) operations().findById(getEntityClass(), id); + } + + @Override + default Entity findById(Id id, LockModeType lockModeType) { + return (Entity) operations().findById(getEntityClass(), id, lockModeType); + } + + @Override + default PanacheReactiveQuery find(String query, Object... params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheReactiveQuery find(String query, Sort sort, Object... params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheReactiveQuery find(String query, Map params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheReactiveQuery find(String query, Sort sort, Map params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheReactiveQuery findAll() { + return (PanacheReactiveQuery) operations().findAll(getEntityClass()); + } + + @Override + default PanacheReactiveQuery findAll(Sort sort) { + return (PanacheReactiveQuery) operations().findAll(getEntityClass(), sort); + } + + @Override + default Uni> list(String query, Object... params) { + return (Uni) operations().list(getEntityClass(), query, params); + } + + @Override + default Uni> list(String query, Sort sort, Object... params) { + return (Uni) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default Uni> list(String query, Map params) { + return (Uni) operations().list(getEntityClass(), query, params); + } + + @Override + default Uni> list(String query, Sort sort, Map params) { + return (Uni) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default Uni> listAll() { + return (Uni) operations().listAll(getEntityClass()); + } + + @Override + default Uni> listAll(Sort sort) { + return (Uni) operations().listAll(getEntityClass(), sort); + } + + @Override + default Uni count() { + return operations().count(getEntityClass()); + } + + @Override + default Uni count(String query, Object... params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Uni count(String query, Map params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Uni deleteAll() { + return operations().deleteAll(getEntityClass()); + } + + @Override + default Uni deleteById(Id id) { + return operations().deleteById(getEntityClass(), id); + } + + @Override + default Uni delete(String query, Object... params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Uni delete(String query, Map params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Uni update(String query, Object... params) { + return operations().update(getEntityClass(), query, params); + } + + @Override + default Uni update(String query, Map params) { + return operations().update(getEntityClass(), query, params); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/package-info.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/package-info.java new file mode 100644 index 0000000000000..092760d097b67 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/package-info.java @@ -0,0 +1,97 @@ +/** + *

    API usage

    + * + * Hibernate with Panache comes in two flavors, the active record pattern via + * {@link io.quarkus.hibernate.orm.panache.PanacheEntity} + * and the repository pattern via {@link io.quarkus.hibernate.orm.panache.PanacheRepository}. + * + * To use the active record pattern, make your entities extend {@link io.quarkus.hibernate.orm.panache.PanacheEntity}, + * use public fields for your columns, use the existing operations defined as static methods on your entity class, + * and define custom ones as static methods on your entity class: + * + *
    + * @Entity
    + * public class Person extends PanacheEntity {
    + *     public String name;
    + *     public LocalDate birth;
    + *     public PersonStatus status;
    + *
    + *     public static Person findByName(String name){
    + *       return find("name", name).firstResult();
    + *     }
    + *
    + *     public static List<Person> findAlive(){
    + *       return list("status", Status.Alive);
    + *     }
    + *
    + *     public static void deleteStefs(){
    + *       delete("name", "Stef");
    + *     }
    + * }
    + * 
    + * + * To use the repository pattern, create a class implementing {@link io.quarkus.hibernate.orm.panache.PanacheRepository}, + * use the existing operations from your repository and define new ones on your repository class: + * + *
    + * @ApplicationScoped
    + * public class PersonRepository implements PanacheRepository<Person> {
    + *    public Person findByName(String name){
    + *        return find("name", name).firstResult();
    + *    }
    + *
    + *    public List<Person> findAlive(){
    + *        return list("status", Status.Alive);
    + *    }
    + *
    + *    public void deleteStefs(){
    + *        delete("name", "Stef");
    + *   }
    + * }
    + * 
    + * + *

    Simplified queries

    + * + *

    + * Normally, HQL queries are of this form: from EntityName [where ...] [order by ...], with optional elements + * at the end. + *

    + *

    + * If your select query does not start with from, select, or with, we support the + * following additional forms: + *

    + *
      + *
    • order by ... which will expand to from EntityName order by ...
    • + *
    • <singleAttribute> (and single parameter) which will expand to + * from EntityName where <singleAttribute> = ?
    • + *
    • where <query> will expand to from EntityName where <query> + *
    • <query> will expand to from EntityName where <query>
    • + *
    + * + *

    + * If your update query does not start with update from, we support the following additional forms: + *

    + *
      + *
    • from EntityName ... which will expand to update from EntityName ...
    • + *
    • set? <singleAttribute> (and single parameter) which will expand to + * update from EntityName set <singleAttribute> = ?
    • + *
    • set? <update-query> will expand to + * update from EntityName set <update-query> = ?
    • + *
    + * + *

    + * If your delete query does not start with delete from, we support the following additional forms: + *

    + *
      + *
    • from EntityName ... which will expand to delete from EntityName ...
    • + *
    • <singleAttribute> (and single parameter) which will expand to + * delete from EntityName where <singleAttribute> = ?
    • + *
    • <query> will expand to delete from EntityName where <query>
    • + *
    + * + * We also support named queries, for Panache to know that a query is a named query and not an HQL one, you need + * to prefix the name of the query with '#'. + * + * @author Stéphane Épardaud + */ +package io.quarkus.hibernate.panache; diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/reactive/PanacheReactiveQuery.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/reactive/PanacheReactiveQuery.java new file mode 100644 index 0000000000000..838caf30e73da --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/reactive/PanacheReactiveQuery.java @@ -0,0 +1,37 @@ +package io.quarkus.hibernate.panache.reactive; + +import java.util.List; + +import io.quarkus.hibernate.orm.panache.common.ProjectedFieldName; +import io.quarkus.hibernate.panache.PanacheQuery; +import io.quarkus.panache.common.exception.PanacheQueryException; +import io.smallrye.mutiny.Uni; + +public interface PanacheReactiveQuery extends PanacheQuery, Uni>, Uni, Uni> { + + /** + * Defines a projection class. This will transform the returned values into instances of the given type using the following + * mapping rules: + *
      + *
    • If your query already selects some specific columns (starts with select distinct? a, b, c…) then we + * transform + * it into a query of the form: select distinct? new ProjectionClass(a, b, c)…. There must be a matching + * constructor + * that accepts the selected column types, in the right order.
    • + *
    • If your query does not select any specific column (starts with from…) then we transform it into a query + * of the form: + * select new ProjectionClass(a, b, c…) from… where we fetch the list of selected columns from your projection + * class' + * single constructor, using its parameter names (or their {@link ProjectedFieldName} annotations), in the same order as the + * constructor.
    • + *
    • If this is already a project query of the form select distinct? new…, we throw a + * {@link PanacheQueryException}
    • + * + * @param type the projected class type + * @return a new query with the same state as the previous one (params, page, range, lockMode, hints, ...) but a projected + * result of the type + * type + * @throws PanacheQueryException if this represents an already-projected query + */ + public PanacheQuery, Uni>, Boolean, Long> project(Class type); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/reactive/PanacheRepositoryReactiveQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/reactive/PanacheRepositoryReactiveQueries.java new file mode 100644 index 0000000000000..4631536edcbac --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/reactive/PanacheRepositoryReactiveQueries.java @@ -0,0 +1,10 @@ +package io.quarkus.hibernate.panache.reactive; + +import java.util.List; + +import io.quarkus.hibernate.panache.PanacheRepositoryQueries; +import io.smallrye.mutiny.Uni; + +public interface PanacheRepositoryReactiveQueries + extends PanacheRepositoryQueries>, PanacheReactiveQuery, Uni, Uni, Id> { +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/PanacheHibernateRecorder.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/PanacheHibernateRecorder.java new file mode 100644 index 0000000000000..77cd651100a06 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/PanacheHibernateRecorder.java @@ -0,0 +1,19 @@ +package io.quarkus.hibernate.panache.runtime; + +import java.util.Map; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class PanacheHibernateRecorder { + public void setEntityToPersistenceUnit(Map entityToPersistenceUnit, boolean incomplete) { + AbstractJpaOperations.setEntityToPersistenceUnit(entityToPersistenceUnit, incomplete); + io.quarkus.hibernate.reactive.panache.common.runtime.AbstractJpaOperations + .setEntityToPersistenceUnit(entityToPersistenceUnit); + } + + public void setRepositoryClassesToEntityClasses(Map, Class> repositoryClassesToEntityClasses) { + AbstractJpaOperations.setRepositoryClassesToEntityClasses(repositoryClassesToEntityClasses); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/ManagedReactiveJpaOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/ManagedReactiveJpaOperations.java new file mode 100644 index 0000000000000..bd10ef402b573 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/ManagedReactiveJpaOperations.java @@ -0,0 +1,23 @@ +package io.quarkus.hibernate.panache.runtime.hr; + +import java.util.List; + +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.reactive.panache.common.runtime.AbstractManagedJpaOperations; +import io.smallrye.mutiny.Uni; + +public class ManagedReactiveJpaOperations extends AbstractManagedJpaOperations> { + + @Override + protected PanacheReactiveQuery createPanacheQuery(Uni session, String query, String originalQuery, + String orderBy, Object paramsArrayOrMap) { + return new PanacheManagedReactiveQueryImpl<>(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + @Override + public Uni> list(PanacheReactiveQuery query) { + return (Uni) query.list(); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/ManagedReactiveOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/ManagedReactiveOperations.java new file mode 100644 index 0000000000000..573561287efcc --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/ManagedReactiveOperations.java @@ -0,0 +1,216 @@ +package io.quarkus.hibernate.panache.runtime.hr; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.panache.common.Sort; +import io.smallrye.mutiny.Uni; + +public class ManagedReactiveOperations implements PanacheReactiveOperations { + + public final static ManagedReactiveOperations INSTANCE = new ManagedReactiveOperations(); + private final static ManagedReactiveJpaOperations DELEGATE = new ManagedReactiveJpaOperations(); + + private ManagedReactiveOperations() { + } + + @Override + public Uni getSession(Class entityClass) { + return DELEGATE.getSession(entityClass); + } + + @Override + public Uni getStatelessSession(Class entityClass) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Uni insert(Object entity) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Uni persist(Object entity) { + return DELEGATE.persist(entity); + } + + @Override + public Uni persistAndFlush(Object entity) { + return DELEGATE.persist(entity) + .chain(v -> DELEGATE.flush(entity.getClass())); + } + + @Override + public Uni delete(Object entity) { + return DELEGATE.delete(entity); + } + + @Override + public Uni update(Object entity) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Uni isPersistent(Object entity) { + return Uni.createFrom().item(DELEGATE.isPersistent(entity)); + } + + @Override + public Uni flush(Object entity) { + return DELEGATE.flush(entity); + } + + @Override + public Uni persist(Iterable entities) { + return DELEGATE.persist(entities); + } + + @Override + public Uni persist(Stream entities) { + return DELEGATE.persist(entities); + } + + @Override + public Uni persist(Object firstEntity, Object... entities) { + return DELEGATE.persist(firstEntity, entities); + } + + @Override + public Uni insert(Iterable entities) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Uni insert(Stream entities) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Uni insert(Object firstEntity, Object... entities) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Uni findById(Class entityClass, Object id) { + return (Uni) DELEGATE.findById(entityClass, id); + } + + @Override + public Uni findById(Class entityClass, Object id, LockModeType lockModeType) { + return (Uni) DELEGATE.findById(entityClass, id, lockModeType); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Object... params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Map params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheReactiveQuery findAll(Class entityClass) { + return DELEGATE.findAll(entityClass); + } + + @Override + public PanacheReactiveQuery findAll(Class entityClass, Sort sort) { + return DELEGATE.findAll(entityClass, sort); + } + + @Override + public Uni> list(Class entityClass, String query, Object... params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public Uni> list(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public Uni> list(Class entityClass, String query, Map params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public Uni> list(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public Uni> listAll(Class entityClass) { + return DELEGATE.listAll(entityClass); + } + + @Override + public Uni> listAll(Class entityClass, Sort sort) { + return DELEGATE.listAll(entityClass, sort); + } + + @Override + public Uni count(Class entityClass) { + return DELEGATE.count(entityClass); + } + + @Override + public Uni count(Class entityClass, String query, Object... params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Uni count(Class entityClass, String query, Map params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Uni deleteAll(Class entityClass) { + return DELEGATE.deleteAll(entityClass); + } + + @Override + public Uni deleteById(Class entityClass, Object id) { + return DELEGATE.deleteById(entityClass, id); + } + + @Override + public Uni delete(Class entityClass, String query, Object... params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Uni delete(Class entityClass, String query, Map params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Uni update(Class entityClass, String query, Object... params) { + return DELEGATE.update(entityClass, query, params).map(i -> i.longValue()); + } + + @Override + public Uni update(Class entityClass, String query, Map params) { + return DELEGATE.update(entityClass, query, params).map(i -> i.longValue()); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/PanacheManagedReactiveQueryImpl.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/PanacheManagedReactiveQueryImpl.java new file mode 100644 index 0000000000000..17b514e810bd9 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/PanacheManagedReactiveQueryImpl.java @@ -0,0 +1,139 @@ +package io.quarkus.hibernate.panache.runtime.hr; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import jakarta.persistence.LockModeType; + +import org.hibernate.query.Page; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.reactive.panache.common.runtime.CommonManagedPanacheQueryImpl; +import io.smallrye.mutiny.Uni; + +public class PanacheManagedReactiveQueryImpl implements PanacheReactiveQuery { + + final CommonManagedPanacheQueryImpl delegate; + + PanacheManagedReactiveQueryImpl(Uni session, String query, String originalQuery, String orderBy, + Object paramsArrayOrMap) { + delegate = new CommonManagedPanacheQueryImpl(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + PanacheManagedReactiveQueryImpl(CommonManagedPanacheQueryImpl delegate) { + this.delegate = delegate; + } + + @Override + public PanacheManagedReactiveQueryImpl page(Page page) { + delegate.page(page.getNumber(), page.getSize()); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl page(int pageIndex, int pageSize) { + delegate.page(pageIndex, pageSize); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl nextPage() { + delegate.nextPage(); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl previousPage() { + delegate.previousPage(); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl firstPage() { + delegate.firstPage(); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl lastPage() { + delegate.lastPage(); + return this; + } + + @Override + public Uni hasNextPage() { + return delegate.hasNextPage(); + } + + @Override + public Uni hasPreviousPage() { + return Uni.createFrom().item(delegate.hasPreviousPage()); + } + + @Override + public Uni pageCount() { + return delegate.pageCount().map(i -> i.longValue()); + } + + @Override + public Page page() { + return Page.page(delegate.page().size, delegate.page().index); + } + + @Override + public PanacheManagedReactiveQueryImpl range(int startIndex, int lastIndex) { + delegate.range(startIndex, lastIndex); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl withLock(LockModeType lockModeType) { + delegate.withLock(lockModeType); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl withHint(String hintName, Object value) { + delegate.withHint(hintName, value); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl filter(String filterName, Map parameters) { + delegate.filter(filterName, parameters); + return this; + } + + @Override + public PanacheManagedReactiveQueryImpl filter(String filterName) { + delegate.filter(filterName, Collections.emptyMap()); + return this; + } + + @Override + public Uni count() { + return delegate.count(); + } + + @Override + public Uni> list() { + return delegate.list(); + } + + @Override + public Uni firstResult() { + return delegate.firstResult(); + } + + @Override + public Uni singleResult() { + return delegate.singleResult(); + } + + @Override + public PanacheReactiveQuery project(Class type) { + return new PanacheManagedReactiveQueryImpl<>(delegate.project(type)); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/PanacheStatelessReactiveQueryImpl.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/PanacheStatelessReactiveQueryImpl.java new file mode 100644 index 0000000000000..55301bd6c4fd2 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/PanacheStatelessReactiveQueryImpl.java @@ -0,0 +1,139 @@ +package io.quarkus.hibernate.panache.runtime.hr; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import jakarta.persistence.LockModeType; + +import org.hibernate.query.Page; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.reactive.panache.common.runtime.CommonStatelessPanacheQueryImpl; +import io.smallrye.mutiny.Uni; + +public class PanacheStatelessReactiveQueryImpl implements PanacheReactiveQuery { + + final CommonStatelessPanacheQueryImpl delegate; + + PanacheStatelessReactiveQueryImpl(Uni session, String query, String originalQuery, String orderBy, + Object paramsArrayOrMap) { + delegate = new CommonStatelessPanacheQueryImpl(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + PanacheStatelessReactiveQueryImpl(CommonStatelessPanacheQueryImpl delegate) { + this.delegate = delegate; + } + + @Override + public PanacheStatelessReactiveQueryImpl page(Page page) { + delegate.page(page.getNumber(), page.getSize()); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl page(int pageIndex, int pageSize) { + delegate.page(pageIndex, pageSize); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl nextPage() { + delegate.nextPage(); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl previousPage() { + delegate.previousPage(); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl firstPage() { + delegate.firstPage(); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl lastPage() { + delegate.lastPage(); + return this; + } + + @Override + public Uni hasNextPage() { + return delegate.hasNextPage(); + } + + @Override + public Uni hasPreviousPage() { + return Uni.createFrom().item(delegate.hasPreviousPage()); + } + + @Override + public Uni pageCount() { + return delegate.pageCount().map(i -> i.longValue()); + } + + @Override + public Page page() { + return Page.page(delegate.page().size, delegate.page().index); + } + + @Override + public PanacheStatelessReactiveQueryImpl range(int startIndex, int lastIndex) { + delegate.range(startIndex, lastIndex); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl withLock(LockModeType lockModeType) { + delegate.withLock(lockModeType); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl withHint(String hintName, Object value) { + delegate.withHint(hintName, value); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl filter(String filterName, Map parameters) { + delegate.filter(filterName, parameters); + return this; + } + + @Override + public PanacheStatelessReactiveQueryImpl filter(String filterName) { + delegate.filter(filterName, Collections.emptyMap()); + return this; + } + + @Override + public Uni count() { + return delegate.count(); + } + + @Override + public Uni> list() { + return delegate.list(); + } + + @Override + public Uni firstResult() { + return delegate.firstResult(); + } + + @Override + public Uni singleResult() { + return delegate.singleResult(); + } + + @Override + public PanacheReactiveQuery project(Class type) { + return new PanacheStatelessReactiveQueryImpl<>(delegate.project(type)); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/StatelessReactiveJpaOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/StatelessReactiveJpaOperations.java new file mode 100644 index 0000000000000..79a0f1b2767fb --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/StatelessReactiveJpaOperations.java @@ -0,0 +1,24 @@ +package io.quarkus.hibernate.panache.runtime.hr; + +import java.util.List; + +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.reactive.panache.common.runtime.AbstractStatelessJpaOperations; +import io.smallrye.mutiny.Uni; + +public class StatelessReactiveJpaOperations extends AbstractStatelessJpaOperations> { + + @Override + protected PanacheReactiveQuery createPanacheQuery(Uni session, String query, + String originalQuery, + String orderBy, Object paramsArrayOrMap) { + return new PanacheStatelessReactiveQueryImpl<>(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + @Override + public Uni> list(PanacheReactiveQuery query) { + return (Uni) query.list(); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/StatelessReactiveOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/StatelessReactiveOperations.java new file mode 100644 index 0000000000000..1dc356970bb1d --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/hr/StatelessReactiveOperations.java @@ -0,0 +1,215 @@ +package io.quarkus.hibernate.panache.runtime.hr; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.panache.common.Sort; +import io.smallrye.mutiny.Uni; + +public class StatelessReactiveOperations implements PanacheReactiveOperations { + + public final static StatelessReactiveOperations INSTANCE = new StatelessReactiveOperations(); + private final static StatelessReactiveJpaOperations DELEGATE = new StatelessReactiveJpaOperations(); + + private StatelessReactiveOperations() { + } + + @Override + public Uni getSession(Class entityClass) { + // FIXME: this is wrong + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni getStatelessSession(Class entityClass) { + // FIXME: this is wrong + return DELEGATE.getSession(); + } + + @Override + public Uni insert(Object entity) { + return DELEGATE.insert(entity); + } + + @Override + public Uni persist(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni persistAndFlush(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni delete(Object entity) { + return DELEGATE.delete(entity); + } + + @Override + public Uni update(Object entity) { + return DELEGATE.update(entity); + } + + @Override + public Uni isPersistent(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni flush(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni persist(Iterable entities) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni persist(Stream entities) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni persist(Object firstEntity, Object... entities) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Uni insert(Iterable entities) { + return DELEGATE.insert(entities); + } + + @Override + public Uni insert(Stream entities) { + return DELEGATE.insert(entities); + } + + @Override + public Uni insert(Object firstEntity, Object... entities) { + return DELEGATE.insert(firstEntity, entities); + } + + @Override + public Uni findById(Class entityClass, Object id) { + return (Uni) DELEGATE.findById(entityClass, id); + } + + @Override + public Uni findById(Class entityClass, Object id, LockModeType lockModeType) { + return (Uni) DELEGATE.findById(entityClass, id, lockModeType); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Object... params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Map params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheReactiveQuery find(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheReactiveQuery findAll(Class entityClass) { + return DELEGATE.findAll(entityClass); + } + + @Override + public PanacheReactiveQuery findAll(Class entityClass, Sort sort) { + return DELEGATE.findAll(entityClass, sort); + } + + @Override + public Uni> list(Class entityClass, String query, Object... params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public Uni> list(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public Uni> list(Class entityClass, String query, Map params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public Uni> list(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public Uni> listAll(Class entityClass) { + return DELEGATE.listAll(entityClass); + } + + @Override + public Uni> listAll(Class entityClass, Sort sort) { + return DELEGATE.listAll(entityClass, sort); + } + + @Override + public Uni count(Class entityClass) { + return DELEGATE.count(entityClass); + } + + @Override + public Uni count(Class entityClass, String query, Object... params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Uni count(Class entityClass, String query, Map params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Uni deleteAll(Class entityClass) { + return DELEGATE.deleteAll(entityClass); + } + + @Override + public Uni deleteById(Class entityClass, Object id) { + return DELEGATE.deleteById(entityClass, id); + } + + @Override + public Uni delete(Class entityClass, String query, Object... params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Uni delete(Class entityClass, String query, Map params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Uni update(Class entityClass, String query, Object... params) { + return DELEGATE.update(entityClass, query, params).map(i -> i.longValue()); + } + + @Override + public Uni update(Class entityClass, String query, Map params) { + return DELEGATE.update(entityClass, query, params).map(i -> i.longValue()); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/ManagedBlockingJpaOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/ManagedBlockingJpaOperations.java new file mode 100644 index 0000000000000..40d48492129e7 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/ManagedBlockingJpaOperations.java @@ -0,0 +1,29 @@ +package io.quarkus.hibernate.panache.runtime.orm; + +import java.util.List; +import java.util.stream.Stream; + +import org.hibernate.Session; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; + +public class ManagedBlockingJpaOperations extends AbstractManagedJpaOperations> { + + @Override + protected PanacheBlockingQuery createPanacheQuery(Session session, String query, String originalQuery, + String orderBy, Object paramsArrayOrMap) { + return new PanacheBlockingQueryImpl<>(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + @Override + public List list(PanacheBlockingQuery query) { + return query.list(); + } + + @Override + public Stream stream(PanacheBlockingQuery query) { + return query.stream(); + } + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/ManagedBlockingOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/ManagedBlockingOperations.java new file mode 100644 index 0000000000000..8a4a82ea7a6fe --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/ManagedBlockingOperations.java @@ -0,0 +1,263 @@ +package io.quarkus.hibernate.panache.runtime.orm; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; + +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.panache.common.Sort; + +public class ManagedBlockingOperations implements PanacheBlockingOperations { + + public final static ManagedBlockingOperations INSTANCE = new ManagedBlockingOperations(); + private final static ManagedBlockingJpaOperations DELEGATE = new ManagedBlockingJpaOperations(); + + private ManagedBlockingOperations() { + } + + @Override + public Session getSession(Class entityClass) { + return DELEGATE.getSession(entityClass); + } + + @Override + public StatelessSession getStatelessSession(Class entityClass) { + // FIXME: this is wrong + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Void insert(Object entity) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Void persist(Object entity) { + DELEGATE.persist(entity); + return null; + } + + @Override + public Void persistAndFlush(Object entity) { + DELEGATE.persist(entity); + DELEGATE.flush(entity); + return null; + } + + @Override + public Void delete(Object entity) { + DELEGATE.delete(entity); + return null; + } + + @Override + public Void update(Object entity) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Boolean isPersistent(Object entity) { + return DELEGATE.isPersistent(entity); + } + + @Override + public Void flush(Object entity) { + DELEGATE.flush(entity); + return null; + } + + @Override + public Void persist(Iterable entities) { + DELEGATE.persist(entities); + return null; + } + + @Override + public Void persist(Stream entities) { + DELEGATE.persist(entities); + return null; + } + + @Override + public Void persist(Object firstEntity, Object... entities) { + DELEGATE.persist(firstEntity, entities); + return null; + } + + @Override + public Void insert(Iterable entities) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Void insert(Stream entities) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Void insert(Object firstEntity, Object... entities) { + throw new UnsupportedOperationException("Stateless operations not supported"); + } + + @Override + public Object findById(Class entityClass, Object id) { + return DELEGATE.findById(entityClass, id); + } + + @Override + public Object findById(Class entityClass, Object id, LockModeType lockModeType) { + return DELEGATE.findById(entityClass, id, lockModeType); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Object... params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Map params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheBlockingQuery findAll(Class entityClass) { + return DELEGATE.findAll(entityClass); + } + + @Override + public PanacheBlockingQuery findAll(Class entityClass, Sort sort) { + return DELEGATE.findAll(entityClass, sort); + } + + @Override + public List list(Class entityClass, String query, Object... params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public List list(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public List list(Class entityClass, String query, Map params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public List list(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public List listAll(Class entityClass) { + return DELEGATE.listAll(entityClass); + } + + @Override + public List listAll(Class entityClass, Sort sort) { + return DELEGATE.listAll(entityClass, sort); + } + + @Override + public Long count(Class entityClass) { + return DELEGATE.count(entityClass); + } + + @Override + public Long count(Class entityClass, String query, Object... params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Long count(Class entityClass, String query, Map params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Long deleteAll(Class entityClass) { + return DELEGATE.deleteAll(entityClass); + } + + @Override + public Boolean deleteById(Class entityClass, Object id) { + return DELEGATE.deleteById(entityClass, id); + } + + @Override + public Long delete(Class entityClass, String query, Object... params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Long delete(Class entityClass, String query, Map params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Long update(Class entityClass, String query, Object... params) { + return (long) DELEGATE.update(entityClass, query, params); + } + + @Override + public Long update(Class entityClass, String query, Map params) { + return (long) DELEGATE.update(entityClass, query, params); + } + + @Override + public Optional findByIdOptional(Class entityClass, Object id) { + return DELEGATE.findByIdOptional(entityClass, id); + } + + @Override + public Optional findByIdOptional(Class entityClass, Object id, LockModeType lockModeType) { + return DELEGATE.findByIdOptional(entityClass, id, lockModeType); + } + + @Override + public Stream stream(Class entityClass, String query, Object... params) { + return DELEGATE.stream(entityClass, query, params); + } + + @Override + public Stream stream(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.stream(entityClass, query, sort, params); + } + + @Override + public Stream stream(Class entityClass, String query, Map params) { + return DELEGATE.stream(entityClass, query, params); + } + + @Override + public Stream stream(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.stream(entityClass, query, sort, params); + } + + @Override + public Stream streamAll(Class entityClass, Sort sort) { + return DELEGATE.streamAll(entityClass, sort); + } + + @Override + public Stream streamAll(Class entityClass) { + return DELEGATE.streamAll(entityClass); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/PanacheBlockingQueryImpl.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/PanacheBlockingQueryImpl.java new file mode 100644 index 0000000000000..e8f6244327160 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/PanacheBlockingQueryImpl.java @@ -0,0 +1,156 @@ +package io.quarkus.hibernate.panache.runtime.orm; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.SharedSessionContract; +import org.hibernate.query.Page; + +import io.quarkus.hibernate.orm.panache.common.runtime.CommonPanacheQueryImpl; +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; + +public class PanacheBlockingQueryImpl implements PanacheBlockingQuery { + + final CommonPanacheQueryImpl delegate; + + PanacheBlockingQueryImpl(SharedSessionContract session, String query, String originalQuery, String orderBy, + Object paramsArrayOrMap) { + delegate = new CommonPanacheQueryImpl(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + PanacheBlockingQueryImpl(CommonPanacheQueryImpl delegate) { + this.delegate = delegate; + } + + @Override + public PanacheBlockingQueryImpl page(Page page) { + delegate.page(page.getNumber(), page.getSize()); + return this; + } + + @Override + public PanacheBlockingQueryImpl page(int pageIndex, int pageSize) { + delegate.page(pageIndex, pageSize); + return this; + } + + @Override + public PanacheBlockingQueryImpl nextPage() { + delegate.nextPage(); + return this; + } + + @Override + public PanacheBlockingQueryImpl previousPage() { + delegate.previousPage(); + return this; + } + + @Override + public PanacheBlockingQueryImpl firstPage() { + delegate.firstPage(); + return this; + } + + @Override + public PanacheBlockingQueryImpl lastPage() { + delegate.lastPage(); + return this; + } + + @Override + public Boolean hasNextPage() { + return delegate.hasNextPage(); + } + + @Override + public Boolean hasPreviousPage() { + return delegate.hasPreviousPage(); + } + + @Override + public Long pageCount() { + return (long) delegate.pageCount(); + } + + @Override + public Page page() { + return Page.page(delegate.page().size, delegate.page().index); + } + + @Override + public PanacheBlockingQueryImpl range(int startIndex, int lastIndex) { + delegate.range(startIndex, lastIndex); + return this; + } + + @Override + public PanacheBlockingQueryImpl withLock(LockModeType lockModeType) { + delegate.withLock(lockModeType); + return this; + } + + @Override + public PanacheBlockingQueryImpl withHint(String hintName, Object value) { + delegate.withHint(hintName, value); + return this; + } + + @Override + public PanacheBlockingQueryImpl filter(String filterName, Map parameters) { + delegate.filter(filterName, parameters); + return this; + } + + @Override + public PanacheBlockingQueryImpl filter(String filterName) { + delegate.filter(filterName, Collections.emptyMap()); + return this; + } + + @Override + public Long count() { + return delegate.count(); + } + + @Override + public List list() { + return delegate.list(); + } + + @Override + public Entity firstResult() { + return delegate.firstResult(); + } + + @Override + public Entity singleResult() { + return delegate.singleResult(); + } + + @Override + public PanacheBlockingQueryImpl project(Class type) { + return new PanacheBlockingQueryImpl<>(delegate.project(type)); + } + + @Override + public Stream stream() { + return delegate.stream(); + } + + @Override + public Optional firstResultOptional() { + return delegate.firstResultOptional(); + } + + @Override + public Optional singleResultOptional() { + return delegate.singleResultOptional(); + } + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/StatelessBlockingJpaOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/StatelessBlockingJpaOperations.java new file mode 100644 index 0000000000000..733e606fb3dea --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/StatelessBlockingJpaOperations.java @@ -0,0 +1,29 @@ +package io.quarkus.hibernate.panache.runtime.orm; + +import java.util.List; +import java.util.stream.Stream; + +import org.hibernate.StatelessSession; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractStatelessJpaOperations; +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; + +public class StatelessBlockingJpaOperations extends AbstractStatelessJpaOperations> { + + @Override + protected PanacheBlockingQuery createPanacheQuery(StatelessSession session, String query, String originalQuery, + String orderBy, Object paramsArrayOrMap) { + return new PanacheBlockingQueryImpl<>(session, query, originalQuery, orderBy, paramsArrayOrMap); + } + + @Override + public List list(PanacheBlockingQuery query) { + return query.list(); + } + + @Override + public Stream stream(PanacheBlockingQuery query) { + return query.stream(); + } + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/StatelessBlockingOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/StatelessBlockingOperations.java new file mode 100644 index 0000000000000..bbd7a65eebef8 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/orm/StatelessBlockingOperations.java @@ -0,0 +1,261 @@ +package io.quarkus.hibernate.panache.runtime.orm; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; + +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.panache.common.Sort; + +public class StatelessBlockingOperations implements PanacheBlockingOperations { + + public final static StatelessBlockingOperations INSTANCE = new StatelessBlockingOperations(); + private final static StatelessBlockingJpaOperations DELEGATE = new StatelessBlockingJpaOperations(); + + private StatelessBlockingOperations() { + } + + @Override + public Session getSession(Class entityClass) { + // FIXME: this is wrong + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public StatelessSession getStatelessSession(Class entityClass) { + return DELEGATE.getSession(entityClass); + } + + @Override + public Void insert(Object entity) { + DELEGATE.insert(entity); + return null; + } + + @Override + public Void persist(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void persistAndFlush(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void delete(Object entity) { + DELEGATE.delete(entity); + return null; + } + + @Override + public Void update(Object entity) { + DELEGATE.update(entity); + return null; + } + + @Override + public Boolean isPersistent(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void flush(Object entity) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void persist(Iterable entities) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void persist(Stream entities) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void persist(Object firstEntity, Object... entities) { + throw new UnsupportedOperationException("Managed operations not supported"); + } + + @Override + public Void insert(Iterable entities) { + DELEGATE.insert(entities); + return null; + } + + @Override + public Void insert(Stream entities) { + DELEGATE.insert(entities); + return null; + } + + @Override + public Void insert(Object firstEntity, Object... entities) { + DELEGATE.insert(entities); + return null; + } + + @Override + public Object findById(Class entityClass, Object id) { + return DELEGATE.findById(entityClass, id); + } + + @Override + public Object findById(Class entityClass, Object id, LockModeType lockModeType) { + return DELEGATE.findById(entityClass, id, lockModeType); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Object... params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Map params) { + return DELEGATE.find(entityClass, query, params); + } + + @Override + public PanacheBlockingQuery find(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.find(entityClass, query, sort, params); + } + + @Override + public PanacheBlockingQuery findAll(Class entityClass) { + return DELEGATE.findAll(entityClass); + } + + @Override + public PanacheBlockingQuery findAll(Class entityClass, Sort sort) { + return DELEGATE.findAll(entityClass, sort); + } + + @Override + public List list(Class entityClass, String query, Object... params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public List list(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public List list(Class entityClass, String query, Map params) { + return DELEGATE.list(entityClass, query, params); + } + + @Override + public List list(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.list(entityClass, query, sort, params); + } + + @Override + public List listAll(Class entityClass) { + return DELEGATE.listAll(entityClass); + } + + @Override + public List listAll(Class entityClass, Sort sort) { + return DELEGATE.listAll(entityClass, sort); + } + + @Override + public Long count(Class entityClass) { + return DELEGATE.count(entityClass); + } + + @Override + public Long count(Class entityClass, String query, Object... params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Long count(Class entityClass, String query, Map params) { + return DELEGATE.count(entityClass, query, params); + } + + @Override + public Long deleteAll(Class entityClass) { + return DELEGATE.deleteAll(entityClass); + } + + @Override + public Boolean deleteById(Class entityClass, Object id) { + return DELEGATE.deleteById(entityClass, id); + } + + @Override + public Long delete(Class entityClass, String query, Object... params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Long delete(Class entityClass, String query, Map params) { + return DELEGATE.delete(entityClass, query, params); + } + + @Override + public Long update(Class entityClass, String query, Object... params) { + return (long) DELEGATE.update(entityClass, query, params); + } + + @Override + public Long update(Class entityClass, String query, Map params) { + return (long) DELEGATE.update(entityClass, query, params); + } + + @Override + public Optional findByIdOptional(Class entityClass, Object id) { + return DELEGATE.findByIdOptional(entityClass, id); + } + + @Override + public Optional findByIdOptional(Class entityClass, Object id, LockModeType lockModeType) { + return DELEGATE.findByIdOptional(entityClass, id, lockModeType); + } + + @Override + public Stream stream(Class entityClass, String query, Object... params) { + return DELEGATE.stream(entityClass, query, params); + } + + @Override + public Stream stream(Class entityClass, String query, Sort sort, Object... params) { + return DELEGATE.stream(entityClass, query, sort, params); + } + + @Override + public Stream stream(Class entityClass, String query, Map params) { + return DELEGATE.stream(entityClass, query, params); + } + + @Override + public Stream stream(Class entityClass, String query, Sort sort, Map params) { + return DELEGATE.stream(entityClass, query, sort, params); + } + + @Override + public Stream streamAll(Class entityClass, Sort sort) { + return DELEGATE.streamAll(entityClass, sort); + } + + @Override + public Stream streamAll(Class entityClass) { + return DELEGATE.streamAll(entityClass); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheBlockingOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheBlockingOperations.java new file mode 100644 index 0000000000000..1b4d48944a41c --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheBlockingOperations.java @@ -0,0 +1,34 @@ +package io.quarkus.hibernate.panache.runtime.spi; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; + +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import io.quarkus.panache.common.Sort; + +public interface PanacheBlockingOperations extends + PanacheOperations, PanacheBlockingQuery, Long, Void, Boolean, Session, StatelessSession> { + + Optional findByIdOptional(Class entityClass, Object id); + + Optional findByIdOptional(Class entityClass, Object id, LockModeType lockModeType); + + Stream stream(Class entityClass, String query, Object... params); + + Stream stream(Class entityClass, String query, Sort sort, Object... params); + + Stream stream(Class entityClass, String query, Map params); + + Stream stream(Class entityClass, String query, Sort sort, Map params); + + Stream streamAll(Class entityClass, Sort sort); + + Stream streamAll(Class entityClass); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheOperations.java new file mode 100644 index 0000000000000..528dfea2d5500 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheOperations.java @@ -0,0 +1,111 @@ +package io.quarkus.hibernate.panache.runtime.spi; + +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import io.quarkus.hibernate.panache.runtime.hr.ManagedReactiveOperations; +import io.quarkus.hibernate.panache.runtime.hr.StatelessReactiveOperations; +import io.quarkus.hibernate.panache.runtime.orm.ManagedBlockingOperations; +import io.quarkus.hibernate.panache.runtime.orm.StatelessBlockingOperations; +import io.quarkus.panache.common.Sort; + +public interface PanacheOperations { + + static PanacheBlockingOperations getBlockingManaged() { + return ManagedBlockingOperations.INSTANCE; + } + + static PanacheReactiveOperations getReactiveManaged() { + return ManagedReactiveOperations.INSTANCE; + } + + static PanacheBlockingOperations getBlockingStateless() { + return StatelessBlockingOperations.INSTANCE; + } + + static PanacheReactiveOperations getReactiveStateless() { + return StatelessReactiveOperations.INSTANCE; + } + + // Operations + + Session getSession(Class entityClass); + + StatelessSession getStatelessSession(Class entityClass); + + Completion insert(Object entity); + + Completion persist(Object entity); + + Completion persistAndFlush(Object entity); + + Completion delete(Object entity); + + Completion update(Object entity); + + Confirmation isPersistent(Object entity); + + Completion flush(Object entity); + + Completion persist(Iterable entities); + + Completion persist(Stream entities); + + Completion persist(Object firstEntity, Object... entities); + + Completion insert(Iterable entities); + + Completion insert(Stream entities); + + Completion insert(Object firstEntity, Object... entities); + + // Queries + + One findById(Class entityClass, Object id); + + One findById(Class entityClass, Object id, LockModeType lockModeType); + + Query find(Class entityClass, String query, Object... params); + + Query find(Class entityClass, String query, Sort sort, Object... params); + + Query find(Class entityClass, String query, Map params); + + Query find(Class entityClass, String query, Sort sort, Map params); + + Query findAll(Class entityClass); + + Query findAll(Class entityClass, Sort sort); + + Many list(Class entityClass, String query, Object... params); + + Many list(Class entityClass, String query, Sort sort, Object... params); + + Many list(Class entityClass, String query, Map params); + + Many list(Class entityClass, String query, Sort sort, Map params); + + Many listAll(Class entityClass); + + Many listAll(Class entityClass, Sort sort); + + Count count(Class entityClass); + + Count count(Class entityClass, String query, Object... params); + + Count count(Class entityClass, String query, Map params); + + Count deleteAll(Class entityClass); + + Confirmation deleteById(Class entityClass, Object id); + + Count delete(Class entityClass, String query, Object... params); + + Count delete(Class entityClass, String query, Map params); + + Count update(Class entityClass, String query, Object... params); + + Count update(Class entityClass, String query, Map params); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheReactiveOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheReactiveOperations.java new file mode 100644 index 0000000000000..7ab1c0c7ca35e --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/runtime/spi/PanacheReactiveOperations.java @@ -0,0 +1,16 @@ +package io.quarkus.hibernate.panache.runtime.spi; + +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.smallrye.mutiny.Uni; + +public interface PanacheReactiveOperations + extends + PanacheOperations, Uni>, PanacheReactiveQuery, Uni, Uni, Uni, Uni, Uni> { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/PanacheStatelessEntityOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/PanacheStatelessEntityOperations.java new file mode 100644 index 0000000000000..96db5bff29f66 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/PanacheStatelessEntityOperations.java @@ -0,0 +1,20 @@ +package io.quarkus.hibernate.panache.stateless; + +import io.quarkus.hibernate.panache.PanacheEntityMarker; + +public interface PanacheStatelessEntityOperations extends PanacheEntityMarker { + /** + * Insert this entity in the database. This will set your ID field if it is not already set. + */ + public Completion insert(); + + /** + * Delete this entity from the database, if it is already persisted. + */ + public Completion delete(); + + /** + * Update this entity to the database. + */ + public Completion update(); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/PanacheStatelessRepositoryOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/PanacheStatelessRepositoryOperations.java new file mode 100644 index 0000000000000..309bec74a8ec6 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/PanacheStatelessRepositoryOperations.java @@ -0,0 +1,73 @@ +package io.quarkus.hibernate.panache.stateless; + +import java.util.Map; +import java.util.stream.Stream; + +public interface PanacheStatelessRepositoryOperations { + + // Operations + + /** + * Returns the {@link Session} for the entity class for extra operations (eg. CriteriaQueries) + * + * @return the {@link Session} for the entity class + */ + Session getSession(); + + /** + * Insert the given entity in the database. + * + * @param entity the entity to insert. + * @see #insert(Iterable) + * @see #insert(Stream) + * @see #insert(Object, Object...) + */ + Completion insert(Entity entity); + + /** + * Delete the given entity from the database. + * + * @param entity the entity to delete. + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #deleteAll() + */ + Completion delete(Entity entity); + + /** + * Update the given entity in the database. + * + * @param entity the entity to update. + */ + Completion update(Entity entity); + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Stream) + * @see #insert(Object,Object...) + */ + Completion insert(Iterable entities); + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Iterable) + * @see #insert(Object,Object...) + */ + Completion insert(Stream entities); + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Stream) + * @see #insert(Iterable) + */ + Completion insert(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities); +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingEntity.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingEntity.java new file mode 100644 index 0000000000000..f65dabc70d015 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingEntity.java @@ -0,0 +1,49 @@ +package io.quarkus.hibernate.panache.stateless.blocking; + +import java.util.Map; +import java.util.stream.Stream; + +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.stateless.PanacheStatelessEntityOperations; +import io.quarkus.panache.common.Parameters; + +public interface PanacheStatelessBlockingEntity extends PanacheStatelessEntityOperations { + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingStateless(); + } + + /** + * Insert this entity in the database. This will set your ID field if it is not already set. + * + * @see #insert(Iterable) + * @see #insert(Stream) + * @see #insert(Object, Object...) + */ + @Override + public default Void insert() { + return operations().insert(this); + } + + /** + * Delete this entity from the database. + * + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #delete(String, Parameters) + * @see #deleteAll() + */ + @Override + public default Void delete() { + return operations().delete(this); + } + + /** + * Update this entity in the database. + */ + @Override + public default Void update() { + return operations().update(this); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepository.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepository.java new file mode 100644 index 0000000000000..ffb56ee74d9ad --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepository.java @@ -0,0 +1,5 @@ +package io.quarkus.hibernate.panache.stateless.blocking; + +public interface PanacheStatelessBlockingRepository extends PanacheStatelessBlockingRepositoryBase { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryBase.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryBase.java new file mode 100644 index 0000000000000..65fdcdbe9d4a3 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryBase.java @@ -0,0 +1,10 @@ +package io.quarkus.hibernate.panache.stateless.blocking; + +import io.quarkus.hibernate.panache.PanacheRepositorySwitcher; + +public interface PanacheStatelessBlockingRepositoryBase + extends PanacheStatelessBlockingRepositoryOperations, + PanacheStatelessBlockingRepositoryQueries, + PanacheRepositorySwitcher { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryOperations.java new file mode 100644 index 0000000000000..07cd93fc481cf --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryOperations.java @@ -0,0 +1,104 @@ +package io.quarkus.hibernate.panache.stateless.blocking; + +import java.util.Map; +import java.util.stream.Stream; + +import org.hibernate.StatelessSession; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.stateless.PanacheStatelessRepositoryOperations; + +public interface PanacheStatelessBlockingRepositoryOperations + extends PanacheStatelessRepositoryOperations { + + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingStateless(); + } + + // Operations + + /** + * Returns the {@link StatelessSession} for the entity class for extra operations (eg. CriteriaQueries) + * + * @return the {@link StatelessSession} for the entity class + */ + default StatelessSession getSession() { + return operations().getStatelessSession(getEntityClass()); + } + + /** + * Insert the given entity in the database, if not already inserted. + * + * @param entity the entity to insert. + * @see #insert(Iterable) + * @see #insert(Stream) + * @see #insert(Object, Object...) + */ + default Void insert(Entity entity) { + return operations().insert(entity); + } + + /** + * Delete the given entity from the database, if it is already inserted. + * + * @param entity the entity to delete. + * @see #isInsertent(Object) + * @see #delete(String, Object...) + * @see #delete(String, Map) + * @see #deleteAll() + */ + default Void delete(Entity entity) { + return operations().delete(entity); + } + + /** + * Update the given entity in the database. + * + * @param entity the entity to update. + */ + default Void update(Entity entity) { + return operations().update(entity); + } + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Stream) + * @see #insert(Object,Object...) + */ + default Void insert(Iterable entities) { + return operations().insert(entities); + } + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Iterable) + * @see #insert(Object,Object...) + */ + default Void insert(Stream entities) { + return operations().insert(entities); + } + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Stream) + * @see #insert(Iterable) + */ + default Void insert(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) { + return operations().insert(firstEntity, entities); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryQueries.java new file mode 100644 index 0000000000000..28c418dd99468 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/blocking/PanacheStatelessBlockingRepositoryQueries.java @@ -0,0 +1,180 @@ +package io.quarkus.hibernate.panache.stateless.blocking; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import jakarta.persistence.LockModeType; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.blocking.PanacheBlockingQuery; +import io.quarkus.hibernate.panache.blocking.PanacheRepositoryBlockingQueries; +import io.quarkus.hibernate.panache.runtime.spi.PanacheBlockingOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.panache.common.Sort; + +public interface PanacheStatelessBlockingRepositoryQueries extends PanacheRepositoryBlockingQueries { + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheBlockingOperations operations() { + return PanacheOperations.getBlockingStateless(); + } + + @Override + default Entity findById(Id id) { + return (Entity) operations().findById(getEntityClass(), id); + } + + @Override + default Entity findById(Id id, LockModeType lockModeType) { + return (Entity) operations().findById(getEntityClass(), id, lockModeType); + } + + @Override + default Optional findByIdOptional(Id id) { + return (Optional) operations().findByIdOptional(getEntityClass(), id); + } + + @Override + default Optional findByIdOptional(Id id, LockModeType lockModeType) { + return (Optional) operations().findByIdOptional(getEntityClass(), id, lockModeType); + } + + @Override + default PanacheBlockingQuery find(String query, Object... params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheBlockingQuery find(String query, Sort sort, Object... params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheBlockingQuery find(String query, Map params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheBlockingQuery find(String query, Sort sort, Map params) { + return (PanacheBlockingQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheBlockingQuery findAll() { + return (PanacheBlockingQuery) operations().findAll(getEntityClass()); + } + + @Override + default PanacheBlockingQuery findAll(Sort sort) { + return (PanacheBlockingQuery) operations().findAll(getEntityClass(), sort); + } + + @Override + default List list(String query, Object... params) { + return (List) operations().list(getEntityClass(), query, params); + } + + @Override + default List list(String query, Sort sort, Object... params) { + return (List) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default List list(String query, Map params) { + return (List) operations().list(getEntityClass(), query, params); + } + + @Override + default List list(String query, Sort sort, Map params) { + return (List) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default List listAll() { + return (List) operations().listAll(getEntityClass()); + } + + @Override + default List listAll(Sort sort) { + return (List) operations().listAll(getEntityClass(), sort); + } + + @Override + default Stream stream(String query, Object... params) { + return (Stream) operations().stream(getEntityClass(), query, params); + } + + @Override + default Stream stream(String query, Sort sort, Object... params) { + return (Stream) operations().stream(getEntityClass(), query, sort, params); + } + + @Override + default Stream stream(String query, Map params) { + return (Stream) operations().stream(getEntityClass(), query, params); + } + + @Override + default Stream stream(String query, Sort sort, Map params) { + return (Stream) operations().stream(getEntityClass(), query, sort, params); + } + + @Override + default Stream streamAll(Sort sort) { + return (Stream) operations().streamAll(getEntityClass(), sort); + } + + @Override + default Stream streamAll() { + return (Stream) operations().streamAll(getEntityClass()); + } + + @Override + default Long count() { + return operations().count(getEntityClass()); + } + + @Override + default Long count(String query, Object... params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Long count(String query, Map params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Long deleteAll() { + return operations().deleteAll(getEntityClass()); + } + + @Override + default Boolean deleteById(Id id) { + return operations().deleteById(getEntityClass(), id); + } + + @Override + default Long delete(String query, Object... params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Long delete(String query, Map params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Long update(String query, Object... params) { + return operations().update(getEntityClass(), query, params); + } + + @Override + default Long update(String query, Map params) { + return operations().update(getEntityClass(), query, params); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveEntity.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveEntity.java new file mode 100644 index 0000000000000..4be9c599000a5 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveEntity.java @@ -0,0 +1,37 @@ +package io.quarkus.hibernate.panache.stateless.reactive; + +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.hibernate.panache.stateless.PanacheStatelessEntityOperations; +import io.smallrye.mutiny.Uni; + +public interface PanacheStatelessReactiveEntity extends PanacheStatelessEntityOperations, Uni> { + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveManaged(); + } + + /** + * Insert this entity in the database. This will set your ID field if it is not already set. + */ + @Override + public default Uni insert() { + return operations().insert(this); + } + + /** + * Delete this entity from the database. + */ + @Override + public default Uni delete() { + return operations().delete(this); + } + + /** + * Update this entity in the database. + */ + @Override + public default Uni update() { + return operations().update(this); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepository.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepository.java new file mode 100644 index 0000000000000..c6d5c6f5841f7 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepository.java @@ -0,0 +1,4 @@ +package io.quarkus.hibernate.panache.stateless.reactive; + +public interface PanacheStatelessReactiveRepository extends PanacheStatelessReactiveRepositoryBase { +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryBase.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryBase.java new file mode 100644 index 0000000000000..892353c5ac83d --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryBase.java @@ -0,0 +1,10 @@ +package io.quarkus.hibernate.panache.stateless.reactive; + +import io.quarkus.hibernate.panache.PanacheRepositorySwitcher; + +public interface PanacheStatelessReactiveRepositoryBase + extends PanacheStatelessReactiveRepositoryOperations, + PanacheStatelessReactiveRepositoryQueries, + PanacheRepositorySwitcher { + +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryOperations.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryOperations.java new file mode 100644 index 0000000000000..6a9b0191283dd --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryOperations.java @@ -0,0 +1,98 @@ +package io.quarkus.hibernate.panache.stateless.reactive; + +import java.util.stream.Stream; + +import org.hibernate.reactive.mutiny.Mutiny; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.hibernate.panache.stateless.PanacheStatelessRepositoryOperations; +import io.smallrye.mutiny.Uni; + +public interface PanacheStatelessReactiveRepositoryOperations + extends PanacheStatelessRepositoryOperations, Uni, Uni, Id> { + + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveStateless(); + } + + // Operations + + /** + * Returns the {@link Mutiny.StatelessSession} for the entity class for extra operations (eg. CriteriaQueries) + * + * @return the {@link Mutiny.StatelessSession} for the entity class + */ + default Uni getSession() { + // FIXME: this is false + return operations().getStatelessSession(getEntityClass()); + } + + /** + * Insert the given entity in the database. + * + * @param entity the entity to insert. + */ + default Uni insert(Entity entity) { + return operations().insert(entity); + } + + /** + * Delete the given entity from the database. + * + * @param entity the entity to delete. + */ + default Uni delete(Entity entity) { + return operations().delete(entity); + } + + /** + * Update the given entity in the database. + * + * @param entity the entity to update. + */ + default Uni update(Entity entity) { + return operations().update(entity); + } + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Stream) + * @see #insert(Object,Object...) + */ + default Uni insert(Iterable entities) { + return operations().insert(entities); + } + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Iterable) + * @see #insert(Object,Object...) + */ + default Uni insert(Stream entities) { + return operations().insert(entities); + } + + /** + * Insert all given entities. + * + * @param entities the entities to insert + * @see #insert(Object) + * @see #insert(Stream) + * @see #insert(Iterable) + */ + default Uni insert(Entity firstEntity, @SuppressWarnings("unchecked") Entity... entities) { + return operations().insert(firstEntity, entities); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryQueries.java b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryQueries.java new file mode 100644 index 0000000000000..9128046b1c598 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/java/io/quarkus/hibernate/panache/stateless/reactive/PanacheStatelessReactiveRepositoryQueries.java @@ -0,0 +1,139 @@ +package io.quarkus.hibernate.panache.stateless.reactive; + +import java.util.List; +import java.util.Map; + +import jakarta.persistence.LockModeType; + +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.panache.reactive.PanacheReactiveQuery; +import io.quarkus.hibernate.panache.reactive.PanacheRepositoryReactiveQueries; +import io.quarkus.hibernate.panache.runtime.spi.PanacheOperations; +import io.quarkus.hibernate.panache.runtime.spi.PanacheReactiveOperations; +import io.quarkus.panache.common.Sort; +import io.smallrye.mutiny.Uni; + +public interface PanacheStatelessReactiveRepositoryQueries extends PanacheRepositoryReactiveQueries { + private Class getEntityClass() { + return AbstractJpaOperations.getRepositoryEntityClass(getClass()); + } + + private PanacheReactiveOperations operations() { + return PanacheOperations.getReactiveStateless(); + } + + @Override + default Entity findById(Id id) { + return (Entity) operations().findById(getEntityClass(), id); + } + + @Override + default Entity findById(Id id, LockModeType lockModeType) { + return (Entity) operations().findById(getEntityClass(), id, lockModeType); + } + + @Override + default PanacheReactiveQuery find(String query, Object... params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheReactiveQuery find(String query, Sort sort, Object... params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheReactiveQuery find(String query, Map params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, params); + } + + @Override + default PanacheReactiveQuery find(String query, Sort sort, Map params) { + return (PanacheReactiveQuery) operations().find(getEntityClass(), query, sort, params); + } + + @Override + default PanacheReactiveQuery findAll() { + return (PanacheReactiveQuery) operations().findAll(getEntityClass()); + } + + @Override + default PanacheReactiveQuery findAll(Sort sort) { + return (PanacheReactiveQuery) operations().findAll(getEntityClass(), sort); + } + + @Override + default Uni> list(String query, Object... params) { + return (Uni) operations().list(getEntityClass(), query, params); + } + + @Override + default Uni> list(String query, Sort sort, Object... params) { + return (Uni) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default Uni> list(String query, Map params) { + return (Uni) operations().list(getEntityClass(), query, params); + } + + @Override + default Uni> list(String query, Sort sort, Map params) { + return (Uni) operations().list(getEntityClass(), query, sort, params); + } + + @Override + default Uni> listAll() { + return (Uni) operations().listAll(getEntityClass()); + } + + @Override + default Uni> listAll(Sort sort) { + return (Uni) operations().listAll(getEntityClass(), sort); + } + + @Override + default Uni count() { + return operations().count(getEntityClass()); + } + + @Override + default Uni count(String query, Object... params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Uni count(String query, Map params) { + return operations().count(getEntityClass(), query, params); + } + + @Override + default Uni deleteAll() { + return operations().deleteAll(getEntityClass()); + } + + @Override + default Uni deleteById(Id id) { + return operations().deleteById(getEntityClass(), id); + } + + @Override + default Uni delete(String query, Object... params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Uni delete(String query, Map params) { + return operations().delete(getEntityClass(), query, params); + } + + @Override + default Uni update(String query, Object... params) { + return operations().update(getEntityClass(), query, params); + } + + @Override + default Uni update(String query, Map params) { + return operations().update(getEntityClass(), query, params); + } +} diff --git a/extensions/panache/hibernate-panache/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/panache/hibernate-panache/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000000000..31e66da8a035f --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,23 @@ +--- +artifact: ${project.groupId}:${project.artifactId}:${project.version} +name: "Hibernate with Panache.Next" +metadata: + keywords: + - "hibernate-orm-panache" + - "hibernate-hr-panache" + - "panache" + - "hibernate" + - "hibernate-reactive" + - "jakarta-data" + - "jpa" + guide: "https://quarkus.io/guides/hibernate-panache" + categories: + - "data" + status: "experimental" + config: + - "quarkus.datasource." + - "quarkus.hibernate-orm." + codestart: + name: "hibernate-panache" + languages: ["java"] + artifact: "io.quarkus:quarkus-project-core-extension-codestarts" diff --git a/extensions/panache/hibernate-panache/runtime/src/test/java/io/quarkus/hibernate/panache/MetamodelTest.java b/extensions/panache/hibernate-panache/runtime/src/test/java/io/quarkus/hibernate/panache/MetamodelTest.java new file mode 100644 index 0000000000000..c8ffedee3e0d4 --- /dev/null +++ b/extensions/panache/hibernate-panache/runtime/src/test/java/io/quarkus/hibernate/panache/MetamodelTest.java @@ -0,0 +1,11 @@ +package io.quarkus.hibernate.panache; + +public class MetamodelTest { + // this only tests that we generated the metamodel, no need to run anything. + public void test() { + String orm = WithId_.ID; + String jd = _WithId.ID; + + // FIXME: .Long, .String, .UUID when we switch to ORM 7 + } +} diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml b/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml index b600f48dc5044..e3f2508a0f224 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml @@ -43,6 +43,20 @@ io.quarkus quarkus-extension-maven-plugin + + + + process-resources + + extension-descriptor + + + + io.quarkus:quarkus-hibernate-reactive + + + + diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithSession.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithSession.java index 01f7bdf3e91ac..e55a16664d234 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithSession.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithSession.java @@ -36,4 +36,9 @@ @Nonbinding String value() default DEFAULT_PERSISTENCE_UNIT_NAME; + /** + * Requests a stateless session, as opposed to a managed session. + */ + @Nonbinding + boolean stateless() default false; } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithTransaction.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithTransaction.java index dfdc42eff339a..c45507b8c4340 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithTransaction.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/WithTransaction.java @@ -38,4 +38,10 @@ @Nonbinding String value() default DEFAULT_PERSISTENCE_UNIT_NAME; + /** + * WARNING: this is temporary, it will be removed in the future. + * Requests a stateless session transaction, as opposed to a managed session transaction. + */ + @Nonbinding + boolean stateless() default false; } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractJpaOperations.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractJpaOperations.java index 952c1410580eb..a70c48b7b50d9 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractJpaOperations.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractJpaOperations.java @@ -2,28 +2,24 @@ import static io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.stream.Collectors; -import java.util.stream.Stream; import jakarta.persistence.LockModeType; +import org.hibernate.LockMode; import org.hibernate.internal.util.LockModeConverter; import org.hibernate.reactive.mutiny.Mutiny; -import org.hibernate.reactive.mutiny.Mutiny.Session; import io.quarkus.panache.common.Parameters; import io.quarkus.panache.common.Sort; import io.quarkus.panache.hibernate.common.runtime.PanacheJpaUtil; import io.smallrye.mutiny.Uni; -public abstract class AbstractJpaOperations { - private static volatile Map entityToPersistenceUnit = Collections.emptyMap(); +public abstract class AbstractJpaOperations { + protected static volatile Map entityToPersistenceUnit = Collections.emptyMap(); public static void setEntityToPersistenceUnit(Map map) { entityToPersistenceUnit = Collections.unmodifiableMap(map); @@ -31,103 +27,57 @@ public static void setEntityToPersistenceUnit(Map map) { // FIXME: make it configurable? static final long TIMEOUT_MS = 5000; - private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + protected static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; - protected abstract PanacheQueryType createPanacheQuery(Uni session, String query, String originalQuery, + protected abstract PanacheQueryType createPanacheQuery(Uni session, String query, String originalQuery, String orderBy, Object paramsArrayOrMap); protected abstract Uni> list(PanacheQueryType query); + protected abstract Uni delete(SessionType session, Object entity); + // // Instance methods - public Uni persist(Object entity) { - return persist(getSession(entity.getClass()), entity); - } - - public Uni persist(Uni sessionUni, Object entity) { - return sessionUni.chain(session -> { - if (!session.contains(entity)) { - return session.persist(entity); - } - return Uni.createFrom().nullItem(); - }); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Uni persist(Iterable entities) { - List list = new ArrayList(); - for (Object entity : entities) { - list.add(entity); - } - return persist(list.toArray(EMPTY_OBJECT_ARRAY)); - } + private Class sessionType; - public Uni persist(Object firstEntity, Object... entities) { - List list = new ArrayList<>(entities.length + 1); - list.add(firstEntity); - Collections.addAll(list, entities); - return persist(list.toArray(EMPTY_OBJECT_ARRAY)); + protected AbstractJpaOperations(Class sessionType) { + this.sessionType = sessionType; } - public Uni persist(Stream entities) { - return persist(entities.toArray()); + public int paramCount(Object[] params) { + return params != null ? params.length : 0; } - public Uni persist(Object... entities) { - Map> sessions = Arrays.stream(entities) - .collect(Collectors.groupingBy(e -> entityToPersistenceUnit.get(e.getClass().getName()))); - - List> results = new ArrayList<>(); - for (Entry> entry : sessions.entrySet()) { - results.add(getSession(entry.getKey()).chain(session -> session.persistAll(entry.getValue().toArray()))); - } - - return Uni.combine().all().unis(results).discardItems(); + public int paramCount(Map params) { + return params != null ? params.size() : 0; } - public Uni delete(Object entity) { - return getSession(entity.getClass()).chain(session -> session.remove(entity)); - } + // These should go into a shared interface between Mutiny.Session and Mutiny.StatelessSession - public boolean isPersistent(Object entity) { - Session currentSession = getCurrentSession(entity.getClass()); - if (currentSession == null) { - // No active session so object is surely non-persistent - return false; - } + protected abstract Uni find(SessionType session, Class entityClass, Object id); - return currentSession.contains(entity); - } + protected abstract Uni find(SessionType session, Class entityClass, Object id, LockMode lockMode); - public Session getCurrentSession(Class entityClass) { - String persistenceUnitName = entityToPersistenceUnit.get(entityClass.getName()); - return SessionOperations.getCurrentSession(persistenceUnitName); - } + protected abstract Mutiny.SelectionQuery createSelectionQuery(SessionType session, String var1, Class var2); - public Uni flush(Object entity) { - return getSession(entity.getClass()).chain(Session::flush); - } + protected abstract Mutiny.SelectionQuery createNamedQuery(SessionType session, String var1, Class var2); - public int paramCount(Object[] params) { - return params != null ? params.length : 0; - } + protected abstract Mutiny.Query createNamedQuery(SessionType session, String var1); - public int paramCount(Map params) { - return params != null ? params.size() : 0; - } + protected abstract Mutiny.MutationQuery createMutationQuery(SessionType session, String var1); // // Queries public Uni findById(Class entityClass, Object id) { - return getSession(entityClass).chain(session -> session.find(entityClass, id)); + return getSession(entityClass).chain(session -> find(session, entityClass, id)); } public Uni findById(Class entityClass, Object id, LockModeType lockModeType) { return getSession(entityClass) - .chain(session -> session.find(entityClass, id, LockModeConverter.convertToLockMode(lockModeType))); + .chain(session -> find(session, entityClass, id, LockModeConverter.convertToLockMode(lockModeType))); } public PanacheQueryType find(Class entityClass, String panacheQuery, Object... params) { @@ -135,7 +85,7 @@ public PanacheQueryType find(Class entityClass, String panacheQuery, Object.. } public PanacheQueryType find(Class entityClass, String panacheQuery, Sort sort, Object... params) { - Uni session = getSession(entityClass); + Uni session = getSession(entityClass); if (PanacheJpaUtil.isNamedQuery(panacheQuery)) { String namedQuery = panacheQuery.substring(1); if (sort != null) { @@ -155,7 +105,7 @@ public PanacheQueryType find(Class entityClass, String panacheQuery, Map entityClass, String panacheQuery, Sort sort, Map params) { - Uni session = getSession(entityClass); + Uni session = getSession(entityClass); if (PanacheJpaUtil.isNamedQuery(panacheQuery)) { String namedQuery = panacheQuery.substring(1); if (sort != null) { @@ -204,13 +154,13 @@ public Uni> list(Class entityClass, String query, Sort sort, Paramete public PanacheQueryType findAll(Class entityClass) { String query = "FROM " + PanacheJpaUtil.getEntityName(entityClass); - Uni session = getSession(entityClass); + Uni session = getSession(entityClass); return createPanacheQuery(session, query, null, null, null); } public PanacheQueryType findAll(Class entityClass, Sort sort) { String query = "FROM " + PanacheJpaUtil.getEntityName(entityClass); - Uni session = getSession(entityClass); + Uni session = getSession(entityClass); return createPanacheQuery(session, query, null, PanacheJpaUtil.toOrderBy(sort), null); } @@ -224,8 +174,8 @@ public Uni> listAll(Class entityClass, Sort sort) { public Uni count(Class entityClass) { return getSession(entityClass) - .chain(session -> session - .createSelectionQuery("FROM " + PanacheJpaUtil.getEntityName(entityClass), entityClass) + .chain(session -> createSelectionQuery(session, "FROM " + PanacheJpaUtil.getEntityName(entityClass), + entityClass) .getResultCount()); } @@ -236,11 +186,11 @@ public Uni count(Class entityClass, String panacheQuery, Object... para return (Uni) getSession(entityClass).chain(session -> { String namedQueryName = panacheQuery.substring(1); NamedQueryUtil.checkNamedQuery(entityClass, namedQueryName); - return bindParameters(session.createNamedQuery(namedQueryName, Long.class), params).getSingleResult(); + return bindParameters(createNamedQuery(session, namedQueryName, Long.class), params).getSingleResult(); }); return getSession(entityClass).chain(session -> bindParameters( - session.createSelectionQuery(PanacheJpaUtil.createQueryForCount(entityClass, panacheQuery, paramCount(params)), + createSelectionQuery(session, PanacheJpaUtil.createQueryForCount(entityClass, panacheQuery, paramCount(params)), Object.class), params).getResultCount()) .onFailure(RuntimeException.class) @@ -253,11 +203,11 @@ public Uni count(Class entityClass, String panacheQuery, Map { String namedQueryName = panacheQuery.substring(1); NamedQueryUtil.checkNamedQuery(entityClass, namedQueryName); - return bindParameters(session.createNamedQuery(namedQueryName, Long.class), params).getSingleResult(); + return bindParameters(createNamedQuery(session, namedQueryName, Long.class), params).getSingleResult(); }); return getSession(entityClass).chain(session -> bindParameters( - session.createSelectionQuery(PanacheJpaUtil.createQueryForCount(entityClass, panacheQuery, paramCount(params)), + createSelectionQuery(session, PanacheJpaUtil.createQueryForCount(entityClass, panacheQuery, paramCount(params)), Object.class), params).getResultCount()) .onFailure(RuntimeException.class) @@ -286,7 +236,7 @@ public Uni exists(Class entityClass, String query, Parameters params public Uni deleteAll(Class entityClass) { return getSession(entityClass).chain( - session -> session.createMutationQuery("DELETE FROM " + PanacheJpaUtil.getEntityName(entityClass)) + session -> createMutationQuery(session, "DELETE FROM " + PanacheJpaUtil.getEntityName(entityClass)) .executeUpdate() .map(Integer::longValue)); } @@ -299,7 +249,7 @@ public Uni deleteById(Class entityClass, Object id) { if (entity == null) { return Uni.createFrom().item(false); } - return getSession(entityClass).chain(session -> session.remove(entity).map(v -> true)); + return getSession(entityClass).chain(session -> delete(session, entity).map(v -> true)); }); } @@ -309,11 +259,12 @@ public Uni delete(Class entityClass, String panacheQuery, Object... par return getSession(entityClass).chain(session -> { String namedQueryName = panacheQuery.substring(1); NamedQueryUtil.checkNamedQuery(entityClass, namedQueryName); - return bindParameters(session.createNamedQuery(namedQueryName), params).executeUpdate().map(Integer::longValue); + return bindParameters(createNamedQuery(session, namedQueryName), params).executeUpdate() + .map(Integer::longValue); }); return getSession(entityClass).chain(session -> bindParameters( - session.createMutationQuery(PanacheJpaUtil.createDeleteQuery(entityClass, panacheQuery, paramCount(params))), + createMutationQuery(session, PanacheJpaUtil.createDeleteQuery(entityClass, panacheQuery, paramCount(params))), params) .executeUpdate().map(Integer::longValue)) .onFailure(RuntimeException.class) @@ -326,11 +277,12 @@ public Uni delete(Class entityClass, String panacheQuery, Map { String namedQueryName = panacheQuery.substring(1); NamedQueryUtil.checkNamedQuery(entityClass, namedQueryName); - return bindParameters(session.createNamedQuery(namedQueryName), params).executeUpdate().map(Integer::longValue); + return bindParameters(createNamedQuery(session, namedQueryName), params).executeUpdate() + .map(Integer::longValue); }); return getSession(entityClass).chain(session -> bindParameters( - session.createMutationQuery(PanacheJpaUtil.createDeleteQuery(entityClass, panacheQuery, paramCount(params))), + createMutationQuery(session, PanacheJpaUtil.createDeleteQuery(entityClass, panacheQuery, paramCount(params))), params) .executeUpdate().map(Integer::longValue)) .onFailure(RuntimeException.class) @@ -352,7 +304,7 @@ public Uni executeUpdate(Class entityClass, String panacheQuery, Obj return (Uni) getSession(entityClass).chain(session -> { String namedQueryName = panacheQuery.substring(1); NamedQueryUtil.checkNamedQuery(entityClass, namedQueryName); - return bindParameters(session.createNamedQuery(namedQueryName), params).executeUpdate(); + return bindParameters(createNamedQuery(session, namedQueryName), params).executeUpdate(); }); String updateQuery = PanacheJpaUtil.createUpdateQuery(entityClass, panacheQuery, paramCount(params)); @@ -367,7 +319,7 @@ public Uni executeUpdate(Class entityClass, String panacheQuery, Map return (Uni) getSession(entityClass).chain(session -> { String namedQueryName = panacheQuery.substring(1); NamedQueryUtil.checkNamedQuery(entityClass, namedQueryName); - return bindParameters(session.createNamedQuery(namedQueryName), params).executeUpdate(); + return bindParameters(createNamedQuery(session, namedQueryName), params).executeUpdate(); }); String updateQuery = PanacheJpaUtil.createUpdateQuery(entityClass, panacheQuery, paramCount(params)); @@ -391,18 +343,25 @@ public Uni update(Class entityClass, String query, Object... params) // // Static helpers - public Uni getSession() { + public Uni getSession() { return getSession(DEFAULT_PERSISTENCE_UNIT_NAME); } - public Uni getSession(Class clazz) { + public Uni getSession(Class clazz) { String className = clazz.getName(); String persistenceUnitName = entityToPersistenceUnit.get(className); + if (persistenceUnitName == null) { + // For Quarkus-configured PUs, or if there is no PU, this is definitely an error. + throw new IllegalStateException(String.format( + "Entity '%s' was not found. Did you forget to annotate your Panache Entity classes with '@Entity'?", + clazz)); + } return getSession(persistenceUnitName); } - public Uni getSession(String persistenceUnitName) { - return SessionOperations.getSession(persistenceUnitName); + public Uni getSession(String persistenceUnitName) { + return sessionType == Mutiny.Session.class ? (Uni) SessionOperations.getSession(persistenceUnitName) + : (Uni) SessionOperations.getStatelessSession(persistenceUnitName); } public static Mutiny.Query bindParameters(Mutiny.Query query, Object[] params) { @@ -437,7 +396,7 @@ public static T bindParameters(T query, Map executeUpdate(String query, Object... params) { return getSession(DEFAULT_PERSISTENCE_UNIT_NAME) - .chain(session -> bindParameters(session.createMutationQuery(query), params) + .chain(session -> bindParameters(createMutationQuery(session, query), params) .executeUpdate()); } @@ -446,7 +405,7 @@ public Uni executeUpdate(String query, Object... params) { */ public Uni executeUpdate(String query, Map params) { return getSession(DEFAULT_PERSISTENCE_UNIT_NAME) - .chain(session -> bindParameters(session.createMutationQuery(query), params) + .chain(session -> bindParameters(createMutationQuery(session, query), params) .executeUpdate()); } } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractManagedJpaOperations.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractManagedJpaOperations.java new file mode 100644 index 0000000000000..bed436cbf5503 --- /dev/null +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractManagedJpaOperations.java @@ -0,0 +1,124 @@ +package io.quarkus.hibernate.reactive.panache.common.runtime; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.LockMode; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.smallrye.mutiny.Uni; + +public abstract class AbstractManagedJpaOperations + extends AbstractJpaOperations { + protected AbstractManagedJpaOperations() { + super(Mutiny.Session.class); + } + + public Uni persist(Object entity) { + return persist(getSession(entity.getClass()), entity); + } + + public Uni persist(Uni sessionUni, Object entity) { + return sessionUni.chain(session -> { + if (!session.contains(entity)) { + return session.persist(entity); + } + return Uni.createFrom().nullItem(); + }); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Uni persist(Iterable entities) { + List list = new ArrayList(); + for (Object entity : entities) { + list.add(entity); + } + return persist(list.toArray(EMPTY_OBJECT_ARRAY)); + } + + public Uni persist(Object firstEntity, Object... entities) { + List list = new ArrayList<>(entities.length + 1); + list.add(firstEntity); + Collections.addAll(list, entities); + return persist(list.toArray(EMPTY_OBJECT_ARRAY)); + } + + public Uni persist(Stream entities) { + return persist(entities.toArray()); + } + + public Uni persist(Object... entities) { + Map> sessions = Arrays.stream(entities) + .collect(Collectors.groupingBy(e -> entityToPersistenceUnit.get(e.getClass().getName()))); + + List> results = new ArrayList<>(); + for (Map.Entry> entry : sessions.entrySet()) { + results.add(getSession(entry.getKey()).chain(session -> session.persistAll(entry.getValue().toArray()))); + } + + return Uni.combine().all().unis(results).discardItems(); + } + + public Uni delete(Object entity) { + return getSession(entity.getClass()).chain(session -> session.remove(entity)); + } + + public boolean isPersistent(Object entity) { + Mutiny.Session currentSession = getCurrentSession(entity.getClass()); + if (currentSession == null) { + // No active session so object is surely non-persistent + return false; + } + + return currentSession.contains(entity); + } + + public Mutiny.Session getCurrentSession(Class entityClass) { + String persistenceUnitName = entityToPersistenceUnit.get(entityClass.getName()); + return SessionOperations.getCurrentSession(persistenceUnitName); + } + + public Uni flush(Object entity) { + return getSession(entity.getClass()).chain(Mutiny.Session::flush); + } + + @Override + public Uni delete(Mutiny.Session session, Object entity) { + return session.remove(entity); + } + + @Override + protected Uni find(Mutiny.Session session, Class entityClass, Object id) { + return session.find(entityClass, id); + } + + @Override + protected Uni find(Mutiny.Session session, Class entityClass, Object id, LockMode lockMode) { + return session.find(entityClass, id, lockMode); + } + + @Override + protected Mutiny.SelectionQuery createSelectionQuery(Mutiny.Session session, String var1, Class var2) { + return session.createSelectionQuery(var1, var2); + } + + @Override + protected Mutiny.SelectionQuery createNamedQuery(Mutiny.Session session, String var1, Class var2) { + return session.createNamedQuery(var1, var2); + } + + @Override + protected Mutiny.Query createNamedQuery(Mutiny.Session session, String var1) { + return session.createNamedQuery(var1); + } + + @Override + protected Mutiny.MutationQuery createMutationQuery(Mutiny.Session session, String var1) { + return session.createMutationQuery(var1); + } +} diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractStatelessJpaOperations.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractStatelessJpaOperations.java new file mode 100644 index 0000000000000..fababacad95b4 --- /dev/null +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/AbstractStatelessJpaOperations.java @@ -0,0 +1,104 @@ +package io.quarkus.hibernate.reactive.panache.common.runtime; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.hibernate.LockMode; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.smallrye.mutiny.Uni; + +public abstract class AbstractStatelessJpaOperations + extends AbstractJpaOperations { + protected AbstractStatelessJpaOperations() { + super(Mutiny.StatelessSession.class); + } + + public Uni insert(Object entity) { + return insert(getSession(entity.getClass()), entity); + } + + public Uni insert(Uni sessionUni, Object entity) { + return sessionUni.chain(session -> session.insert(entity)); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Uni insert(Iterable entities) { + List list = new ArrayList(); + for (Object entity : entities) { + list.add(entity); + } + return insert(list.toArray(EMPTY_OBJECT_ARRAY)); + } + + public Uni insert(Object firstEntity, Object... entities) { + List list = new ArrayList<>(entities.length + 1); + list.add(firstEntity); + Collections.addAll(list, entities); + return insert(list.toArray(EMPTY_OBJECT_ARRAY)); + } + + public Uni insert(Stream entities) { + return insert(entities.toArray()); + } + + public Uni insert(Object... entities) { + Map> sessions = Arrays.stream(entities) + .collect(Collectors.groupingBy(e -> entityToPersistenceUnit.get(e.getClass().getName()))); + + List> results = new ArrayList<>(); + for (Map.Entry> entry : sessions.entrySet()) { + results.add(getSession(entry.getKey()).chain(session -> session.insertAll(entry.getValue().toArray()))); + } + + return Uni.combine().all().unis(results).discardItems(); + } + + public Uni update(Object entity) { + return getSession(entity.getClass()).chain(session -> session.update(entity)); + } + + public Uni delete(Object entity) { + return getSession(entity.getClass()).chain(session -> session.delete(entity)); + } + + @Override + public Uni delete(Mutiny.StatelessSession session, Object entity) { + return session.delete(entity); + } + + @Override + protected Uni find(Mutiny.StatelessSession session, Class entityClass, Object id) { + return session.get(entityClass, id); + } + + @Override + protected Uni find(Mutiny.StatelessSession session, Class entityClass, Object id, LockMode lockMode) { + return session.get(entityClass, id, lockMode); + } + + @Override + protected Mutiny.SelectionQuery createSelectionQuery(Mutiny.StatelessSession session, String var1, Class var2) { + return session.createSelectionQuery(var1, var2); + } + + @Override + protected Mutiny.SelectionQuery createNamedQuery(Mutiny.StatelessSession session, String var1, Class var2) { + return session.createNamedQuery(var1, var2); + } + + @Override + protected Mutiny.Query createNamedQuery(Mutiny.StatelessSession session, String var1) { + return session.createNamedQuery(var1); + } + + @Override + protected Mutiny.MutationQuery createMutationQuery(Mutiny.StatelessSession session, String var1) { + return session.createMutationQuery(var1); + } +} diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonAbstractPanacheQueryImpl.java similarity index 92% rename from extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java rename to extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonAbstractPanacheQueryImpl.java index d5d09c18f4746..4a27a4a2bc597 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonAbstractPanacheQueryImpl.java @@ -28,7 +28,7 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; -public class CommonPanacheQueryImpl { +public abstract class CommonAbstractPanacheQueryImpl { private Object paramsArrayOrMap; /** @@ -46,7 +46,6 @@ public class CommonPanacheQueryImpl { */ protected String customCountQueryForSpring; private String orderBy; - private Uni em; private Page page; private Uni count; @@ -59,7 +58,9 @@ public class CommonPanacheQueryImpl { private Map> filters; private Class projectionType; - public CommonPanacheQueryImpl(Uni em, String query, String originalQuery, String orderBy, + protected Uni em; + + public CommonAbstractPanacheQueryImpl(Uni em, String query, String originalQuery, String orderBy, Object paramsArrayOrMap) { this.em = em; this.query = query; @@ -68,7 +69,8 @@ public CommonPanacheQueryImpl(Uni em, String query, String origi this.paramsArrayOrMap = paramsArrayOrMap; } - private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String newQueryString, + protected CommonAbstractPanacheQueryImpl(CommonAbstractPanacheQueryImpl previousQuery, + String newQueryString, String customCountQueryForSpring, Class projectionType) { this.em = previousQuery.em; @@ -85,9 +87,16 @@ private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String n this.projectionType = projectionType; } + protected abstract CommonAbstractPanacheQueryImpl newQuery(String query, + String customCountQueryForSpring, Class type); + + protected abstract Filter enableFilter(SessionType session, String filter); + + protected abstract void disableFilter(SessionType session, String filter); + // Builder - public CommonPanacheQueryImpl project(Class type) { + public CommonAbstractPanacheQueryImpl project(Class type) { String selectQuery = query; if (PanacheJpaUtil.isNamedQuery(query)) { selectQuery = NamedQueryUtil.getNamedQuery(query.substring(1)); @@ -102,7 +111,7 @@ public CommonPanacheQueryImpl project(Class type) { // If the query starts with a select clause, we pass it on to ORM which can handle that via a projection type if (lowerCasedTrimmedQuery.startsWith("select ")) { // I think projections do not change the result count, so we can keep the custom count query - return new CommonPanacheQueryImpl<>(this, query, customCountQueryForSpring, type); + return newQuery(query, customCountQueryForSpring, type); } // FIXME: this assumes the query starts with "FROM " probably? @@ -110,7 +119,7 @@ public CommonPanacheQueryImpl project(Class type) { // build select clause with a constructor expression String selectClause = "SELECT " + getParametersFromClass(type, null); // I think projections do not change the result count, so we can keep the custom count query - return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, customCountQueryForSpring, null); + return newQuery(selectClause + selectQuery, customCountQueryForSpring, null); } private StringBuilder getParametersFromClass(Class type, String parentParameter) { @@ -353,7 +362,7 @@ public Uni singleResult() { }); } - private Mutiny.SelectionQuery createQuery(Mutiny.Session em) { + private Mutiny.SelectionQuery createQuery(SessionType em) { Mutiny.SelectionQuery jpaQuery = createBaseQuery(em); if (range != null) { @@ -379,7 +388,7 @@ private Mutiny.SelectionQuery createQuery(Mutiny.Session em) { return jpaQuery; } - private Mutiny.SelectionQuery createQuery(Mutiny.Session em, int maxResults) { + private Mutiny.SelectionQuery createQuery(SessionType em, int maxResults) { Mutiny.SelectionQuery jpaQuery = createBaseQuery(em); if (range != null) { @@ -400,7 +409,7 @@ private Mutiny.SelectionQuery createQuery(Mutiny.Session em, int maxResults) } @SuppressWarnings("unchecked") - private Mutiny.SelectionQuery createBaseQuery(Mutiny.Session em) { + private Mutiny.SelectionQuery createBaseQuery(SessionType em) { Mutiny.SelectionQuery hibernateQuery; if (PanacheJpaUtil.isNamedQuery(query)) { String namedQuery = query.substring(1); @@ -433,11 +442,11 @@ private Mutiny.SelectionQuery createBaseQuery(Mutiny.Session em) { return hibernateQuery; } - private Uni applyFilters(Mutiny.Session em, Supplier> uni) { + private Uni applyFilters(SessionType em, Supplier> uni) { if (filters == null) return uni.get(); for (Entry> entry : filters.entrySet()) { - Filter filter = em.enableFilter(entry.getKey()); + Filter filter = enableFilter(em, entry.getKey()); for (Entry paramEntry : entry.getValue().entrySet()) { filter.setParameter(paramEntry.getKey(), paramEntry.getValue()); } @@ -445,7 +454,7 @@ private Uni applyFilters(Mutiny.Session em, Supplier> uni) { } return uni.get().onTermination().invoke(() -> { for (Entry> entry : filters.entrySet()) { - em.disableFilter(entry.getKey()); + disableFilter(em, entry.getKey()); } }); } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonManagedPanacheQueryImpl.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonManagedPanacheQueryImpl.java new file mode 100644 index 0000000000000..485f2f12430fb --- /dev/null +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonManagedPanacheQueryImpl.java @@ -0,0 +1,40 @@ +package io.quarkus.hibernate.reactive.panache.common.runtime; + +import org.hibernate.Filter; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.smallrye.mutiny.Uni; + +public class CommonManagedPanacheQueryImpl extends CommonAbstractPanacheQueryImpl { + + public CommonManagedPanacheQueryImpl(Uni em, String query, String originalQuery, String orderBy, + Object paramsArrayOrMap) { + super(em, query, originalQuery, orderBy, paramsArrayOrMap); + } + + protected CommonManagedPanacheQueryImpl(CommonManagedPanacheQueryImpl previousQuery, String newQueryString, + String customCountQueryForSpring, Class projectionType) { + super(previousQuery, newQueryString, customCountQueryForSpring, projectionType); + } + + @Override + public CommonManagedPanacheQueryImpl project(Class type) { + return (CommonManagedPanacheQueryImpl) super.project(type); + } + + @Override + protected CommonAbstractPanacheQueryImpl newQuery(String query, String customCountQueryForSpring, + Class type) { + return new CommonManagedPanacheQueryImpl<>(this, query, customCountQueryForSpring, type); + } + + @Override + protected Filter enableFilter(Mutiny.Session session, String filter) { + return session.enableFilter(filter); + } + + @Override + protected void disableFilter(Mutiny.Session session, String filter) { + session.disableFilter(filter); + } +} diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonStatelessPanacheQueryImpl.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonStatelessPanacheQueryImpl.java new file mode 100644 index 0000000000000..19d9852305a5e --- /dev/null +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonStatelessPanacheQueryImpl.java @@ -0,0 +1,46 @@ +package io.quarkus.hibernate.reactive.panache.common.runtime; + +import org.hibernate.Filter; +import org.hibernate.reactive.mutiny.Mutiny; + +import io.smallrye.mutiny.Uni; + +public class CommonStatelessPanacheQueryImpl extends CommonAbstractPanacheQueryImpl { + + protected Uni em; + + public CommonStatelessPanacheQueryImpl(Uni em, String query, String originalQuery, String orderBy, + Object paramsArrayOrMap) { + super(em, query, originalQuery, orderBy, paramsArrayOrMap); + } + + protected CommonStatelessPanacheQueryImpl(CommonStatelessPanacheQueryImpl previousQuery, String newQueryString, + String customCountQueryForSpring, Class projectionType) { + super(previousQuery, newQueryString, customCountQueryForSpring, projectionType); + } + + @Override + public CommonStatelessPanacheQueryImpl project(Class type) { + return (CommonStatelessPanacheQueryImpl) super.project(type); + } + + @Override + protected CommonAbstractPanacheQueryImpl newQuery(String query, + String customCountQueryForSpring, Class type) { + return new CommonStatelessPanacheQueryImpl<>(this, query, customCountQueryForSpring, type); + } + + @Override + protected Filter enableFilter(Mutiny.StatelessSession session, String filter) { + // FIXME + throw new UnsupportedOperationException("Not supported yet upstream"); + // return session.enableFilter(filter); + } + + @Override + protected void disableFilter(Mutiny.StatelessSession session, String filter) { + // FIXME + throw new UnsupportedOperationException("Not supported yet upstream"); + // session.disableFilter(filter); + } +} diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/SessionOperations.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/SessionOperations.java index bb3cc826d4ca4..1104eabe1e53e 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/SessionOperations.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/SessionOperations.java @@ -13,9 +13,9 @@ import org.hibernate.reactive.context.Context.Key; import org.hibernate.reactive.context.impl.BaseKey; import org.hibernate.reactive.mutiny.Mutiny; -import org.hibernate.reactive.mutiny.Mutiny.Session; import org.hibernate.reactive.mutiny.Mutiny.SessionFactory; import org.hibernate.reactive.mutiny.Mutiny.Transaction; +import org.jboss.logging.Logger; import io.quarkus.arc.Arc; import io.quarkus.arc.ClientProxy; @@ -31,10 +31,14 @@ */ public final class SessionOperations { + private static final Logger LOG = Logger.getLogger(SessionOperations.class); + private static final String ERROR_MSG = "Hibernate Reactive Panache requires a safe (isolated) Vert.x sub-context, but the current context hasn't been flagged as such."; private static final ComputingCache> SESSION_KEY_MAP = new ComputingCache<>( k -> createSessionKey(k)); + private static final ComputingCache> STATELESS_SESSION_KEY_MAP = new ComputingCache<>( + k -> createStatelessSessionKey(k)); private static final ComputingCache SESSION_FACTORY_MAP = new ComputingCache<>( k -> createSessionFactory(k)); @@ -55,16 +59,22 @@ private static SessionFactory createSessionFactory(String persistenceunitname) { return sessionFactory; } - private static Key createSessionKey(String persistenceUnitName) { + private static Key createSessionKey(String persistenceUnitName) { + Implementor implementor = (Implementor) ClientProxy + .unwrap(SESSION_FACTORY_MAP.getValue(persistenceUnitName)); + return new BaseKey<>(Mutiny.Session.class, implementor.getUuid()); + } + + private static Key createStatelessSessionKey(String persistenceUnitName) { Implementor implementor = (Implementor) ClientProxy .unwrap(SESSION_FACTORY_MAP.getValue(persistenceUnitName)); - return new BaseKey<>(Session.class, implementor.getUuid()); + return new BaseKey<>(Mutiny.StatelessSession.class, implementor.getUuid()); } // This key is used to indicate that reactive sessions should be opened lazily/on-demand (when needed) in the current vertx context private static final String SESSION_ON_DEMAND_KEY = "hibernate.reactive.panache.sessionOnDemand"; - // This key is used to keep track of the Set sessions created on demand + // This key is used to keep track of the Set sessions (managed or stateless) created on demand private static final String SESSION_ON_DEMAND_OPENED_KEY = "hibernate.reactive.panache.sessionOnDemandOpened"; /** @@ -75,6 +85,7 @@ private static Key createSessionKey(String persistenceUnitName) { * @param work * @return a new {@link Uni} * @see #getSession(String) + * @see #getStatelessSession(String) */ static Uni withSessionOnDemand(Supplier> work) { Context context = vertxContext(); @@ -105,7 +116,7 @@ static Uni withSessionOnDemand(Supplier> work) { } /** - * Performs the work in the scope of a reactive transaction. An existing session is reused if possible. + * Performs the work in the scope of a reactive transaction. An existing managed session is reused if possible. * * @param * @param work @@ -137,6 +148,39 @@ public static Uni withTransaction(Function> work) { return withSession(DEFAULT_PERSISTENCE_UNIT_NAME, s -> s.withTransaction(work)); } + /** + * Performs the work in the scope of a reactive transaction. An existing stateless session is reused if possible. + * + * @param + * @param work + * @return a new {@link Uni} + */ + public static Uni withStatelessTransaction(Supplier> work) { + return withStatelessSession(s -> s.withTransaction(t -> work.get())); + } + + /** + * Performs the work in the scope of a reactive transaction. An existing stateless session is reused if possible. + * + * @param + * @param work + * @return a new {@link Uni} + */ + public static Uni withStatelessTransaction(String persistenceUnitName, Supplier> work) { + return withStatelessSession(persistenceUnitName, s -> s.withTransaction(t -> work.get())); + } + + /** + * Performs the work in the scope of a reactive transaction. An existing stateless session is reused if possible. + * + * @param + * @param work + * @return a new {@link Uni} + */ + public static Uni withStatelessTransaction(Function> work) { + return withStatelessSession(DEFAULT_PERSISTENCE_UNIT_NAME, s -> s.withTransaction(work)); + } + /** * Performs the work in the scope of a named reactive session. * An existing session is reused if possible. @@ -146,8 +190,13 @@ public static Uni withTransaction(Function> work) { * @param work * @return a new {@link Uni} */ - public static Uni withSession(String persistenceUnitName, Function> work) { + public static Uni withSession(String persistenceUnitName, Function> work) { Context context = vertxContext(); + // First make sure we don't already have an opened stateless session + Uni error = checkNoStatelessSession(context, persistenceUnitName); + if (error != null) { + return error; + } Key key = SESSION_KEY_MAP.getValue(persistenceUnitName); Mutiny.Session current = context.getLocal(key); if (current != null && current.isOpen()) { @@ -157,12 +206,23 @@ public static Uni withSession(String persistenceUnitName, Function LOG.debugf("Opening lazy managed session for Persistence Unit '%s'", persistenceUnitName)) .invoke(s -> context.putLocal(key, s)) .chain(work::apply) .eventually(() -> closeSession(persistenceUnitName)); } } + private static Uni checkNoStatelessSession(Context context, String persistenceUnitName) { + Key statelessKey = STATELESS_SESSION_KEY_MAP.getValue(persistenceUnitName); + Mutiny.StatelessSession currentStateless = context.getLocal(statelessKey); + if (currentStateless != null && currentStateless.isOpen()) { + return Uni.createFrom().failure( + new IllegalStateException("There is already a stateless session opened for this persistence unit")); + } + return null; + } + /** * Performs the work in the scope of the default reactive session. * An existing session is reused if possible. @@ -171,10 +231,64 @@ public static Uni withSession(String persistenceUnitName, Function Uni withSession(Function> work) { + public static Uni withSession(Function> work) { return withSession(DEFAULT_PERSISTENCE_UNIT_NAME, work); } + /** + * Performs the work in the scope of a named reactive stateless session. + * An existing stateless session is reused if possible. + * + * @param + * @param persistenceUnitName + * @param work + * @return a new {@link Uni} + */ + public static Uni withStatelessSession(String persistenceUnitName, Function> work) { + Context context = vertxContext(); + // First make sure we don't already have an opened managed session + Uni error = checkNoManagedSession(context, persistenceUnitName); + if (error != null) { + return error; + } + Key key = STATELESS_SESSION_KEY_MAP.getValue(persistenceUnitName); + Mutiny.StatelessSession current = context.getLocal(key); + if (current != null && current.isOpen()) { + // reactive session exists - reuse this session + return work.apply(current); + } else { + // reactive session does not exist - open a new one and close it when the returned Uni completes + return SESSION_FACTORY_MAP.getValue(persistenceUnitName) + .openStatelessSession() + .invoke(() -> LOG.debugf("Opening lazy stateless session for Persistence Unit '%s'", persistenceUnitName)) + .invoke(s -> context.putLocal(key, s)) + .chain(work::apply) + .eventually(() -> closeSession(persistenceUnitName)); + } + } + + private static Uni checkNoManagedSession(Context context, String persistenceUnitName) { + Key managedKey = SESSION_KEY_MAP.getValue(persistenceUnitName); + Mutiny.Session currentManaged = context.getLocal(managedKey); + if (currentManaged != null && currentManaged.isOpen()) { + return Uni.createFrom() + .failure(new IllegalStateException("There is already a managed session opened for this persistence unit")); + } + return null; + } + + /** + * Performs the work in the scope of the default reactive stateless session. + * An existing session is reused if possible. + * + * @param + * @param work + * @return a new {@link Uni} + */ + public static Uni withStatelessSession(Function> work) { + return withStatelessSession(DEFAULT_PERSISTENCE_UNIT_NAME, work); + } + /** * If there is a reactive session stored in the current Vert.x duplicated context then this session is reused. *

      @@ -195,6 +309,11 @@ public static Uni getSession() { public static Uni getSession(String persistenceUnitName) { Context context = vertxContext(); + // First make sure we don't already have an opened stateless session + Uni error = checkNoStatelessSession(context, persistenceUnitName); + if (error != null) { + return error; + } Key key = SESSION_KEY_MAP.getValue(persistenceUnitName); Mutiny.Session current = context.getLocal(key); if (current != null && current.isOpen()) { @@ -210,6 +329,7 @@ public static Uni getSession(String persistenceUnitName) { } if (onDemandSessionsCreated.contains(persistenceUnitName)) { + // FIXME: this method does the same as what's just above // a new reactive session is opened in a previous stage, reuse it return Uni.createFrom().item(() -> getCurrentSession(persistenceUnitName)); } else { @@ -217,6 +337,7 @@ public static Uni getSession(String persistenceUnitName) { // the context was marked as "lazy" which means that the session will be eventually closed onDemandSessionsCreated.add(persistenceUnitName); return SESSION_FACTORY_MAP.getValue(persistenceUnitName).openSession() + .invoke(() -> LOG.debugf("Opening lazy session for Persistence Unit '%s'", persistenceUnitName)) .invoke(s -> context.putLocal(key, s)); } } else { @@ -228,6 +349,70 @@ public static Uni getSession(String persistenceUnitName) { } } + /** + * If there is a reactive stateless session stored in the current Vert.x duplicated context then this stateless session is + * reused. + *

      + * However, if there is no reactive stateless session found then: + *

        + *
      1. if the current vertx duplicated context is marked as "lazy" then a new stateless session is opened and stored it in + * the + * context
      2. + *
      3. otherwise an exception thrown
      4. + *
      + * + * @throws IllegalStateException If no reactive stateless session was found in the context and the context was not marked to + * open a + * new session lazily + * @return the {@link Mutiny.Session} + */ + public static Uni getStatelessSession() { + return getStatelessSession(DEFAULT_PERSISTENCE_UNIT_NAME); + } + + public static Uni getStatelessSession(String persistenceUnitName) { + Context context = vertxContext(); + // First make sure we don't already have an opened managed session + Uni error = checkNoManagedSession(context, persistenceUnitName); + if (error != null) { + return error; + } + Key key = STATELESS_SESSION_KEY_MAP.getValue(persistenceUnitName); + Mutiny.StatelessSession current = context.getLocal(key); + if (current != null && current.isOpen()) { + // reuse the existing reactive session + return Uni.createFrom().item(current); + } else { + if (context.getLocal(SESSION_ON_DEMAND_KEY) != null) { + // This will keep track of all on-demand opened sessions + Set onDemandSessionsCreated = context.getLocal(SESSION_ON_DEMAND_OPENED_KEY); + if (onDemandSessionsCreated == null) { + onDemandSessionsCreated = new HashSet<>(); + context.putLocal(SESSION_ON_DEMAND_OPENED_KEY, onDemandSessionsCreated); + } + + if (onDemandSessionsCreated.contains(persistenceUnitName)) { + // FIXME: this method does the same as what's just above + // a new reactive session is opened in a previous stage, reuse it + return Uni.createFrom().item(() -> getCurrentStatelessSession(persistenceUnitName)); + } else { + // open a new reactive session and store it in the vertx duplicated context + // the context was marked as "lazy" which means that the session will be eventually closed + onDemandSessionsCreated.add(persistenceUnitName); + return SESSION_FACTORY_MAP.getValue(persistenceUnitName).openStatelessSession() + .invoke(() -> LOG.debugf("Opening lazy stateless session for Persistence Unit '%s'", + persistenceUnitName)) + .invoke(s -> context.putLocal(key, s)); + } + } else { + throw new IllegalStateException("No current Mutiny.StatelessSession found" + + "\n\t- no reactive stateless session was found in the Vert.x context and the context was not marked to open a new session lazily" + + "\n\t- a stateless session is opened automatically for JAX-RS resource methods annotated with an HTTP method (@GET, @POST, etc.); inherited annotations are not taken into account" + + "\n\t- you may need to annotate the business method with @WithStatelessSession or @WithStatelessTransaction"); + } + } + } + /** * @return the current reactive session stored in the context, or {@code null} if no session exists */ @@ -240,6 +425,18 @@ public static Mutiny.Session getCurrentSession(String persistenceUnitName) { return null; } + /** + * @return the current reactive stateless session stored in the context, or {@code null} if no stateless session exists + */ + public static Mutiny.StatelessSession getCurrentStatelessSession(String persistenceUnitName) { + Context context = vertxContext(); + Mutiny.StatelessSession current = context.getLocal(STATELESS_SESSION_KEY_MAP.getValue(persistenceUnitName)); + if (current != null && current.isOpen()) { + return current; + } + return null; + } + /** * * @return the current vertx duplicated context @@ -256,18 +453,30 @@ private static Context vertxContext() { } } + /** + * Close any session open for that persistence unit (stateless or managed, there can be only one opened at a time) + */ static Uni closeSession(String persistenceUnitName) { + LOG.debugf("Closing session for Persistence Unit '%s'", persistenceUnitName); Context context = vertxContext(); Key key = SESSION_KEY_MAP.getValue(persistenceUnitName); Mutiny.Session current = context.getLocal(key); if (current != null && current.isOpen()) { + LOG.debugf("Closing opened managed session for Persistence Unit '%s'", persistenceUnitName); return current.close().eventually(() -> context.removeLocal(key)); } + Key statelessKey = STATELESS_SESSION_KEY_MAP.getValue(persistenceUnitName); + Mutiny.StatelessSession currentStateless = context.getLocal(statelessKey); + if (currentStateless != null && currentStateless.isOpen()) { + LOG.debugf("Closing opened stateless session for Persistence Unit '%s'", persistenceUnitName); + return currentStateless.close().eventually(() -> context.removeLocal(statelessKey)); + } return Uni.createFrom().voidItem(); } static void clear() { SESSION_FACTORY_MAP.clear(); SESSION_KEY_MAP.clear(); + STATELESS_SESSION_KEY_MAP.clear(); } } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithSessionInterceptor.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithSessionInterceptor.java index cd8de943a973a..c9ddd19d346ee 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithSessionInterceptor.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithSessionInterceptor.java @@ -17,20 +17,25 @@ public Object intercept(InvocationContext context) throws Exception { // Bindings are validated at build time - method-level binding declared on a method that does not return Uni results in a build failure // However, a class-level binding implies that methods that do not return Uni are just a no-op if (isUniReturnType(context)) { - String persistenceUnitName = getPersistenceUnitName(context); - return SessionOperations.withSession(persistenceUnitName, s -> proceedUni(context)); + WithSession withSession = getAnnotation(context); + String persistenceUnitName = withSession.value(); + if (withSession.stateless()) { + return SessionOperations.withStatelessSession(persistenceUnitName, s -> proceedUni(context)); + } else { + return SessionOperations.withSession(persistenceUnitName, s -> proceedUni(context)); + } } return context.proceed(); } - private String getPersistenceUnitName(InvocationContext context) { + private WithSession getAnnotation(InvocationContext context) { // Check method-level annotation first WithSession annotation = context.getMethod().getAnnotation(WithSession.class); if (annotation == null) { // Check class-level annotation annotation = context.getTarget().getClass().getAnnotation(WithSession.class); } - return annotation.value(); // Annotation has default + return annotation; } } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithTransactionInterceptor.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithTransactionInterceptor.java index 73dc54a1ea7bd..dbb7d71dca5d0 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithTransactionInterceptor.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/WithTransactionInterceptor.java @@ -17,19 +17,24 @@ public Object intercept(InvocationContext context) throws Exception { // Bindings are validated at build time - method-level binding declared on a method that does not return Uni results in a build failure // However, a class-level binding implies that methods that do not return Uni are just a no-op if (isUniReturnType(context)) { - String persistenceUnitName = getPersistenceUnitName(context); - return SessionOperations.withTransaction(persistenceUnitName, () -> proceedUni(context)); + WithTransaction withTransaction = getAnnotation(context); + String persistenceUnitName = withTransaction.value(); + if (withTransaction.stateless()) { + return SessionOperations.withStatelessTransaction(persistenceUnitName, () -> proceedUni(context)); + } else { + return SessionOperations.withTransaction(persistenceUnitName, () -> proceedUni(context)); + } } return context.proceed(); } - private String getPersistenceUnitName(InvocationContext context) { + private WithTransaction getAnnotation(InvocationContext context) { WithTransaction annotation = context.getMethod().getAnnotation(WithTransaction.class); if (annotation == null) { // Check class-level annotation annotation = context.getTarget().getClass().getAnnotation(WithTransaction.class); } - return annotation.value(); // Annotation has default + return annotation; } } diff --git a/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/KotlinJpaOperations.kt b/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/KotlinJpaOperations.kt index 225c57302cc5c..1dc9f2ecfb5cc 100644 --- a/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/KotlinJpaOperations.kt +++ b/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/KotlinJpaOperations.kt @@ -1,10 +1,10 @@ package io.quarkus.hibernate.reactive.panache.kotlin.runtime -import io.quarkus.hibernate.reactive.panache.common.runtime.AbstractJpaOperations +import io.quarkus.hibernate.reactive.panache.common.runtime.AbstractManagedJpaOperations import io.smallrye.mutiny.Uni import org.hibernate.reactive.mutiny.Mutiny -class KotlinJpaOperations : AbstractJpaOperations>() { +class KotlinJpaOperations : AbstractManagedJpaOperations>() { override fun createPanacheQuery( session: Uni, query: String, diff --git a/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/PanacheQueryImpl.kt b/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/PanacheQueryImpl.kt index c6912d5ee6c33..843623057e034 100644 --- a/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/PanacheQueryImpl.kt +++ b/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/runtime/PanacheQueryImpl.kt @@ -1,6 +1,6 @@ package io.quarkus.hibernate.reactive.panache.kotlin.runtime -import io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanacheQueryImpl +import io.quarkus.hibernate.reactive.panache.common.runtime.CommonManagedPanacheQueryImpl import io.quarkus.hibernate.reactive.panache.kotlin.PanacheQuery import io.quarkus.panache.common.Page import io.quarkus.panache.common.Parameters @@ -9,7 +9,7 @@ import jakarta.persistence.LockModeType import org.hibernate.reactive.mutiny.Mutiny class PanacheQueryImpl : PanacheQuery { - private var delegate: CommonPanacheQueryImpl + private var delegate: CommonManagedPanacheQueryImpl internal constructor( em: Uni, @@ -18,10 +18,11 @@ class PanacheQueryImpl : PanacheQuery { orderBy: String?, paramsArrayOrMap: Any?, ) { - delegate = CommonPanacheQueryImpl(em, query, originalQuery, orderBy, paramsArrayOrMap) + delegate = + CommonManagedPanacheQueryImpl(em, query, originalQuery, orderBy, paramsArrayOrMap) } - private constructor(delegate: CommonPanacheQueryImpl) { + private constructor(delegate: CommonManagedPanacheQueryImpl) { this.delegate = delegate } diff --git a/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheHibernateResourceProcessor.java b/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheHibernateResourceProcessor.java index 04e9cb7f298a9..ed1cf6c041fd7 100644 --- a/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheHibernateResourceProcessor.java +++ b/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheHibernateResourceProcessor.java @@ -46,6 +46,7 @@ import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem; import io.quarkus.panache.hibernate.common.deployment.HibernateEnhancersRegisteredBuildItem; import io.quarkus.panache.hibernate.common.deployment.PanacheJpaEntityOperationsEnhancer; +import io.quarkus.panache.hibernate.common.deployment.PanacheJpaRepositoryEnhancer; import io.smallrye.common.annotation.CheckReturnValue; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; @@ -110,10 +111,11 @@ void build(CombinedIndexBuildItem index, List methodCustomizers = methodCustomizersBuildItems.stream() .map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList()); - PanacheJpaRepositoryEnhancer daoEnhancer = new PanacheJpaRepositoryEnhancer(index.getIndex()); + PanacheJpaRepositoryEnhancer daoEnhancer = new PanacheJpaRepositoryEnhancer(index.getIndex(), + ReactiveJavaJpaTypeBundle.BUNDLE); Set daoClasses = new HashSet<>(); Set panacheEntities = new HashSet<>(); - for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_PANACHE_REPOSITORY_BASE)) { + for (ClassInfo classInfo : index.getIndex().getAllKnownImplementations(DOTNAME_PANACHE_REPOSITORY_BASE)) { // Skip PanacheRepository if (classInfo.name().equals(DOTNAME_PANACHE_REPOSITORY)) continue; @@ -124,7 +126,7 @@ void build(CombinedIndexBuildItem index, panacheEntities.add(typeParameters.get(0).name().toString()); daoClasses.add(classInfo.name().toString()); } - for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_PANACHE_REPOSITORY)) { + for (ClassInfo classInfo : index.getIndex().getAllKnownImplementations(DOTNAME_PANACHE_REPOSITORY)) { if (daoEnhancer.skipRepository(classInfo)) continue; daoClasses.add(classInfo.name().toString()); diff --git a/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheJpaRepositoryEnhancer.java b/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheJpaRepositoryEnhancer.java deleted file mode 100644 index bd9c5e14bcfbb..0000000000000 --- a/extensions/panache/hibernate-reactive-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/deployment/PanacheJpaRepositoryEnhancer.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.quarkus.hibernate.reactive.panache.deployment; - -import org.jboss.jandex.IndexView; -import org.objectweb.asm.ClassVisitor; - -import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer; -import io.quarkus.panache.common.deployment.visitors.PanacheRepositoryClassOperationGenerationVisitor; - -public class PanacheJpaRepositoryEnhancer extends PanacheRepositoryEnhancer { - - public PanacheJpaRepositoryEnhancer(IndexView index) { - super(index); - } - - @Override - public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) { - return new PanacheRepositoryClassOperationGenerationVisitor(className, outputClassVisitor, - this.indexView, ReactiveJavaJpaTypeBundle.BUNDLE); - } -} diff --git a/extensions/panache/hibernate-reactive-panache/runtime/pom.xml b/extensions/panache/hibernate-reactive-panache/runtime/pom.xml index 94f0cd6c6600d..44fe32b9e3059 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/pom.xml +++ b/extensions/panache/hibernate-reactive-panache/runtime/pom.xml @@ -32,6 +32,12 @@ io.smallrye.common smallrye-common-annotation + + + jakarta.data + jakarta.data-api + true + jakarta.json.bind jakarta.json.bind-api diff --git a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java index 75024176d7151..8293ffb6f3d88 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java +++ b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/CustomCountPanacheQuery.java @@ -2,7 +2,7 @@ import org.hibernate.reactive.mutiny.Mutiny; -import io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanacheQueryImpl; +import io.quarkus.hibernate.reactive.panache.common.runtime.CommonManagedPanacheQueryImpl; import io.smallrye.mutiny.Uni; //TODO this class is only needed by the Spring Data JPA module and would not be placed there if it weren't for a dev-mode classloader issue @@ -11,7 +11,7 @@ public class CustomCountPanacheQuery extends PanacheQueryImpl { public CustomCountPanacheQuery(Uni em, String query, String customCountQuery, Object paramsArrayOrMap) { - super(new CommonPanacheQueryImpl(em, query, null, null, paramsArrayOrMap) { + super(new CommonManagedPanacheQueryImpl(em, query, null, null, paramsArrayOrMap) { { this.customCountQueryForSpring = customCountQuery; } diff --git a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/JpaOperations.java b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/JpaOperations.java index 5f888b55d744b..9591e855e2584 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/JpaOperations.java +++ b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/JpaOperations.java @@ -4,10 +4,10 @@ import org.hibernate.reactive.mutiny.Mutiny; -import io.quarkus.hibernate.reactive.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.reactive.panache.common.runtime.AbstractManagedJpaOperations; import io.smallrye.mutiny.Uni; -public class JpaOperations extends AbstractJpaOperations> { +public class JpaOperations extends AbstractManagedJpaOperations> { public static final JpaOperations INSTANCE = new JpaOperations(); diff --git a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/PanacheQueryImpl.java b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/PanacheQueryImpl.java index 0c3c8f481e245..da80b34ce38c7 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/PanacheQueryImpl.java +++ b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/runtime/PanacheQueryImpl.java @@ -9,20 +9,20 @@ import org.hibernate.reactive.mutiny.Mutiny; import io.quarkus.hibernate.reactive.panache.PanacheQuery; -import io.quarkus.hibernate.reactive.panache.common.runtime.CommonPanacheQueryImpl; +import io.quarkus.hibernate.reactive.panache.common.runtime.CommonManagedPanacheQueryImpl; import io.quarkus.panache.common.Page; import io.quarkus.panache.common.Parameters; import io.smallrye.mutiny.Uni; public class PanacheQueryImpl implements PanacheQuery { - private CommonPanacheQueryImpl delegate; + private CommonManagedPanacheQueryImpl delegate; PanacheQueryImpl(Uni em, String query, String originalQuery, String orderBy, Object paramsArrayOrMap) { - this.delegate = new CommonPanacheQueryImpl(em, query, originalQuery, orderBy, paramsArrayOrMap); + this.delegate = new CommonManagedPanacheQueryImpl(em, query, originalQuery, orderBy, paramsArrayOrMap); } - protected PanacheQueryImpl(CommonPanacheQueryImpl delegate) { + protected PanacheQueryImpl(CommonManagedPanacheQueryImpl delegate) { this.delegate = delegate; } diff --git a/extensions/panache/hibernate-reactive-panache/runtime/src/test/java/io/quarkus/hibernate/reactive/panache/MetamodelTest.java b/extensions/panache/hibernate-reactive-panache/runtime/src/test/java/io/quarkus/hibernate/reactive/panache/MetamodelTest.java new file mode 100644 index 0000000000000..f08715b8ea473 --- /dev/null +++ b/extensions/panache/hibernate-reactive-panache/runtime/src/test/java/io/quarkus/hibernate/reactive/panache/MetamodelTest.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.reactive.panache; + +public class MetamodelTest { + // this only tests that we generated the metamodel, no need to run anything. + public void test() { + String orm = PanacheEntity_.ID; + String jd = _PanacheEntity.ID; + } +} diff --git a/extensions/panache/mongodb-panache-kotlin/deployment/src/main/java/io/quarkus/mongodb/panache/kotlin/deployment/KotlinPanacheMongoRepositoryEnhancer.java b/extensions/panache/mongodb-panache-kotlin/deployment/src/main/java/io/quarkus/mongodb/panache/kotlin/deployment/KotlinPanacheMongoRepositoryEnhancer.java index 12e24f3098619..fd3189398345a 100644 --- a/extensions/panache/mongodb-panache-kotlin/deployment/src/main/java/io/quarkus/mongodb/panache/kotlin/deployment/KotlinPanacheMongoRepositoryEnhancer.java +++ b/extensions/panache/mongodb-panache-kotlin/deployment/src/main/java/io/quarkus/mongodb/panache/kotlin/deployment/KotlinPanacheMongoRepositoryEnhancer.java @@ -11,17 +11,15 @@ import io.quarkus.panache.common.deployment.visitors.KotlinPanacheClassOperationGenerationVisitor; public class KotlinPanacheMongoRepositoryEnhancer extends PanacheRepositoryEnhancer { - private final TypeBundle types; public KotlinPanacheMongoRepositoryEnhancer(IndexView index, TypeBundle types) { - super(index); - this.types = types; + super(index, types); } @Override public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) { return new KotlinPanacheClassOperationGenerationVisitor(outputClassVisitor, - indexView.getClassByName(DotName.createSimple(className)), indexView, types, - types.repositoryBase(), Collections.emptyList()); + indexView.getClassByName(DotName.createSimple(className)), indexView, bundle, + bundle.repositoryBase(), Collections.emptyList()); } } diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoRepositoryEnhancer.java b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoRepositoryEnhancer.java index eb4cc0e7e4df6..01e538ad88e01 100644 --- a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoRepositoryEnhancer.java +++ b/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoRepositoryEnhancer.java @@ -8,16 +8,14 @@ import io.quarkus.panache.common.deployment.visitors.PanacheRepositoryClassOperationGenerationVisitor; public class PanacheMongoRepositoryEnhancer extends PanacheRepositoryEnhancer { - private final TypeBundle typeBundle; public PanacheMongoRepositoryEnhancer(IndexView index, TypeBundle typeBundle) { - super(index); - this.typeBundle = typeBundle; + super(index, typeBundle); } @Override public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) { - return new PanacheRepositoryClassOperationGenerationVisitor(className, outputClassVisitor, indexView, typeBundle); + return new PanacheRepositoryClassOperationGenerationVisitor(className, outputClassVisitor, indexView, bundle); } } diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/KotlinPanacheRepositoryEnhancer.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/KotlinPanacheRepositoryEnhancer.java index 6672129fd401c..3a3fdc67b9988 100644 --- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/KotlinPanacheRepositoryEnhancer.java +++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/KotlinPanacheRepositoryEnhancer.java @@ -11,13 +11,11 @@ public class KotlinPanacheRepositoryEnhancer extends PanacheRepositoryEnhancer { private List methodCustomizers; - private TypeBundle bundle; public KotlinPanacheRepositoryEnhancer(IndexView index, List methodCustomizers, TypeBundle bundle) { - super(index); + super(index, bundle); this.methodCustomizers = methodCustomizers; - this.bundle = bundle; } @Override diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheRepositoryEnhancer.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheRepositoryEnhancer.java index 83daf39ea1e66..92d024489cd8d 100644 --- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheRepositoryEnhancer.java +++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheRepositoryEnhancer.java @@ -10,9 +10,11 @@ public abstract class PanacheRepositoryEnhancer implements BiFunction { protected final IndexView indexView; + protected final TypeBundle bundle; - public PanacheRepositoryEnhancer(IndexView index) { + public PanacheRepositoryEnhancer(IndexView index, TypeBundle bundle) { this.indexView = index; + this.bundle = bundle; } @Override diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java b/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/hibernate/common/deployment/PanacheJpaRepositoryEnhancer.java similarity index 75% rename from extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java rename to extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/hibernate/common/deployment/PanacheJpaRepositoryEnhancer.java index fffe02fa3e159..eb48a9dbd0d9c 100644 --- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java +++ b/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/hibernate/common/deployment/PanacheJpaRepositoryEnhancer.java @@ -1,4 +1,4 @@ -package io.quarkus.hibernate.orm.panache.deployment; +package io.quarkus.panache.hibernate.common.deployment; import org.jboss.jandex.IndexView; import org.objectweb.asm.ClassVisitor; @@ -9,16 +9,13 @@ public class PanacheJpaRepositoryEnhancer extends PanacheRepositoryEnhancer { - private TypeBundle typeBundle; - - public PanacheJpaRepositoryEnhancer(IndexView index, TypeBundle typeBundle) { - super(index); - this.typeBundle = typeBundle; + public PanacheJpaRepositoryEnhancer(IndexView index, TypeBundle bundle) { + super(index, bundle); } @Override public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) { return new PanacheRepositoryClassOperationGenerationVisitor(className, outputClassVisitor, - this.indexView, typeBundle); + this.indexView, this.bundle); } } diff --git a/extensions/panache/pom.xml b/extensions/panache/pom.xml index 78a5e62e143c3..93779d078eef6 100644 --- a/extensions/panache/pom.xml +++ b/extensions/panache/pom.xml @@ -17,6 +17,7 @@ panache-common panache-hibernate-common panache-mock + hibernate-panache hibernate-orm-panache-common hibernate-orm-panache hibernate-orm-panache-kotlin diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/CustomQueryMethodsAdder.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/CustomQueryMethodsAdder.java index 253eade916e48..74dee5a9f3538 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/CustomQueryMethodsAdder.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/CustomQueryMethodsAdder.java @@ -36,7 +36,7 @@ import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; import io.quarkus.hibernate.orm.panache.runtime.AdditionalJpaOperations; import io.quarkus.panache.common.Parameters; import io.quarkus.panache.common.deployment.TypeBundle; @@ -206,7 +206,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.delete deleteCount = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "delete", long.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "delete", long.class, Class.class, String.class, Parameters.class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), @@ -216,7 +216,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.delete deleteCount = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "delete", long.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "delete", long.class, Class.class, String.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), @@ -247,7 +247,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.executeUpdate updateCount = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "executeUpdate", int.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "executeUpdate", int.class, String.class, Map.class), methodCreator.readStaticField(operationsField), methodCreator.load(queryString), @@ -257,7 +257,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.executeUpdate updateCount = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "executeUpdate", + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "executeUpdate", int.class, String.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.load(queryString), @@ -334,7 +334,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.find() panacheQuery = methodCreator.invokeStaticMethod( MethodDescriptor.ofMethod(AdditionalJpaOperations.class, "find", - PanacheQuery.class, AbstractJpaOperations.class, Class.class, String.class, + PanacheQuery.class, AbstractManagedJpaOperations.class, Class.class, String.class, String.class, io.quarkus.panache.common.Sort.class, Parameters.class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), @@ -347,7 +347,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.find() panacheQuery = methodCreator.invokeStaticMethod( MethodDescriptor.ofMethod(AdditionalJpaOperations.class, "find", - PanacheQuery.class, AbstractJpaOperations.class, Class.class, String.class, + PanacheQuery.class, AbstractManagedJpaOperations.class, Class.class, String.class, String.class, io.quarkus.panache.common.Sort.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java index bf743cb41b209..ccbecc6fa3cff 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java @@ -32,7 +32,7 @@ import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; import io.quarkus.hibernate.orm.panache.runtime.AdditionalJpaOperations; import io.quarkus.panache.common.deployment.TypeBundle; import io.quarkus.panache.hibernate.common.runtime.PanacheJpaUtil; @@ -165,7 +165,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.find() ResultHandle panacheQuery = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "find", Object.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "find", Object.class, Class.class, String.class, io.quarkus.panache.common.Sort.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), @@ -216,7 +216,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.count() ResultHandle count = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "count", long.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "count", long.class, Class.class, String.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), @@ -239,7 +239,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.exists() ResultHandle exists = methodCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "exists", boolean.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "exists", boolean.class, Class.class, String.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), @@ -268,7 +268,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr // call JpaOperations.delete() ResultHandle delete = methodCreator.invokeStaticMethod( MethodDescriptor.ofMethod(AdditionalJpaOperations.class, "deleteWithCascade", - long.class, AbstractJpaOperations.class, Class.class, String.class, Object[].class), + long.class, AbstractManagedJpaOperations.class, Class.class, String.class, Object[].class), methodCreator.readStaticField(operationsField), methodCreator.readInstanceField(entityClassFieldDescriptor, methodCreator.getThis()), methodCreator.load(parseResult.getQuery()), paramsArray); diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java index 0d65d147be519..b86eba2326760 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/StockMethodsAdder.java @@ -46,7 +46,7 @@ import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; import io.quarkus.hibernate.orm.panache.runtime.AdditionalJpaOperations; import io.quarkus.panache.common.deployment.TypeBundle; import io.quarkus.spring.data.deployment.DotNames; @@ -241,7 +241,7 @@ private boolean isPersistable(DotName entityDotName) { private void generatePersistAndReturn(ResultHandle entity, BytecodeCreator bytecodeCreator) { bytecodeCreator.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "persist", void.class, Object.class), + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "persist", void.class, Object.class), bytecodeCreator.readStaticField(operationsField), entity); bytecodeCreator.returnValue(entity); @@ -251,7 +251,7 @@ private void generateMergeAndReturn(ResultHandle entity, BytecodeCreator bytecod FieldDescriptor entityClassFieldDescriptor) { ResultHandle entityClass = bytecodeCreator.readInstanceField(entityClassFieldDescriptor, bytecodeCreator.getThis()); ResultHandle session = bytecodeCreator.invokeVirtualMethod( - ofMethod(AbstractJpaOperations.class, "getSession", Session.class, Class.class), + ofMethod(AbstractManagedJpaOperations.class, "getSession", Session.class, Class.class), bytecodeCreator.readStaticField(operationsField), entityClass); entity = bytecodeCreator.invokeInterfaceMethod( @@ -320,7 +320,7 @@ private void generateSaveAndFlush(ClassCreator classCreator, ResultHandle entity = saveAndFlush.getMethodParam(0); entity = saveAndFlush.invokeVirtualMethod(save, saveAndFlush.getThis(), entity); saveAndFlush.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "flush", void.class), + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "flush", void.class), saveAndFlush.readStaticField(operationsField)); saveAndFlush.returnValue(entity); } @@ -412,7 +412,7 @@ private void generateFlush(ClassCreator classCreator, String generatedClassName, try (MethodCreator flush = classCreator.getMethodCreator(flushDescriptor)) { flush.addAnnotation(Transactional.class); flush.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "flush", void.class), + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "flush", void.class), flush.readStaticField(operationsField)); flush.returnValue(null); } @@ -440,7 +440,7 @@ private void generateFindById(ClassCreator classCreator, FieldDescriptor entityC idTypeStr.replace('.', '/'), entityTypeStr.replace('.', '/'))); ResultHandle id = findById.getMethodParam(0); ResultHandle entity = findById.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "findById", Object.class, Class.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "findById", Object.class, Class.class, Object.class), findById.readStaticField(operationsField), findById.readInstanceField(entityClassFieldDescriptor, findById.getThis()), id); @@ -554,7 +554,7 @@ private void generateSpecificFindEntityReference(ClassCreator classCreator, Fiel try (MethodCreator findById = classCreator.getMethodCreator(getReferenceByIdDescriptor)) { ResultHandle entity = findById.invokeStaticMethod(ofMethod(RepositorySupport.class, actualMethodName, - Object.class, AbstractJpaOperations.class, Class.class, Object.class), + Object.class, AbstractManagedJpaOperations.class, Class.class, Object.class), findById.readStaticField(operationsField), findById.readInstanceField(entityClassFieldDescriptor, findById.getThis()), findById.getMethodParam(0)); @@ -591,7 +591,7 @@ private void generateFindAll(ClassCreator classCreator, FieldDescriptor entityCl findAll.setSignature(String.format("()Ljava/util/List;", entityTypeStr.replace('.', '/'))); ResultHandle panacheQuery = findAll.invokeVirtualMethod( - ofMethod(AbstractJpaOperations.class, "findAll", Object.class, Class.class), + ofMethod(AbstractManagedJpaOperations.class, "findAll", Object.class, Class.class), findAll.readStaticField(operationsField), findAll.readInstanceField(entityClassFieldDescriptor, findAll.getThis())); ResultHandle list = findAll.invokeInterfaceMethod( @@ -633,7 +633,7 @@ private void generateFindAllWithSort(ClassCreator classCreator, FieldDescriptor findAll.getMethodParam(0)); ResultHandle panacheQuery = findAll.invokeVirtualMethod( - ofMethod(AbstractJpaOperations.class, "findAll", Object.class, Class.class, + ofMethod(AbstractManagedJpaOperations.class, "findAll", Object.class, Class.class, io.quarkus.panache.common.Sort.class), findAll.readStaticField(operationsField), findAll.readInstanceField(entityClassFieldDescriptor, findAll.getThis()), sort); @@ -692,14 +692,14 @@ private void generateFindAllWithPageable(ClassCreator classCreator, FieldDescrip AssignableResultHandle panacheQueryVar = findAll.createVariable(PanacheQuery.class); ResultHandle panacheQueryWithoutSort = sortNullTrue.invokeVirtualMethod( - ofMethod(AbstractJpaOperations.class, "findAll", Object.class, Class.class), + ofMethod(AbstractManagedJpaOperations.class, "findAll", Object.class, Class.class), sortNullTrue.readStaticField(operationsField), sortNullTrue.readInstanceField(entityClassFieldDescriptor, sortNullTrue.getThis())); sortNullTrue.assign(panacheQueryVar, panacheQueryWithoutSort); sortNullTrue.breakScope(); ResultHandle panacheQueryWithSort = sortNullFalse.invokeVirtualMethod( - ofMethod(AbstractJpaOperations.class, "findAll", Object.class, Class.class, + ofMethod(AbstractManagedJpaOperations.class, "findAll", Object.class, Class.class, io.quarkus.panache.common.Sort.class), sortNullFalse.readStaticField(operationsField), sortNullFalse.readInstanceField(entityClassFieldDescriptor, sortNullFalse.getThis()), panacheSort); @@ -750,7 +750,7 @@ private void generateFindAllById(ClassCreator classCreator, FieldDescriptor enti ResultHandle list = findAllById.invokeStaticMethod( MethodDescriptor.ofMethod(RepositorySupport.class, "findByIds", - List.class, AbstractJpaOperations.class, Class.class, Iterable.class), + List.class, AbstractManagedJpaOperations.class, Class.class, Iterable.class), findAllById.readStaticField(operationsField), entityClass, findAllById.getMethodParam(0)); @@ -796,7 +796,7 @@ private void generateCount(ClassCreator classCreator, FieldDescriptor entityClas if (!classCreator.getExistingMethods().contains(countDescriptor)) { try (MethodCreator count = classCreator.getMethodCreator(countDescriptor)) { ResultHandle result = count.invokeVirtualMethod( - ofMethod(AbstractJpaOperations.class, "count", long.class, Class.class), + ofMethod(AbstractManagedJpaOperations.class, "count", long.class, Class.class), count.readStaticField(operationsField), count.readInstanceField(entityClassFieldDescriptor, count.getThis())); count.returnValue(result); @@ -826,7 +826,7 @@ private void generateDeleteById(ClassCreator classCreator, FieldDescriptor entit deleteById.getThis()); ResultHandle deleted = deleteById.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "deleteById", boolean.class, + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "deleteById", boolean.class, Class.class, Object.class), deleteById.readStaticField(operationsField), entityClass, id); @@ -884,7 +884,7 @@ private void generateDelete(ClassCreator classCreator, String generatedClassName delete.addAnnotation(Transactional.class); ResultHandle entity = delete.getMethodParam(0); delete.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "delete", void.class, Object.class), + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "delete", void.class, Object.class), delete.readStaticField(operationsField), entity); delete.returnValue(null); } @@ -950,7 +950,7 @@ private void generateSpecificDeleteAllWithIterable(ClassCreator classCreator, St ResultHandle entities = deleteAll.getMethodParam(0); deleteAll.invokeStaticMethod( MethodDescriptor.ofMethod(RepositorySupport.class, "deleteAll", - void.class, AbstractJpaOperations.class, Iterable.class), + void.class, AbstractManagedJpaOperations.class, Iterable.class), deleteAll.readStaticField(operationsField), entities); deleteAll.returnValue(null); @@ -971,7 +971,7 @@ private void generateDeleteAll(ClassCreator classCreator, FieldDescriptor entity deleteAll.addAnnotation(Transactional.class); deleteAll.invokeStaticMethod( MethodDescriptor.ofMethod(AdditionalJpaOperations.class, "deleteAllWithCascade", long.class, - AbstractJpaOperations.class, Class.class.getName()), + AbstractManagedJpaOperations.class, Class.class.getName()), deleteAll.readStaticField(operationsField), deleteAll.readInstanceField(entityClassFieldDescriptor, deleteAll.getThis())); deleteAll.returnValue(null); @@ -992,7 +992,7 @@ private void generateDeleteAllInBatch(ClassCreator classCreator, FieldDescriptor try (MethodCreator deleteAll = classCreator.getMethodCreator(deleteAllInBatchDescriptor)) { deleteAll.addAnnotation(Transactional.class); ResultHandle result = deleteAll.invokeVirtualMethod( - MethodDescriptor.ofMethod(AbstractJpaOperations.class, "deleteAll", long.class, Class.class), + MethodDescriptor.ofMethod(AbstractManagedJpaOperations.class, "deleteAll", long.class, Class.class), deleteAll.readStaticField(operationsField), deleteAll.readInstanceField(entityClassFieldDescriptor, deleteAll.getThis())); deleteAll.returnValue(result); diff --git a/extensions/spring-data-jpa/runtime/src/main/java/io/quarkus/spring/data/runtime/RepositorySupport.java b/extensions/spring-data-jpa/runtime/src/main/java/io/quarkus/spring/data/runtime/RepositorySupport.java index d8a4003ca3d52..74e0c40d29107 100644 --- a/extensions/spring-data-jpa/runtime/src/main/java/io/quarkus/spring/data/runtime/RepositorySupport.java +++ b/extensions/spring-data-jpa/runtime/src/main/java/io/quarkus/spring/data/runtime/RepositorySupport.java @@ -6,7 +6,7 @@ import io.quarkus.hibernate.orm.panache.Panache; import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations; +import io.quarkus.hibernate.orm.panache.common.runtime.AbstractManagedJpaOperations; @SuppressWarnings("unused") // the methods of this class are invoked in the generated bytecode public final class RepositorySupport { @@ -14,7 +14,7 @@ public final class RepositorySupport { private RepositorySupport() { } - public static List findByIds(AbstractJpaOperations> operations, Class entityClass, + public static List findByIds(AbstractManagedJpaOperations> operations, Class entityClass, Iterable ids) { Objects.requireNonNull(ids); @@ -28,7 +28,7 @@ public static List findByIds(AbstractJpaOperations> operation .toList(); } - public static void deleteAll(AbstractJpaOperations> operations, Iterable entities) { + public static void deleteAll(AbstractManagedJpaOperations> operations, Iterable entities) { for (Object entity : entities) { operations.delete(entity); } @@ -45,7 +45,7 @@ public static void deleteAll(AbstractJpaOperations> operations, * @deprecated use {@link RepositorySupport#getReferenceById)} instead. */ @Deprecated - public static Object getOne(AbstractJpaOperations> operations, Class entityClass, Object id) { + public static Object getOne(AbstractManagedJpaOperations> operations, Class entityClass, Object id) { return getReferenceById(operations, entityClass, id); } @@ -60,11 +60,12 @@ public static Object getOne(AbstractJpaOperations> operations, C * @deprecated use {@link RepositorySupport#getReferenceById)} instead. */ @Deprecated - public static Object getById(AbstractJpaOperations> operations, Class entityClass, Object id) { + public static Object getById(AbstractManagedJpaOperations> operations, Class entityClass, Object id) { return getReferenceById(operations, entityClass, id); } - public static Object getReferenceById(AbstractJpaOperations> operations, Class entityClass, Object id) { + public static Object getReferenceById(AbstractManagedJpaOperations> operations, Class entityClass, + Object id) { return operations.getSession(entityClass).getReference(entityClass, id); } diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Status.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Status.java index cf909fb2e0302..6fea916a81568 100644 --- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Status.java +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/defaultpu/Status.java @@ -1,6 +1,6 @@ package io.quarkus.it.panache.defaultpu; public enum Status { + DECEASED, LIVING, - DECEASED }