未加@Transactional的QueryDSL查询为何导致无法获取JDBC连接?
QueryDSL查询引发JDBC连接耗尽,添加@Transactional解决的原因解析
问题现象
调用接口多次后抛出连接获取失败异常:
Unable to acquire JDBC Connection; nested exception is org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection
通过pgAdmin观察到PostgreSQL中大量会话被该接口生成的SQL占据,当连接池被占满后,应用无法获取新连接。
相关代码与生成的SQL
接口方法代码
public Response getChainObjectDepartments(@RequestBody List<UUID> ids) { val ud = QUserDepartmentPlain.userDepartmentPlain; val d = QDepartmentPlain.departmentPlain; val result = jpaQueryFactory.select(d).from(d).join(ud).on(ud.departmentId.eq(d.id)).where(ud.userId.in(ids)).transform( groupBy(ud.userId).as(GroupBy.list(d)) ); return Response.ok(result); }
QueryDSL生成的SQL
select userdepart1_."user_id" as col_0_0_, department0_."id" as col_1_0_, department0_."id" as id1_29_, department0_."charge_leader_id" as charge_11_29_, department0_."dingtalk_id" as dingtalk2_29_, department0_."is_audit" as is_audi12_29_, department0_."is_enable" as is_enabl3_29_, department0_."name" as name4_29_, department0_."parent_id" as parent_i5_29_, department0_."remark" as remark6_29_, department0_."sort" as sort7_29_, department0_."tz_create" as tz_creat8_29_, department0_."tz_retire" as tz_retir9_29_, department0_."tz_update" as tz_upda10_29_ from "organization"."department" department0_ inner join "organization"."rel_user_department" userdepart1_ on ( userdepart1_."department_id"=department0_."id" ) where userdepart1_."user_id" in ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
原因分析
通常纯查询方法无需添加@Transactional,但这里的特殊点在于使用了QueryDSL的transform方法进行分组操作:
- 结果集遍历的连接持有问题:
transform方法需要遍历查询返回的完整结果集来完成分组逻辑。在没有事务上下文时,JPA的EntityManager处于非事务模式,查询执行后连接不会立即被释放回连接池——因为分组操作需要持续访问结果集,连接会被一直占用直到分组完成。高并发场景下,大量请求的连接长时间占用会直接耗尽连接池。 - 事务上下文的连接管理:添加
@Transactional后,Spring会为整个方法创建一个事务上下文,期间所有数据库操作共享同一个连接。当方法执行完毕(分组完成、结果返回),事务自动提交,EntityManager被关闭,连接会被正确释放回连接池,避免了连接长时间占用的问题。 - 非事务模式的JPA行为:Spring中,无事务的JPA查询默认是"每次操作获取连接",但
transform的分组逻辑属于查询后的内存处理,这个过程中连接并没有被及时回收,导致数据库会话一直处于活跃状态,最终堆积占满连接池。
总结
这个场景下@Transactional的作用并非开启事务支持,而是借助Spring的事务连接管理机制,确保在分组操作完成后及时释放JDBC连接,避免连接池耗尽。
内容的提问来源于stack exchange,提问作者Xiong Liding
相关产品推荐
相关产品推荐

