You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Hikari连接池无法获取JDBC连接问题求助

排查Hikari连接池耗尽问题:EntityManager未正确管理

从你提供的代码和异常日志来看,问题的核心是连接泄漏——每次请求创建的EntityManager没有被正确关闭,导致Hikari连接池里的连接被持续占用,最终耗尽所有可用连接,引发超时异常。

问题根源分析

先看你的BaseDAO里的em()方法:

protected EntityManager em() {
    return emf.createEntityManager();
}

每次调用这个方法都会创建一个新的EntityManager实例,而EntityManager初始化时会从Hikari连接池获取一条JDBC连接。如果这个EntityManager没有被手动关闭,对应的JDBC连接就不会归还到连接池里。

再看StudentDao的findStudentSearchData方法:虽然加了@Transactional注解,但这个注解是用来管理Spring容器托管的EntityManager的,而你手动通过emf.createEntityManager()创建的EntityManager完全脱离了Spring的事务管理范围,所以事务结束后Spring不会帮你关闭它,连接就一直被占用着。当并发请求达到连接池最大容量(日志里看到total=10),新的请求就会等待连接超时。

解决方法

这里有两种可靠的修复方案,推荐第一种更贴合Spring生态的方式:

方案1:让Spring托管EntityManager(推荐)

修改BaseDAO,直接注入Spring管理的EntityManager,而不是自己通过EntityManagerFactory创建:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import java.io.Serializable;

public abstract class BaseDAO<E extends Serializable, K extends Serializable> {
    protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
    private final Class<E> entityType;
    private final Class<K> keyType;
    
    // 直接注入Spring管理的EntityManager
    @Autowired
    private EntityManager em;

    public BaseDAO(final Class<E> entityType, final Class<K> keyType) {
        this.entityType = entityType;
        this.keyType = keyType;
    }

    protected Class<E> getEntityType() {
        return this.entityType;
    }

    protected Class<K> getKeyType() {
        return this.keyType;
    }

    protected EntityManager em() {
        return em; // 返回Spring托管的实例,无需手动创建
    }

    protected CriteriaBuilder cb() {
        return em.getCriteriaBuilder(); // 从托管的EntityManager获取CriteriaBuilder
    }
}

这样一来,@Transactional注解就能正常工作了:Spring会在事务开始时绑定EntityManager,事务结束时自动关闭它,对应的JDBC连接会归还到Hikari连接池,不会出现泄漏。

方案2:手动管理EntityManager的生命周期(不推荐,仅作备选)

如果你因为某些原因必须手动创建EntityManager,一定要用try-with-resources语法确保它被自动关闭:

@Repository
public class StudentDao extends BaseDAO<StudentEntity, Integer> {
    public StudentDao() {
        super(StudentEntity.class, Integer.class);
    }

    @Transactional(readOnly = true)
    public List<StudentEntity> findStudentSearchData() {
        // 使用try-with-resources自动关闭EntityManager
        try (EntityManager entityManager = emf.createEntityManager()) {
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            CriteriaQuery<StudentEntity> query = cb.createQuery(StudentEntity.class);
            Root<StudentEntity> from = query.from(StudentEntity.class);
            CriteriaQuery<StudentEntity> select = query.select(from);
            TypedQuery<StudentEntity> studentEntityTypedQuery = entityManager.createQuery(select).setMaxResults(50);
            return studentEntityTypedQuery.getResultList();
        } // 此处自动关闭EntityManager,连接归还连接池
    }
}

注意:这种方式下@Transactional注解其实是多余的,因为你手动创建的EntityManager不在Spring事务上下文里,注解无法对它生效。

验证修复效果

修复后,你可以再次并发调用API,观察Hikari的日志:如果active连接数在请求结束后能回到0,说明连接已经正常归还,问题解决。

内容的提问来源于stack exchange,提问作者Ashish Ratan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:46:45