当我在 Jaxrs 资源上添加 @Transactional 时发生异常
Posted
技术标签:
【中文标题】当我在 Jaxrs 资源上添加 @Transactional 时发生异常【英文标题】:Exception occurs when I added @Transactional on Jaxrs Resource 【发布时间】:2019-09-10 08:08:20 【问题描述】:在 Quarkus 资源中,我尝试在响应中编写 Java 8 流。
我为 Stream 类型创建了 MessageBodyWriter。
@Provider
public class StreamBodyWriter implements MessageBodyWriter<Stream>
@Context
private Providers providers;
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType)
return Stream.class.isAssignableFrom(type);
@Override
public long getSize(Stream stream, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) return -1;
@Override
public void writeTo(Stream stream, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException
Object obj = stream.collect(Collectors.toList());
Class<?> objType = obj.getClass();
MessageBodyWriter writer = providers.getMessageBodyWriter(objType,
null, annotations, mediaType);
writer.writeTo(obj, objType, null, annotations,
mediaType, httpHeaders, entityStream);
Stream 需要一个 jdbc 游标才能使其工作,并且它需要事务支持。
我确定我已经安装了一个 postgres jdbc 扩展,并且应用程序启动时没有错误。
@Transactional
public Response getAllPosts(
@QueryParam("q") String q,
@QueryParam("offset") @DefaultValue("0") int offset,
@QueryParam("size") @DefaultValue("10") int size
)
return ok(this.posts.findByKeyword(q, offset, size)).build();
@ApplicationScoped
public class PostRepository implements PanacheRepositoryBase<Post, String>
public Stream<Post> findByKeyword(String q, int offset, int size)
if (q == null || q.trim().isEmpty())
return this.streamAll(Sort.descending("createdAt"))
.skip(offset)
.limit(size);
else
return this.streamAll(Sort.descending("createdAt"))
.filter(p -> p.title.contains(q) || p.content.contains(q))
.skip(offset)
.limit(size);
@Entity
public class Post implements Serializable
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
String id;
String title;
String content;
LocalDateTime createdAt;
当我在资源上添加 CDI @Transactional 时,在启动阶段收到以下错误。
[38;5;203m: org.jboss.resteasy.spi.UnhandledException: org.hibernate.exception.GenericJDBCException: could not advance using next()
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:381)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:209)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:587)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:508)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:252)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:249)
at io.quarkus.resteasy.runtime.ResteasyFilter$ResteasyResponseWrapper.sendError(ResteasyFilter.java:64)
at io.undertow.servlet.handlers.DefaultServlet.doGet(DefaultServlet.java:175)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at io.quarkus.resteasy.runtime.ResteasyFilter.doFilter(ResteasyFilter.java:28)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.server.handlers.PathHandler.handleRequest(PathHandler.java:91)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$9$1$1.call(UndertowDeploymentRecorder.java:513)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:174)
at io.undertow.server.handlers.HttpContinueReadHandler.handleRequest(HttpContinueReadHandler.java:65)
at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$1.handleRequest(UndertowDeploymentRecorder.java:92)
at io.undertow.server.handlers.CanonicalPathHandler.handleRequest(CanonicalPathHandler.java:49)
at io.quarkus.undertow.deployment.devmode.UndertowHotReplacementSetup.handleHotDeploymentRequest(UndertowHotReplacementSetup.java:85)
at io.quarkus.undertow.deployment.devmode.UndertowHotReplacementSetup$1$1.handleRequest(UndertowHotReplacementSetup.java:61)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:376)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:224)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1395)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at java.base/java.lang.Thread.run(Thread.java:834)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: org.hibernate.exception.GenericJDBCException: could not advance using next()
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
at org.hibernate.internal.ScrollableResultsImpl.convert(ScrollableResultsImpl.java:70)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:105)
at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33)
at java.base/java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1811)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at com.example.StreamBodyWriter.writeTo(StreamBodyWriter.java:39)
at com.example.StreamBodyWriter.writeTo(StreamBodyWriter.java:17)
at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:193)
at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:64)
at org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:155)
at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$2(ServerResponseWriter.java:156)
at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:405)
at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:232)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:97)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:70)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:578)
... 59 more
Caused by: java.sql.SQLException: ResultSet is closed
at io.agroal.pool.wrapper.ResultSetWrapper$1.invoke(ResultSetWrapper.java:49)
at com.sun.proxy.$Proxy81.next(Unknown Source)
at io.agroal.pool.wrapper.ResultSetWrapper.next(ResultSetWrapper.java:85)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:100)
... 79 more
【问题讨论】:
您已经按照文档中的描述设置了属性? quarkus.io/guides/datasource-guide @SimonMartinelli 我刚刚清理了我的代码,然后再次运行应用程序,得到了 updated 异常。奇怪的是,如果我删除所有@Transactional
,返回结果,但我不确定是不是基于游标来获取结果。从打印日志中查询所有结果。
看看这里***.com/questions/56430329/…
【参考方案1】:
当您使用返回 Stream
的实体方法时,您需要让该方法在事务中处理它。
在您的代码中,情况并非如此,因为 Stream 由您的 StreamBodyWriter
处理,collect()
方法所在的位置。
要解决此问题,您必须在 getAllPosts
或 findByKeyword
方法内处理您的流。所以摆脱StreamBodyWriter
并从你的休息端点返回一个集合。
【讨论】:
我知道这种方式行得通,但是如何让Stream在StreamBodyWriter中运行良好? 你不能,它必须是事务的一部分,Quarkus 上的事务实现基于 CDI 拦截器,它将拦截带有@Transactional
注释的方法调用。我不认为您的 StreamBodyWriter
可能是事务的一部分,因为它将在您的 JAX-RS 资源(定义事务的资源)之外由 resteasy 使用。以上是关于当我在 Jaxrs 资源上添加 @Transactional 时发生异常的主要内容,如果未能解决你的问题,请参考以下文章
使用 Guice 3.0 + JaxRS 2.0 对 REST API 进行版本控制
当我在 Visual Studio 2017 的解决方案资源管理器中右键单击 Web 项目时,无法将添加->现有项目视为 Azure Web 作业选项