我正在使用 Timefold 1.15 和 Quarkus 3.1
我正在尝试在求解时间表时保存数据
但是我在解决过程中需要一些上下文,用于行安全性的租户上下文和身份上下文
但我面临与上下文不可用相关的问题
示例代码:
solverManager.solveBuilder()
.withProblemId(finalRequestId)
.withProblemFinder((id) -> timeTableJob.get(finalRequestId).timetable)
.withBestSolutionConsumer(bestSolution -> timeTableJob.put(finalRequestId, Job.ofTimetable(bestSolution)))
.withFinalBestSolutionConsumer(solution -> {
timeTableJob.put(finalRequestId,Job.ofTimetable(solution));
plannedLessonRepository.save(solution);
})
.withExceptionHandler((id, exception) -> {
timeTableJob.put(finalRequestId, Job.ofException(exception));
LOGGER.error("Failed solving jobId ({}).", finalRequestId, exception);
})
.run();
错误:
2024-11-26 17:18:32,463错误[org.acm.sch.res.TimeTableResource](pool-13-thread-1)无法解决jobId(101)。:org.hibernate.HibernateException:SessionFactory配置为多-tenancy,但未指定租户标识符 在 org.hibernate.internal.AbstractSharedSessionContract.getTenantId(AbstractSharedSessionContract.java:292) 在org.hibernate.internal.AbstractSharedSessionContract。(AbstractSharedSessionContract.java:193) 在 org.hibernate.internal.SessionImpl.(SessionImpl.java:230) 在 org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1381) 在 org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1247) 在 io.quarkus.hibernate.orm.runtime.session.JTASessionOpener.openSession(JTASessionOpener.java:46) 在 io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:92) 在 io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.find(TransactionScopedSession.java:175) 在 org.hibernate.engine.spi.SessionLazyDelegator.find(SessionLazyDelegator.java:825) 在 org.hibernate.Session_OpdLahisOZ9nWRPXMsEFQmQU03A_Synthetic_ClientProxy.find(来源未知) 在 io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.findById(AbstractJpaOperations.java:183) 在 org.acme.timetable.persistence.PlannedLessonRepository.findById(PlannedLessonRepository.java) 在 org.acme.timetable.persistence.PlannedLessonRepository.findById(PlannedLessonRepository.java) 在 org.acme.timetable.persistence.PlannedLessonRepository.save(PlannedLessonRepository.java:24) 在 org.acme.timetable.persistence.PlannedLessonRepository_Subclass.save$$superforward(来源未知) 在 org.acme.timetable.persistence.PlannedLessonRepository_Subclass$$function$$1.apply(来源未知) 在 io.quarkus.arc.impl.AroundInvokeInvocalContext.proceed(AroundInvokeInvocalContext.java:73) 在 io.quarkus.arc.impl.AroundInvokeInvocalContext.proceed(AroundInvokeInvocalContext.java:62) 在io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:136) 在io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:107) 在 io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38) 在io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:61) 在 io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32) 在 io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(来源未知) 在 io.quarkus.arc.impl.InterceptorInspiration.invoke(InterceptorInspiration.java:42) 在 io.quarkus.arc.impl.AroundInvokeInitationContext.perform(AroundInvokeInitationContext.java:30) 在 io.quarkus.arc.impl.InitationContexts.performAroundInvoke(InitationContexts.java:27) 在 org.acme.timetable.persistence.PlannedLessonRepository_Subclass.save(来源未知) 在 org.acme.timetable.persistence.PlannedLessonRepository_ClientProxy.save(来源未知) 在 org.acme.timetable.rest.TimeTableResource.lambda$solve$2(TimeTableResource.java:112) 在 ai.timefold.solver.core.impl.solver.ConsumerSupport.lambda$consumeFinalBestSolution$1(ConsumerSupport.java:102) 在 java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) 在 java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) 在 java.base/java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1136) 在 java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) 在 java.base/java.lang.Thread.run(Thread.java:833)
最佳解决方案使用者、最终最佳解决方案使用者和异常处理程序在与接受 API 调用的线程不同的线程中运行。因此,事务的上下文不会传播。
您可以做的是使用 QuarkusTransaction 在最终最佳解决方案使用者中启动和结束事务(并删除
@Transactional
注释)。
solverManager.solveBuilder()
.withProblemId(finalRequestId)
.withProblemFinder((id) -> timeTableJob.get(finalRequestId).timetable)
.withBestSolutionConsumer(bestSolution -> timeTableJob.put(finalRequestId, Job.ofTimetable(bestSolution)))
.withFinalBestSolutionConsumer(solution -> {
QuarkusTransaction.joiningExisting().run(() -> {
timeTableJob.put(finalRequestId,Job.ofTimetable(solution));
plannedLessonRepository.save(solution);
});
})
.withExceptionHandler((id, exception) -> {
timeTableJob.put(finalRequestId, Job.ofException(exception));
LOGGER.error("Failed solving jobId ({}).", finalRequestId, exception);
})
.run();