|
| 1 | +[[reactive-repositories]] |
| 2 | +== Reactive repositories |
| 3 | + |
| 4 | +Hibernate Data Repositories provides repositories backed by https://hibernate.org/reactive/[Hibernate Reactive] for use in reactive programming. |
| 5 | +The methods of a reactive repository are non-blocking, and so every operation returns a reactive stream. |
| 6 | +This is an extension to the programming model defined by Jakarta Data. |
| 7 | + |
| 8 | +[NOTE] |
| 9 | +==== |
| 10 | +The Jakarta Data specification has not yet defined a way to write repositories for use in reactive programming, but the spec was written to accommodate such extensions, and this capability might be standardized in a future release. |
| 11 | +==== |
| 12 | + |
| 13 | +In Hibernate Data Repositories we use https://smallrye.io/smallrye-mutiny/[Mutiny] to work with reactive streams. |
| 14 | + |
| 15 | +[WARNING] |
| 16 | +==== |
| 17 | +If and when Jakarta Data _does_ provide standard support for reactive repositories, the functionality will almost certainly be based on Java's `CompletionStage`, and not on Mutiny. |
| 18 | +==== |
| 19 | + |
| 20 | +In our opinion, Mutiny is a _much_ more comfortable API than `CompletionStage`. |
| 21 | + |
| 22 | +=== Defining a reactive repository |
| 23 | + |
| 24 | +In the following code example we notice the two requirements for a reactive repository in Hibernate Data Repositories: |
| 25 | + |
| 26 | +1. there must be a resource accessor method returning the underlying `Mutiny.StatelessSession` from Hibernate Reactive, and |
| 27 | +2. the return type of every other operation is `Uni`, a reactive stream type defined by Mutiny. |
| 28 | + |
| 29 | +For example, a `@Find` method which would return `Book` in a regular Jakarta Data repository must return `Uni<Book>` in a reactive repository. |
| 30 | +Similarly, lifecycle methods usually return `Uni<Void>` instead of `void`. |
| 31 | + |
| 32 | +[source,java] |
| 33 | +---- |
| 34 | +@Repository |
| 35 | +interface Library { |
| 36 | +
|
| 37 | + Mutiny.StatelessSession session(); |
| 38 | +
|
| 39 | + @Find |
| 40 | + Uni<Book> book(String isbn); |
| 41 | +
|
| 42 | + @Insert |
| 43 | + Uni<Void> add(Book book); |
| 44 | +
|
| 45 | + @Find |
| 46 | + Uni<List<Book>> books(@By("isbn") String[] ibsns); |
| 47 | +} |
| 48 | +---- |
| 49 | + |
| 50 | +It's _not_ possible to mix blocking and non-blocking operations in the same repository interface. |
| 51 | + |
| 52 | +=== Obtaining a reactive repository |
| 53 | + |
| 54 | +To make use of our reactive repository, we'll need to bootstrap Hibernate Reactive and obtain a `Mutiny.SessionFactory`. |
| 55 | +For example, if we have a persistence unit named `example` in our `persistence.xml` file, we can obtain a `SessionFactory` like this: |
| 56 | + |
| 57 | +[source,java] |
| 58 | +---- |
| 59 | +Mutiny.SessionFactory factory = |
| 60 | + createEntityManagerFactory("example") |
| 61 | + .unwrap(Mutiny.SessionFactory.class); |
| 62 | +---- |
| 63 | + |
| 64 | +Please refer to the documentation for Hibernate Reactive for more information on this topic. |
| 65 | + |
| 66 | +Once we have the `SessionFactory`, we can easily obtain a `Mutiny.StatelessSession`, and use it to instantiate our repository: |
| 67 | + |
| 68 | +[source,java] |
| 69 | +---- |
| 70 | +factory.withStatelessTransaction(session -> { |
| 71 | + Library library = new Library_(session); |
| 72 | + ... |
| 73 | +}) |
| 74 | +---- |
| 75 | + |
| 76 | +TIP: In Quarkus, all this is unnecessary, and you can directly inject the `Library`. |
| 77 | + |
| 78 | +=== Calling a reactive repository |
| 79 | + |
| 80 | +To actually make use of a reactive repository, you'll need to be familiar with the programming model of reactive streams. |
| 81 | +For this, we refer you to the Mutiny documentation, and to the documentation for Hibernate Reactive, which goes over some gotchas. |
| 82 | + |
| 83 | +The most important thing to understand is that a code fragment like the following does _not_ result in any immediate interaction with the database: |
| 84 | + |
| 85 | +[source,java] |
| 86 | +---- |
| 87 | +Uni<Void> uni = |
| 88 | + factory.withStatelessTransaction(session -> { |
| 89 | + Library library = new Library_(session); |
| 90 | + return library.book("9781932394153"); |
| 91 | + }) |
| 92 | + .invoke(book -> out.println(book.title)) |
| 93 | + .replaceWithVoid(); |
| 94 | +---- |
| 95 | + |
| 96 | +This code does no more than construct a reactive stream. |
| 97 | +We can execute the stream blockingly by calling `uni.await().indefinitely()`, but that's not something we would ever do in real code. |
| 98 | +Instead, what we usually do is simply return the stream, allowing it to be executed in a non-blocking way. |
0 commit comments