第88篇JDBC与连接池2026版系列导航《Java 100 天进阶之路》完整目录 |⬅️ 上一篇第87篇MyBatis源码阅读 |➡️ 下一篇第89篇MySQL面试压轴题一、核心知识点JDBC 体系架构API 层 → 驱动层厂商实现→ SPI 层Driver动态加载三种 StatementStatement静态SQL、PreparedStatement预编译防注入、CallableStatement存储过程PreparedStatement防注入原理SQL 模板预编译 → 参数独立传递 → 只作为数据处理连接池核心概念DataSource替代DriverManagerHikariCPvsDruid选型连接池核心参数maximum-pool-size、connection-timeout、max-lifetime、idle-timeout2026架构演进R2DBC响应式虚拟线程 传统JDBC简单替代方案动态治理配置中心联动Nacos/Apollo、弹性伸缩、连接泄漏熔断可观测性OpenTelemetry Micrometer 统一指标采集安全加固SQL 防火墙WallFilter、字段级加密jasypt、数据库代理层现代化语法JDK 21RecordJdbcTemplatetry-with-resources二、通俗讲解1分钟开心学1. JDBC 是什么JDBC 是 Java 操作数据库的官方标准 API。它定义了Connection、Statement、ResultSet等接口由各数据库厂商提供具体实现如 MySQL Connector/J。生活类比JDBC 就像手机充电器的统一接口标准USB-C。你只需按标准插拔不用管充电头内部怎么变压——标准由 Java 制定各数据库厂商提供“充电头”驱动。2. 连接池是什么每次操作数据库都创建和关闭连接是非常昂贵的操作TCP 三次握手 认证。连接池在程序启动时预先创建一批连接使用时“借”用完“还”省去反复创建销毁的开销。生活类比没有连接池每次用水都要现挖一口井。有连接池提前建好水库用水时直接开闸取水。3. 为什么 PreparedStatement 能防 SQL 注入普通Statement是字符串拼接用户输入 OR 11会被拼进 SQL变成恒真条件绕过登录。PreparedStatement先将 SQL 模板发送给数据库预编译参数值通过占位符独立传递只作为数据处理不被解析为 SQL 逻辑从根本上杜绝注入。一句话总结Statement把用户输入当“代码”拼进去PreparedStatement把用户输入当“数据”传进去。4. 2026架构新视角响应式 vs 虚拟线程传统 JDBC 连接池阻塞 I/O通过大连接池应对并发。最简单、最成熟适合 90% 的业务系统。R2DBC响应式非阻塞 I/O一个连接可多路复用处理多个请求。适合网关、实时数据流、超高并发场景。虚拟线程 传统 JDBCJDK 21 引入虚拟线程用轻量级线程替代平台线程。使用传统 JDBC 也能获得接近响应式的并发能力且代码无需改造是 2026 年主流推荐方案。一句话选型先尝试虚拟线程 传统 JDBC不够再上 R2DBC。5. HikariCP vs Druid vs R2DBC 一句话定位选型定位适用场景HikariCP极速、轻量高并发互联网应用Spring Boot 默认Druid监控型全功能需要深度 SQL 分析、审计的中大型企业R2DBC响应式非阻塞网关、实时流处理、超高并发 I/O 密集型三、实操代码案例 场景说明3.1 JDBC 基础操作try-with-resourcespublicUsergetUserById(intuserId){StringsqlSELECT id, name, age FROM user WHERE id ?;try(ConnectionconnDriverManager.getConnection(url,username,password);PreparedStatementpstmtconn.prepareStatement(sql)){pstmt.setInt(1,userId);try(ResultSetrspstmt.executeQuery()){if(rs.next()){returnnewUser(rs.getInt(id),rs.getString(name),rs.getInt(age));}}}catch(SQLExceptione){log.error(查询失败,e);}returnnull;}关键点ResultSet也需要在嵌套的 try-with-resources 中关闭生产环境用DataSource替代DriverManager。3.2 SQL 注入演示 vs PreparedStatement 防护❌ 危险写法StringsqlSELECT * FROM users WHERE username username AND password password;Statementstmtconn.createStatement();ResultSetrsstmt.executeQuery(sql);// 攻击者输入 username admin --直接绕过密码校验✅ 安全写法StringsqlSELECT * FROM users WHERE username ? AND password ?;try(PreparedStatementpstmtconn.prepareStatement(sql)){pstmt.setString(1,username);pstmt.setString(2,password);ResultSetrspstmt.executeQuery();// 参数作为纯数据传递}3.3 批量操作优化publicint[]batchInsertOrders(ListOrderorders){StringsqlINSERT INTO orders (user_id, amount, status) VALUES (?, ?, ?);try(ConnectionconndataSource.getConnection();PreparedStatementpstmtconn.prepareStatement(sql)){for(Orderorder:orders){pstmt.setInt(1,order.userId());pstmt.setBigDecimal(2,order.amount());pstmt.setInt(3,order.status());pstmt.addBatch();}returnpstmt.executeBatch();}}3.4 Spring Boot HikariCP 配置⚠️重要生产环境禁止硬编码用户名和密码。以下使用环境变量占位符密码需通过 Kubernetes Secret 或配置中心注入。spring:datasource:url:jdbc:mysql://localhost:3306/testdb?useUnicodetrueserverTimezoneAsia/Shanghaiusername:${DB_USERNAME:app_user}# 使用环境变量禁止硬编码password:${DB_PASSWORD:changeme_in_production}# 使用环境变量禁止硬编码driver-class-name:com.mysql.cj.jdbc.Driverhikari:minimum-idle:10maximum-pool-size:20connection-timeout:5000idle-timeout:60000max-lifetime:300000connection-test-query:SELECT 1leak-detection-threshold:5000# 开发/测试环境开启通过环境变量注入凭证# 方式一直接注入本地开发exportDB_USERNAMEmyappexportDB_PASSWORDMyStrongPssw0rdjava-jarmyapp.jar# 方式二Docker 容器注入dockerrun-eDB_USERNAMEmyapp-eDB_PASSWORDMyStrongPssw0rd myapp:latest# 方式三Kubernetes Secret生产推荐# 1. 创建 Secretkubectl create secret generic db-credentials --from-literalusernamemyapp --from-literalpasswordMyStrongPssw0rd# 2. 在 Deployment 中引用密码自动注入 Pod 环境变量3.5 Druid 配置与监控spring:datasource:type:com.alibaba.druid.pool.DruidDataSourcedruid:username:${DB_USERNAME:app_user}password:${DB_PASSWORD:changeme_in_production}initial-size:5max-active:20max-wait:60000stat-view-servlet:enabled:trueurl-pattern:/druid/*login-username:${DRUID_ADMIN_USER:admin}# 生产用强密码 环境变量login-password:${DRUID_ADMIN_PASS:admin}# 生产用强密码 环境变量filter:stat:log-slow-sql:trueslow-sql-millis:2000merge-sql:true# 必须开启否则SQL统计失真wall:config:delete-whitelist:false# 禁止无where条件的delete生产经验merge-sql: true能将SELECT * FROM user WHERE id 1和id 2合并统计否则监控数据失真。强烈建议为 Druid 监控页配置独立的强密码不低于 16 位含大小写字母、数字、特殊符号并通过环境变量注入。3.6 JDK 21 Record JdbcTemplate// 定义不可变数据载体publicrecordUser(Longid,Stringname,Integerage){}// JdbcTemplate 查询RepositorypublicclassUserDao{privatefinalJdbcTemplatejdbcTemplate;publicListUserfindAll(){StringsqlSELECT id, name, age FROM user;returnjdbcTemplate.query(sql,(rs,rowNum)-newUser(rs.getLong(id),rs.getString(name),rs.getInt(age)));}}3.7 动态治理Apollo 动态刷新 HikariCP生产环境使用配置中心动态调整连接池避免重启ComponentRefreshScopepublicclassDynamicDataSourceConfig{Value(${db.pool.max-size:20})privateintmaxPoolSize;BeanRefreshScopepublicHikariConfighikariConfig(){HikariConfigconfignewHikariConfig();config.setMaximumPoolSize(maxPoolSize);// 其他配置...returnconfig;}}原理Nacos/Apollo 配置变更时RefreshScope触发 Bean 重新创建新连接立即生效。3.8 R2DBC 响应式示例适用场景网关、实时流处理、超高并发 I/O 密集型RepositorypublicclassReactiveUserDao{privatefinalDatabaseClientclient;publicFluxUserfindAll(){returnclient.sql(SELECT id, name, age FROM user).map((row,meta)-newUser(row.get(id,Long.class),row.get(name,String.class),row.get(age,Integer.class))).all();}}四、避坑要点问题错误/误区后果正确做法SQL 注入使用Statement拼接用户输入数据泄露、删库使用PreparedStatement参数化查询连接泄漏未关闭Connection/Statement/ResultSet连接耗尽服务不可用try-with-resources自动关闭线程安全多线程共享Connection数据错乱每个线程从连接池获取独立连接max-lifetime过大超过 MySQLwait_timeout连接被 MySQL 断开池中不知设为 5~30 分钟小于 MySQLwait_timeoutmaximum-pool-size过大数据库连接数耗尽数据库拒绝连接根据压测调整一般 10~30Druidmerge-sql未开启同 SQL 不同参数被分别统计SQL 监控数据失真merge-sql: trueDriverManager vs DataSource使用DriverManager.getConnection()无法利用连接池使用DataSource 连接池监控盲区未配置监控指标生产问题无法定位配置 Druid 或接入 OpenTelemetry连接池过度追求“动态调整”频繁动态修改连接池大小触发连接重创性能抖动配置中心联动时设置合理的变更阈值如变化超过20%才触发避免频繁调整五、面试高频考点Q1Statement、PreparedStatement、CallableStatement的区别Statement执行静态 SQL有注入风险PreparedStatement支持预编译和参数化查询防注入且可复用执行计划CallableStatement用于调用存储过程。Q2PreparedStatement为什么能防止 SQL 注入SQL 模板先发送给数据库预编译参数值通过占位符独立传递仅作为数据处理不被解析为 SQL 逻辑从根本上杜绝注入。Q3HikariCP 和 Druid 如何选型HikariCP极致性能、轻量级适合高并发场景Druid功能全面内置 Web 监控、SQL 防火墙、慢查询分析适合需要深度监控的中大型企业。Q4连接池的核心参数有哪些maximum-pool-size最大连接数、connection-timeout获取连接超时、max-lifetime连接最大存活时间必须小于 MySQLwait_timeout、idle-timeout空闲连接回收时间。Q5DriverManager和DataSource的区别DriverManager每次创建新连接无法利用连接池DataSource是 JDBC 2.0 引入的标准接口支持连接池、分布式事务等生产环境推荐使用。Q6 R2DBC 和 JDBC 连接池的设计理念有何不同JDBC 连接池基于“线程安全”和“复用昂贵连接”一个连接同时只能被一个线程使用而 R2DBC 连接池基于“非阻塞”和“多路复用”一个连接可以处理多个并发请求适合 I/O 密集型场景。Q7 2026 年推荐用 R2DBC 还是虚拟线程 传统 JDBC优先推荐虚拟线程 传统 JDBC代码无需改造JDK 21 原生支持已能满足绝大多数场景。如果业务是网关或超高并发 I/O 密集型再考虑 R2DBC。Q8 如何实现数据库连接的平滑下线无损上线结合 HikariCP 的配置和数据库代理如 Pgbouncer。在应用重启时先将连接池中的连接标记为“不可用”等待当前事务提交后再断开连接避免connection reset异常。也可以通过配置中心动态调小maximum-pool-size让连接自然回收。Q9 如何防止应用把数据库连接打满三层防护① 应用层设置合理的maximum-pool-size② 数据库层设置max_connections并配置用户级限制③ 中间件层如 Sentinel对数据库 RT 进行熔断降级。六、练习题代码题使用PreparedStatement实现用户登录校验要求防 SQL 注入并对比Statement字符串拼接的漏洞。 思路分别用Statement和PreparedStatement实现登录输入admin --测试观察Statement如何被绕过。配置题基于 Spring Boot 配置 HikariCP要求最大连接数 30、连接超时 3 秒、连接最大存活 10 分钟并开启泄漏检测。 思路配置maximum-pool-size: 30、connection-timeout: 3000、max-lifetime: 600000、leak-detection-threshold: 5000。分析题某生产系统频繁出现Connection is not available错误连接池配置maximum-pool-size10并发请求 50。如何排查 思路① 检查是否有连接泄漏未正常关闭② 增大maximum-pool-size并压测③ 检查数据库max_connections是否足够④ 检查慢 SQL 是否占用连接过长⑤ 开启leak-detection-threshold定位泄漏代码。 你的学习进度当前第88篇 / 共108篇 ·进阶篇数据库与持久层框架第83~90篇✅ 已完成基础篇44篇 第91~ 96篇Redis/MQ 第83~88篇 正在学第88篇⏳ 待学习第89~ 90篇 第97~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 本专栏每篇都包含避坑表 面试高频考点 练习题。每天30分钟100天拿offer 下一篇文章预告《第89篇MySQL面试压轴题2026版》内容简介汇总 30 道 MySQL 大厂面试真题索引优化、事务隔离级别、MVCC、锁机制、SQL 调优、分库分表、读写分离等附标准话术 加分回答 实战经验助你轻松应对 MySQL 面试。 学完这篇你将能应对 90% 的 MySQL 面试题从“会用”到“精通”。福利提醒评论区留言“JDBC”可领取《JDBC 与连接池核心参数速查表》PDF。《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送