【问题标题】:Postgres connection has been closed error in Spring BootSpring Boot 中的 Postgres 连接已关闭错误
【发布时间】:2015-06-19 15:29:19
【问题描述】:

我正在运行一个 Spring Boot 应用程序来创建 REST API。我经常收到一条错误消息,指出数据库连接已关闭,之后我无法对应用程序进行任何调用。我正在使用 Postgres 数据库。这是完整的堆栈跟踪:

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed: 
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:457)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:276)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy91.findByUriMoniker(Unknown Source)
    at com.mypkg.businessobjects.OrderInfoBO.getOrderInfo(OrderInfoBO.java:76)
    at com.mypkg.controller.OrderInfoController.getOrderInfo(OrderInfoController.java:78)
    at sun.reflect.GeneratedMethodAccessor104.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:130)
    at com.mypkg.config.CORSFilter.doFilter(CORSFilter.java:39)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:60)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:132)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:60)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:132)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:85)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:63)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70)
    at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:261)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:247)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:76)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:166)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:197)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:759)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed: 
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:1771)
    at org.hibernate.jpa.internal.TransactionImpl.begin(TransactionImpl.java:64)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:159)
    at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:380)
    ... 56 more
Caused by: org.hibernate.TransactionException: JDBC begin transaction failed: 
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:76)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:162)
    at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1435)
    at org.hibernate.jpa.internal.TransactionImpl.begin(TransactionImpl.java:61)
    ... 58 more
Caused by: org.postgresql.util.PSQLException: This connection has been closed.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.checkClosed(AbstractJdbc2Connection.java:833)
    at org.postgresql.jdbc2.AbstractJdbc2Connection.getAutoCommit(AbstractJdbc2Connection.java:794)
    at sun.reflect.GeneratedMethodAccessor35.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108)
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:81)
    at com.sun.proxy.$Proxy56.getAutoCommit(Unknown Source)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:68)
    ... 61 more

当我重新启动应用程序时,它就消失了。我认为当我重新启动我的 postgres 数据库时会出现这个问题。为什么会这样?

