问题
Dears, I want to update my domain entities according to message being received by Kafka topic. I am using Quarkus latest and Smallrye reactive messaging with Kafka. Pub-sub model is working fine with me, but in consumer method I am unable to update my entities using entityManager or HibernatePanache.
Whenever I try to use entityManager code inside consumer message, an exception is being thrown and handled silently. Here is my consumer code :
@Transactional
@Incoming("new-payment")
public CompletionStage<Void> updateTotalBuyers(String paymentEvent) {
return CompletableFuture.runAsync(() -> {
PaymentEvent event = jsonb.fromJson(paymentEvent, PaymentEvent.class);
TypedQuery<Book> query = em.createQuery("SELECT b FROM Book b where b.isbn=:isbn", Book.class);
query.setParameter("isbn", event.getIsbn());
Book book = query.getSingleResult();
book.setTotalBuyers(book.getTotalBuyers() + 1);
em.merge(book);
});
}
If anyone has a working code snippet for my problem it would be great. Also, how can I print that silent exception for further debugging ?
Update : I surrounded the code with try/catch block and the below exception is being thrown :
javax.enterprise.context.ContextNotActiveException: interface javax.enterprise.context.RequestScoped at io.quarkus.hibernate.orm.runtime.RequestScopedEntityManagerHolder_ClientProxy.arc$delegate(RequestScopedEntityManagerHolder_ClientProxy.zig:83) at io.quarkus.hibernate.orm.runtime.RequestScopedEntityManagerHolder_ClientProxy.getOrCreateEntityManager(RequestScopedEntityManagerHolder_ClientProxy.zig:191) at io.quarkus.hibernate.orm.runtime.entitymanager.TransactionScopedEntityManager.getEntityManager(TransactionScopedEntityManager.java:78) at io.quarkus.hibernate.orm.runtime.entitymanager.TransactionScopedEntityManager.createQuery(TransactionScopedEntityManager.java:317) at io.quarkus.hibernate.orm.runtime.entitymanager.ForwardingEntityManager.createQuery(ForwardingEntityManager.java:142) at io.quarkus.hibernate.orm.panache.runtime.JpaOperations.find(JpaOperations.java:208) at io.quarkus.hibernate.orm.panache.runtime.JpaOperations.find(JpaOperations.java:200) at org.ibra.ebs.book.model.Book.find(Book.java) at org.ibra.ebs.book.service.BookService.lambda$updateTotalBuyers$0(BookService.java:106) at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1626) at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
I added annotation @ActivateRequestContext
on both class and method with no luck.
Update : I tried to elevate context-propagation using
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-reactive-streams-operators</artifactId>
</dependency>
Also same exception is being thrown with some context-propagation classes (which means it is being activated).
Regards.
回答1:
Add @ApplicationScoped
and @ActivateRequestContext
both these annotations on the class and @Transactional
annotation on the updateTotalBuyers
method.
Cheers!
回答2:
Finally it worked :), Yes the answer was around context-propagation and here is what I did :
@Transactional
@Incoming("new-payment")
public CompletionStage<?> updateTotalBuyers(Message<String> paymentEvent) {
//TODO: move to CDI producer
ManagedExecutor executor = ManagedExecutor.builder()
.maxAsync(5)
.propagated(ThreadContext.CDI,
ThreadContext.TRANSACTION)
.build();
//TODO: move to CDI producer
ThreadContext threadContext = ThreadContext.builder()
.propagated(ThreadContext.CDI,
ThreadContext.TRANSACTION)
.build();
return executor.runAsync(threadContext.contextualRunnable(() -> {
try {
log.info("Into update total buyers");
PaymentEvent event = jsonb.fromJson(paymentEvent.getPayload(), PaymentEvent.class);
Book book = Book.find("isbn", event.getIsbn()).singleResult();
book.totalBuyers++;
book.persist();
log.info("Total books {}", book.totalBuyers);
} catch(Exception e) {
log.error("Something wrong happened !!!", e);
} finally {
paymentEvent.ack();
}
}));
}
I will try to make it more standard now.
回答3:
I had simillar issue using Quarkus + a single thread executor to collect AWS SQS messages.
- Initial - wrong approach:
private final ExecutorService scheduler = Executors.newSingleThreadExecutor();
scheduler.submit(/*MyRunnable*/)
- Solution: Injecting ManagedExecutor which is provided by quarkus:
@Inject
ManagedExecutor managedExecutor;
managedExecutor.submit(/*MyRunnable*/);
In your particular case you can replace following code with injecting ManagedExecutor.
ManagedExecutor executor = ManagedExecutor.builder()
.maxAsync(5)
.propagated(ThreadContext.CDI,
ThreadContext.TRANSACTION)
.build();
//TODO: move to CDI producer
ThreadContext threadContext = ThreadContext.builder()
.propagated(ThreadContext.CDI,
ThreadContext.TRANSACTION)
.build();
来源:https://stackoverflow.com/questions/58534957/how-to-execute-jpa-entity-manager-operations-inside-quarkus-kafka-consumer-metho