Spring Boot Hikari连接池无法获取JDBC连接问题求助
从你提供的代码和异常日志来看,问题的核心是连接泄漏——每次请求创建的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