【问题讨论】:

    标签: spring postgresql spring-boot


    【解决方案1】:

    这是其他帖子的一半回答,我想非常明确。我也想变得更像 Spring-Boot-esque。如有必要,请随意更改时间间隔。

    选项 1:从池中删除断开的连接。

    使用这些属性:

    spring.datasource.test-on-borrow=true
    spring.datasource.validation-query=SELECT 1;
    spring.datasource.validation-interval=30000
    

    选项 2:使池中的连接保持活动状态。

    使用这些属性:

    spring.datasource.test-while-idle=true
    spring.datasource.validation-query=SELECT 1;
    spring.datasource.time-between-eviction-runs-millis=60000
    

    选项 3:主动丢弃空闲连接。

    使用这些属性(注意:我无法为 Spring Boot 找到可靠的文档。超时时间是秒而不是毫秒):

    spring.datasource.remove-abandoned=true
    spring.datasource.remove-abandoned-timeout=60
    

    开机愉快!

    【讨论】:

    • Spring Boot 1.4 更明确地表明这些设置是特定于池实现的,因为它们需要正确添加前缀(例如 spring.datasource.tomcatspring.datasource.dbcpspring.datasource.dbcp2 或 @ 987654327@) 而不是 spring.datasource。 Spring 的宽松绑定然后会将它们传递给底层连接池实现。检查您正在使用的特定池的文档,了解正确的配置参数是什么……上面的 tomcat 和 dbcp 看起来不错,但 hikari 有点不同。
    【解决方案2】:

    非常有效的问题,这个问题通常是许多人面临的。异常通常发生在池和数据库之间的网络连接丢失时(大部分时间是由于重新启动)。查看您指定的堆栈跟踪,很明显您正在使用jdbc pool 来获取连接。 JDBC pool 具有微调各种连接池设置的选项,并记录有关池内发生的事情的详细信息。

    可以参考detailed apache documentation on pool configuration指定放弃超时时间

    检查removeAbandoned、removeAbandonedTimeout、logAbandoned参数

    此外,您可以使用其他属性来进一步加强验证

    使用 testXXX 和 validationQuery 获取连接有效性。

    【讨论】:

    • 此解决方案对您有帮助吗?我有类似的问题,但这对我没有帮助。
    • @Mark.ewd:如果仍然相关,您可以尝试stackoverflow.com/a/33660586/56285
    • 在 Spring Boot 中,如果您自动配置数据源,如果使用了适当的驱动程序类(例如 org.postgresql.Driver),池也会自动配置。您可以通过在 application.properties 中提供以下属性来调整池:spring.datasource.test-on-borrow=true, spring.datasource.remove-abandoned=true, spring.datasource.validation-query=SELECT 1;跨度>
    • 我看到了一个与此相关的问题 - “无法在事务中间更改事务隔离级别。”。我删除了上面评论中提到的这些设置,它开始正常工作。任何想法为什么?
    • @Tisha,我不建议更改隔离级别设置。请查看数据库驱动手册并检查是否需要配置任何其他设置以启用放弃连接。
    【解决方案3】:

    我遇到了完全相同的问题,with this setup,也使用DataSource from Tomcat (org.apache.tomcat.jdbc.pool) 连接到 Heroku Postgres:

    org.springframework.transaction.CannotCreateTransactionException: 
        Could not open JPA EntityManager for transaction
    org.hibernate.TransactionException: JDBC begin transaction failed: ] 
        with root cause
    org.postgresql.util.PSQLException: This connection has been closed.
    

    为我解决的问题是将它添加到 DataSource 初始化代码中(借用 a Grails question):

    dataSource.setTestOnBorrow(true);
    dataSource.setTestWhileIdle(true);
    dataSource.setTestOnReturn(true);
    dataSource.setValidationQuery("SELECT 1");
    

    我不确定是否需要所有这三个来获得稳定的连接——也许不需要——但启用所有这些可能不会造成太大的伤害。

    JavaDocs 阐明了发生了什么:参见例如setTestOnBorrow()。可能有点令人惊讶的是,默认情况下没有进行此类测试。

    【讨论】:

      【解决方案4】:

      这个异常基本上说 JDBC 连接被关闭了,但这并不意味着 数据库服务器未运行(还有另一个例外)。重新启动数据库服务器时可能会发生这种情况 或在数据库服务器断开连接之后(例如,由于超时)。 所以这里的问题是为什么应用程序没有在新的 HTTP 请求上重新连接到服务器。

      通常这是连接池的错误配置,它应该在每次应用程序时验证连接 “借”一个。解决这个问题只需要以下内容:

      spring.datasource.validation-query=SELECT 1;
      spring.datasource.test-on-borrow=true
      

      其他配置参数(来自其他答案)是此异常不严格要求的优化。

      但有时即使正确配置了 JDBC 池,也可能存在某个应用程序错误,其中应用程序持有数据库连接 HTTP 请求结束后不将其返回到 JDBC 池。 所以 JDBC 池甚至没有可能验证 DB 连接(它只知道 DB 连接是“ALLOCATED”)。 这里的一般解决方案是确保应用程序返回连接并在每个 HTTP 请求上“借用”一个新连接。

      此类错误的一个示例:

      @Component
      public MyService {
          @Resource
          private EntityManagerFactory emf;
          
          private EntityManager em;
          
          public MyService() {
               em = emf.createEntityManager();//em never return back its JDBC connection to the pool (using em.close())
          }
      }
      

      上述错误的解决方案是使用注入/托管的EntityManager(首选)

      @Component
      public MyService {
          @PersistenceContext
          private EntityManager em;
      }
      

      或者如果你真的需要自己管理它,为每个 HTTP 请求创建一个 EntityManager,如果你真的需要,在 try-finally 块中关闭它

      @Component
      public MyService {
          @Resource
          private EntityManagerFactory emf;
          
          private EntityManager em;
          
          public void myMethod() {
               EntityManager em = emf.createEntityManager();
               try {
               
               } finaly {
                  em.close();//do not forget other cleanup operations like rolling back the transaction
               }
          }
      }
      

      【讨论】:

        【解决方案5】:

        我遇到了完全相同的问题,但就我而言,上述答案没有帮助。我发现在进行长查询时会出现相同的错误。就我而言,我调用了 findAll(Iterable ids) 并传递了超过 100'000 个 id 的庞大列表。对列表进行分区(例如,使用来自 Apache Commons 或 Google Guava 的 ListUtils)并使用更少的 id 调用 findAll() 就可以了。

        【讨论】:

          【解决方案6】:

          当您在存储库中编写查询时,请尝试将 @Repository 注释保留在存储库中

          【讨论】:

            猜你喜欢
            • 2015-04-25
            • 2017-11-03
            • 2018-07-11
            • 2012-03-31
            • 2015-10-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多