【问题标题】:Connection leak using Hibernate SessionFactory in Spring Boot在 Spring Boot 中使用 Hibernate SessionFactory 的连接泄漏
【发布时间】:2017-10-16 02:26:54
【问题描述】:

我有一个Spring Boot (1.5.3) 应用程序,我在其中自动连接Hibernate (5.0.12) SessionFactory,如下所示:

application.properties:

spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext

在配置类中:

@Bean
public HibernateJpaSessionFactoryBean sessionFactory() {
    return new HibernateJpaSessionFactoryBean();
}

@Services

@Autowired
private SessionFactory sessionFactory;

这很好用。但我的问题是,在每次请求之后,我得到越来越多的空闲 PostgreSQL 进程,并且在多次请求之后,最终我得到了这个:

Caused by: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.postgresql.util.PSQLException: FATAL: remaining connection slots are reserved for non-replication superuser connections
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
...
[ERROR] [16.05.17 20:19:39]               DirectJDKLog.java:181  Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; 
nested exception is javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431) ~[spring-orm-4.3.8.RELEASE.jar:4.3.8.RELEASE]
...
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.postgresql.util.PSQLException: FATAL: remaining connection slots are reserved for non-replication superuser connections
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
...
[ERROR] [16.05.17 20:19:39]               DirectJDKLog.java:181  Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction;
nested exception is javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431) ~[spring-orm-4.3.8.RELEASE.jar:4.3.8.RELEASE]
...
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
...
Caused by: org.postgresql.util.PSQLException: FATAL: remaining connection slots are reserved for non-replication superuser connections
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]
...
[ WARN] [16.05.17 20:19:40]         SqlExceptionHelper.java:127  SQL Error: 0, SQLState: 53300
[ERROR] [16.05.17 20:19:40]         SqlExceptionHelper.java:129  FATAL: remaining connection slots are reserved for non-replication superuser connections
[ERROR] [16.05.17 20:19:40]               DirectJDKLog.java:181  Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; 
nested exception is org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection] with root cause
org.postgresql.util.PSQLException: FATAL: remaining connection slots are reserved for non-replication superuser connections
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455) ~[postgresql-9.4.1212.jre7.jar:9.4.1212.jre7]

我在简单的 SQL 查询中经常使用StatelessSession,并且我有 99.9% 的把握在返回结果之前在相关的@Service 中关闭它们。

我有一种未确认的感觉,对于早期的 Spring Boot 版本和引入的依赖项,问题并不存在。不过不确定...

这些明显泄漏的原因可能是什么?

为了完整起见,这里有一些示例在 @Service: 中使用:

@Transactional(readOnly = true)
@Cacheable(value = "countries", key = "#root.methodName")
public List<Country> getCountries() {
    final StatelessSession session = sessionFactory.openStatelessSession();
    final Query query = session.createQuery("from Country order by id");
    final List<Country> result = query.list();
    session.close();
    return result;
}

@Transactional(readOnly = true)
public long countTimeZones() {
    final StatelessSession session = sessionFactory.openStatelessSession();
    final Long result = (Long) session.createQuery("select count(o) from TimeZone o").uniqueResult();
    session.close();
    return result;
}

@Transactional(readOnly = true)
public List<Map<String, Object>> getPhotoAlbums() {
    final StatelessSession session = sessionFactory.openStatelessSession();
    final SQLQuery query = session.createSQLQuery("select "
            + "cast(m.id as varchar), "
            + "m.name "
            // etc
            + "from media_album m "
            + "where m.account = :account "
            + "and ... "
            + "order by ...");
    query.setParameter("account", uuidOfAccount);
    query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
    final List<Map<String, Object>> result = query.list();
    session.close();
    return result;
}

【问题讨论】:

    标签: java postgresql hibernate spring-boot


    【解决方案1】:

    我可以通过如下更改配置来“解决”泄漏问题(但不确定所有这些步骤是否必要):

    application.properties:

    spring.jpa.properties.hibernate.current_session_context_class=
    org.springframework.orm.hibernate5.SpringSessionContext
    

    配置类:

    添加@EnableAutoConfiguration,以及:

    @Bean
    public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory emf) {
        HibernateJpaSessionFactoryBean factory = new HibernateJpaSessionFactoryBean();
        factory.setEntityManagerFactory(emf);
        return factory;
    }
    

    这个答案很有帮助:

    required a bean of type 'org.hibernate.SessionFactory' that could not be found

    【讨论】:

      【解决方案2】:

      我相信你需要设置以下属性:

      spring.datasource.max-active
      

      例如spring.datasource.max-active=5

      似乎弹簧连接池打开的连接数超出了您的 postgresql.conf 文件中允许的数量

      【讨论】:

      • 设置 max-active 并不能解决“泄漏”问题,但似乎只是设置了可以使用多少连接的硬限制。
      • 我建议将您对 session.close() 的调用放在 finally 块中,以确保它们被正确关闭
      猜你喜欢
      • 2011-02-18
      • 2014-09-23
      • 2018-03-22
      • 2019-03-21
      • 1970-01-01
      • 1970-01-01
      • 2018-07-25
      • 2019-04-16
      • 2015-12-04
      相关资源
      最近更新 更多