首页 > 解决方案 > 无法为事务打开 JPA EntityManager,在任务执行期间数据库连接关闭

问题描述

我一直在寻找类似的问题,但我发现的每个问题都有点不同。

我正在使用 SpringBoot (1.5.2.RELEASE) 和 PostgresSQL DB (42.0.0)。
我在下面描述问题:
我有一个方法,它需要一些实体,将其发送到外部服务,并将结果保存到数据库。每个实体都被一一获取(不在列表中)。这个过程有时需要几秒钟,有时甚至几分钟(取决于要处理的实体)。
一切正常,除非突然抛出异常:

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is 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:447)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
        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.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
        at com.service.someClass.settings.SettingsServiceImpl$$EnhancerBySpringCGLIB$$897e9c29.getPropertySettings(<generated>)
        ..rest of stackTrace

有时我需要 2:15(分:秒),有时需要 2:40 才能得到这个例外。
在对 DBeaver 进行一些调查后,我检查了与 DB 的连接select * from pg_stat_activity;。在流程执行期间,我正在刷新pg_stat_activity我注意到一个连接负责创建事务和提交。在几秒钟内SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE会出现该连接的查询。在开始时,我可以看到 3 个连接。在进程执行期间,我可以看到正在创建一个额外的连接,它从当前工作表中读取一些实体。一段时间后,负责创建和提交会话的连接消失了。休息连接已经获得了当前的查询SELECT 1,此时后端中的异常被抛出(我之前粘贴过)。
更具可读性(我希望)的版本如下: 在此处输入图像描述

我已经阅读了有关数据库连接配置的信息,并且该症状将我引导至 Tomcat 配置,如下所示:

tomcat:
      removeAbandoned: true
      removeAbandonedTimeout: 120
      testOnBorrow: true
      validationQuery: SELECT 1;
      validationInterval: 30000
      testWhileIdle: true
      timeBetweenEvictionRunsMillis: 60000
      maxActive: 10
      initialSize: 2
      minIdle: 2
      maxIdle: 5

测试了什么?
经过更多阅读后,我尝试设置removeAbandoned: false. 整个过程无一例外地完成。有什么不同?在创建了一个与 DB 的额外连接并完成了进程之后 - 该连接仍然出现在pg_stat_activity.
当我设置removeAbandonedTimeout:为较低的值时 - 进程崩溃得更快。我试过 60 和 10。当我设置值 10 时,进程在 30 秒后崩溃,第二次在 ~50 秒后崩溃。当它设置为 120 时,它会在 ~2:30-2:50(分钟)后崩溃得更多。
这就是为什么我假设以某种方式完成整个工作(创建和提交事务)的主要连接(错误与否)被识别为已放弃的连接(据我所知 - 未关闭)。
什么是重要的?关闭的连接一直在“工作” - 改变价值query列并更新开始和结束时间执行。现在更奇怪的是为什么突然关门了。

我不是很有经验,但我认为 TransactionManager (Spring) 可能有问题。

标签: springdatabasepostgresqlhibernatejpa

解决方案


经过文档研究,我发现了我的问题的起源和解决方案。

在我的情况下,问题在于 Tomcat 配置和我的进程可以执行几分钟的事实。
当服务开始工作时,它会从连接池中借用一个到 DB 的免费连接。这时一个计时器开始计算具体的数据库客户端占用特定连接的时间。事实上,我的连接每 3 秒执行几个查询,我假设(错误)根据定义它不能被放弃。

弃? 那么实际上,这abandoned意味着什么?abandoned当某些数据库客户端从连接池中借用连接并且没有将其归还某些值时,特定连接就变成了(removeAbandonedTimeout: (int)默认为 180 秒)。什么是重要的?这并不影响您如何使用该连接。如果它处于空闲状态,或者正在执行查询......有

什么帮助?
就我而言,我在 .yml 配置文件属性中定义:

spring:
  datasource:
    jdbcInterceptors: ResetAbandonedTimer

每次准备语句或执行查询时,计时器将重置连接池上的放弃计时器。这样即使是长进程(活进程)也不会超时。
我发现了一个与我的问题相关的 stackoverflow帖子,并跟踪我找到了很好的文章,其中很好地描述了这个功能

什么是好奇?即使在官方 Tomcat文档中我也没有找到解决方案。它说的是使用org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer和它的作用,但没有说什么属性可以“打开”这个功能。

希望它对某人有所帮助并节省时间。


推荐阅读