Spring Boot+JpaRepository+c3p0连接泄漏与死锁问题求助
问题排查与解决方案
一、控制器层@Transactional不生效的核心原因
Spring MVC控制器由DispatcherServlet直接调用,而Spring事务AOP默认基于动态代理实现。如果控制器未被正确代理(比如方法为final、未通过Spring代理链路调用),@Transactional注解会直接失效,导致每个数据库操作都开启新事务、获取新连接。另外,即使代理生效,若仓库方法事务传播行为(默认REQUIRED)因上层事务未启动,也会触发新事务创建。
二、具体修复步骤
1. 迁移事务注解到Service层(推荐方案)
控制器仅负责请求接收,将业务逻辑与事务控制放到Service层是Spring最佳实践,能彻底避免代理失效问题:
@Service @Transactional public class UserService { @Autowired private UserRegistrationRepository userRegistrationRepository; public Long getCustomerNumber(Long userId) { return userRegistrationRepository.getCustomerNumber(userId); // 该端点的其他数据库操作统一放在此处,共享同一事务连接 } }
控制器修改为注入Service调用:
@RestController @RequestMapping(value ={"/customer", "/agent"}, produces = {"application/json"}) public class UserController extends BaseController { @Autowired private UserService userService; @GetMapping("/customer/{userId}") public ResponseEntity<Long> getCustomerNo(@PathVariable Long userId) { return ResponseEntity.ok(userService.getCustomerNumber(userId)); } }
2. 若必须在控制器层使用事务(不推荐)
需确保以下两点:
- 控制器类及方法不能是final类型(动态代理无法代理final方法)
- 开启CGLIB代理支持,在配置类或配置文件中添加:
或在@Configuration @EnableTransactionManagement(proxyTargetClass = true) public class TransactionManagerConfig { // 原有事务管理器配置代码 }application.properties中添加:spring.aop.proxy-target-class=true
3. 优化数据库连接池配置
即使事务生效,并发过高时连接池耗尽仍会引发问题,以HikariCP为例调整参数:
# 最小空闲连接数 spring.datasource.hikari.minimum-idle=5 # 最大连接数(根据系统并发量合理设置,避免过大增加数据库负担) spring.datasource.hikari.maximum-pool-size=20 # 连接超时时间 spring.datasource.hikari.connection-timeout=30000 # 连接最大存活时间 spring.datasource.hikari.max-lifetime=1800000
4. 排查数据库死锁根源
死锁可能源于事务间资源竞争,需查询Oracle死锁日志(通过v$lock、v$session视图),确认锁竞争的表或行数据,优化SQL执行顺序、添加合适索引以减少锁等待时间。
三、关键注意事项
- 事务控制优先放在Service层,符合分层架构且避免代理失效风险
- 确保同一请求内的所有数据库操作都在同一事务上下文内,才能复用连接
- 连接池最大连接数需结合系统并发量和数据库承载能力合理设置
内容的提问来源于stack exchange,提问作者Hn073
相关产品推荐
相关产品推荐

