As the book continues, we learn about the dependency injection features of Quarkus. This time, let’s look at the concept of subscriptions.
Subscribe
Subscriptions are a concept you see a lot in Reactive programming.
Let’s start with a piece of code. Below, is a query interface that retrieves all brand information and returns it.
@Inject
BrandInfoRepository brandInfoRepository;
@Route(path = "/brandInfos", methods = HttpMethod.GET)
void brandInofs2(RoutingContext rc) {
brandInfoRepository.findAll()
.list()
.subscribe()
.with(brandInfos -> {
LOGGER.info("size:{}", brandInfos.size());
handleJson(rc, brandInfos);
},
t -> handleFail(rc, t));
}
Copy the code
And the subscribe() method is to subscribe. Let’s spread out the chain notation above.
@Inject
BrandInfoRepository brandInfoRepository;
@Route(path = "/brandInfos", methods = HttpMethod.GET)
void brandInfos(RoutingContext rc) {
PanacheQuery<BrandInfo> query = brandInfoRepository.findAll();
Uni<List<BrandInfo>> listUni = query.list();
UniSubscribe<List<BrandInfo>> uniSubscribe = listUni.subscribe();
Consumer<List<BrandInfo>> listConsumer = brandInfos -> {
LOGGER.info("btandInfos size: {}", brandInfos.size());
handleJson(rc, brandInfos);
};
Consumer<Throwable> throwableConsumer = t -> handleFail(rc, t);
uniSubscribe.with(listConsumer, throwableConsumer);
}
Copy the code
- Line 1 returns PanacheQuery, which is simply building a valid query condition, not initiating the query.
- Line 2 returns Uni for asynchronous operations that accept 0 or 1 result.
- Line 3 returns UniSubscribe, which is a subscription. (We subscribe to Uni to trigger computation)
- Lines 4-7 return to Consumer, the handler if it succeeds.
- Line 8 returns Consumer, what to do if it fails.
- Line 9
.with
Method that tells you what to do in case of success and failure.
Uni
Uni allows you to accept 0 or 1 asynchronous results.
/**
* A {@link Uni} represents a lazy asynchronous action. It follows the subscription pattern, meaning that the action
* is only triggered once a {@link UniSubscriber} subscribes to the {@link Uni}.
* <p>
* A {@link Uni} can have two outcomes:
* <ol>
* <li>An {@code item} event, forwarding the completion of the action (potentially {@code null} if the item
* does not represent a value, but the action was completed successfully)</li>
* <li>A {@code failure} event, forwarding an exception</li>
* </ol>
* <p>
* To trigger the computation, a {@link UniSubscriber} must subscribe to the Uni. It will be notified of the outcome
* once there is an {@code item} or {@code failure} event fired by the observed Uni. A subscriber receives
* (asynchronously) a {@link UniSubscription} and can cancel the demand at any time. Note that cancelling after
* having received the outcome is a no-op.
* <p>
*
* @param <T> the type of item produced by the {@link Uni}
*/
public interface Uni<T> {}
Copy the code
We don’t know if Uni returns 0 or 1. Uni is also Quarkus’ most common asynchronous return result.
- Uni is a lazy asynchronous operation that follows a subscription model.
- Only one subscription can be triggered.
- There are only two results. One is that an item is returned indicating that the operation is complete. The other is that an exception is returned.
- To trigger calculations, you must subscribe to Uni.
Return an element
Uni<BrandInfo> findOne(String id);
Copy the code
Return a collection
Uni<List<BrandInfo>> findAll();
Copy the code
The subsequent operation
Quarkus provides a wealth of methods for Uni’s subsequent processing, including apis that can be divided into data creation, event methods, and transformation methods.
There are two common subsequent operations: item and subscribe
Item
@Route(path = "/brandInfos3", methods = HttpMethod.GET)
Uni<List<BrandInfo>> brandInfos3(RoutingContext rc) {
return brandInfoRepository.findAll().list()
// Successfully get a non-empty Item
.onItem().ifNotNull().transformToUni(entity -> {
return Uni.createFrom().item(entity);
})
// If Item is empty, fail
.onItem().ifNull().fail();
}
Copy the code
If you can get the Item, you can proceed directly.
subscribe
@Route(path = "/brandInfos2", methods = HttpMethod.GET)
void brandInofs2(RoutingContext rc) {
brandInfoRepository.findAll()
.list()
.subscribe()
.with(brandInfos -> {
LOGGER.info("size:{}", brandInfos.size());
handleJson(rc, brandInfos);
},
t -> handleFail(rc, t));
}
Copy the code
Convert to UniSubscribe and work with the subscribed mode.
UniSubscribe
The purpose of UniSubscribe is to subscribe to the results returned asynchronously. It is often possible to convert Uni to a subscription approach to our business code.
/**
* Allow subscribing to a {@link Uni} to be notified of the different events coming from {@code upstream}.
* Two kind of events can be received:
* <ul>
* <li>{@code item} - the item of the {@link Uni}, can be {@code null}</li>
* <li>{@code failure} - the failure propagated by the {@link Uni}</li>
* </ul>
*
* @param <T> the type of item
*/
public class UniSubscribe<T> {}Copy the code
Uni is allowed to subscribe to upstream and we can take different events for processing.
When the asynchronous result is returned, UniSubscribe accepts two kinds of events (or consumers, if you like) :
- The result that you get back, that is
Uni
In theItem
, this value can benull
. - Exception returned, failed, not to mention.
gossip
From the perspective of coding experience, it is still recommended to use the subscription model for data operation, after all, the concept of subscription and consumer are familiar. We dictated the Quarkus asynchronous operation process: after we got a Uni, we subscribed to Uni, which triggered the calculation of item acquisition in Uni, and declared the processing method of success or failure in with.
The main business code is reflected in the process of consumer processing, the overall design is more elegant.