java - Spring + hibernate + HikariCP : how to handle DB connection while doing long running REST call?

标签 java hibernate spring-boot transactional hikaricp

我有一个项目在 Spring Boot 1.3.8、Hikari CP 2.6.1 和 Hibernate (Spring ORM 4.2.8) 上运行。服务层的代码如下所示:

public void doStuff() {
    A a = dao.findByWhatever();
    if (a.hasProperty()) {
        B b = restService.doRemoteRequestWithRetries(); // May take long time
    }
    a.setProp(b.getSomethig());
    dao.save(b);
}

Hikari 配置如下:spring.datasource.leakDetectionThreshold=2000。 问题是外部 REST 服务非常慢,通常需要 2 秒以上的时间才能响应,因此我们看到了很多 java.lang.Exception: Apparent connection leak detected 这不过是错误的负面的,尽管可以清楚地看到问题:我们在执行休息请求时保持数据库连接。

问题是:如何正确分离 DB 和 REST 的东西?或者如何告诉 hibernate 释放两者之间的连接?以便我们在等待 REST 响应时将数据库连接返回到池中。

我已经尝试设置 hibernate.connection.release_mode=AFTER_TRANSACTION 并且它有点帮助,至少我们没有连接泄漏异常。唯一的问题是我们的测试开始显示:

2018-04-17 15:48:03.438  WARN 94029 --- [           main] o.s.orm.jpa.vendor.HibernateJpaDialect   : JDBC Connection to reset not identical to originally prepared Connection - please make sure to use connection release mode ON_CLOSE (the default) and to run against Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false`

测试使用注入(inject)的 DAO 在数据库中插入记录,然后通过应用程序 API 检查它们。它们没有用 @Transactional 注释,并且监听器列表如下所示:

@TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    TransactionDbUnitTestExecutionListener.class
})

有什么想法可能是测试的问题吗?

最佳答案

在代码中

public void doStuff() {
    A a = dao.findByWhatever();
    if (a.hasProperty()) {
        B b = restService.doRemoteRequestWithRetries(); // May take long time
    }
    a.setProp(b.getSomethig());
    dao.save(b);
}

我在这里看到三个任务 - 获取实体 A、连接到远程服务和更新实体 A。并且所有这些都在同一个事务中,因此底层连接将保持到方法完成

因此,我们的想法是将任务一和任务三拆分为单独的事务,在调用远程服务之前允许释放连接。

基本上,对于 spring boot,您需要添加 spring.jpa.open-in-view=false。这不会注册 OpenEntityManagerInViewInterceptor,因此 entityManager(依次连接)未绑定(bind)到当前线程/请求。

随后,使用@Transactional 将这三个任务拆分为单独的方法。这有助于我们将 entityManager 绑定(bind)到事务范围并在事务方法结束时释放连接。

注意:并确保在调用这些方法之前没有任何事务开始/正在进行(即,调用者 - 如 Controller 等)。否则目的就失败了,这些新的@Transactional 方法将在与以前相同的事务中运行。


因此,高级方法可能如下所示:

  • 在 spring boot application.properties 添加属性 spring.jpa.open-in-view=false
  • 接下来您需要将doStuff 方法拆分为新服务类中的三个方法。目的是确保他们使用不同的交易。
    • 带有@Transactional 的第一个方法将调用 A a = dao.findByWhatever();`。
  • 第二种方法进行远程调用。
  • 使用@Transactional的第三种方法将通过 JPA merge 或对象上的 hibernate saveOrUpdate 调用其余代码a`。
  • 现在在您当前的代码中 Autowiring 这个新服务并调用 3 个方法。

关于java - Spring + hibernate + HikariCP : how to handle DB connection while doing long running REST call?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49883100/

相关文章:

hibernate - nHibernate.Search 使用 nHibernate v2

java - hibernate 异常: transaction roll back failed

java - Spring Boot 测试数据库初始化运行两次

java - 从 Java 中的字符串中删除 BOM

java - 无法使用 Rest API 数据填充 pojo

java - 找不到约束 'javax.validation.constraints.Size' 的 validator

java - 我应该如何获得 2 个 list<Object> 之间的差异

java - MapStruct 与 Spring Boot,使用自定义注释来注释生成的类

java - 如何将 HashMap(作为对象属性)保存到数据存储区

java - 如何在数学任务中使用我的输入