我尝试从线程使用 Panache 存储库,该存储库不是由 Quarkus “管理”的(因为我们使用 ActiveMQ 5 JmsListeners,其中 javax.jms.MessageConsumer 在其自己的线程池中运行。它的实现如下代码片段:
@ApplicationScoped
public class MyConsumer implements javax.jms.MessageListener {
@Inject
javax.jms.ConnectionFactory connectionFactory;
@Inject
MyPanacheRepository repository;
private Session session;
public void init(@Observes StartupEvent event) {
if (this.connectionFactory != null) {
try {
Connection connection = this.connectionFactory.createConnection();
connection.start();
this.session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(...);
MessageConsumer consumer = this.session.createConsumer(...);
consumer.setMessageListener(this);
} catch (JMSException e) {
...
}
}
}
@PreDestroy
public void destroy() {
if (this.session != null) {
try {
this.session.close();
} catch (JMSException e) {
...
}
}
}
public void onMessage(Message message) {
Job job = this.repository.findById(message.getLongProperty("jobId"));
}
}
当 onMessage() 方法收到消息时,出现以下异常:
ERROR [MyConsumer] (ActiveMQ Session Task-3) javax.enterprise.context.ContextNotActiveException: Cannot use the EntityManager/Session because neither a transaction nor a CDI request context is active. Consider adding @Transactional to your method to automatically activate a transaction, or @ActivateRequestContext if you have valid reasons not to use transactions.
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:106)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.find(TransactionScopedSession.java:173)
at org.hibernate.engine.spi.SessionLazyDelegator.find(SessionLazyDelegator.java:650)
at org.hibernate.Session_5b93bee577ae2f8d76647de04cfab36afbf52958_Synthetic_ClientProxy.find(Unknown Source)
at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.findById(AbstractJpaOperations.java:179)
at MyPanacheRepository.findById(MyPanacheRepository.java)
因此 PanacheRepository 找不到获取在线程 ActiveMQ 会话任务 3 中运行的 Hibernate 会话的方法。
解决这个问题的最佳解决方案是什么?我可以通过在 findById(....) 周围添加 UserTransaction.begin() 调用来解决这个问题,但我真的不需要事务来读取数据!?
我希望我早点为你看到这个。
问题是当您尝试打开与数据库的连接时,请求上下文尚未激活,因为应用程序尚未完成启动。
这一行:
public void init(@Observes StartupEvent event) {}
将使您的 init 函数在 Quarkus 应用程序完成其所有启动任务之前运行,其中之一是与数据库建立请求上下文。
为了解决您的问题,您可以在 init 方法上添加此注释,告诉它在运行 init 方法之前启动数据库请求上下文。
import jakarta.enterprise.context.control.ActivateRequestContext;
@ApplicationScoped
public class MyConsumer implements javax.jms.MessageListener {
@ActivateRequestContext
public void init(@Observes StartupEvent event) {}
}