2. Spring JDBC与数据源基础

在Spring企业数据访问体系中,JDBC并没有被“淘汰”,而是被重新组织:Spring把连接获取、异常转换、资源释放这些重复且容易出错的环节封装起来,让开发者把注意力放在SQL与结果映射上。理解这条链路的起点,是DataSource接口。

一、DataSource:连接的抽象工厂

javax.sql.DataSource是JDBC规范定义的接口,它替代了早期直接使用DriverManager.getConnection()的方式。核心方法只有两个:getConnection()与getConnection(String user, String password)。它的价值在于:调用方只依赖接口,不关心底层是单连接、连接池还是分布式数据源。

从实现形态看,DataSource可分为三类:

  • 基础实现:如DriverManagerDataSource,每次调用都新建物理连接,仅适合测试或极低频场景。
  • 连接池实现:如HikariCP、Druid、DBCP2、Tomcat JDBC Pool,复用物理连接,是生产环境的标准选择。
  • 路由/代理实现:如AbstractRoutingDataSource,用于多数据源动态切换。

连接池的本质是“用空间换时间”:预先建立若干物理连接放入池中,应用借出、归还,避免每次TCP握手与数据库认证的开销。选型时通常关注以下参数:

参数含义典型取值
maximumPoolSize池中最大连接数CPU核数×2~4,或按DB上限反推
minimumIdle最小空闲连接数与maximumPoolSize接近,减少抖动
connectionTimeout获取连接的最长等待3000ms
idleTimeout空闲连接回收时间600000ms
maxLifetime连接最大存活时间小于DB的wait_timeout

HikariCP以轻量、低延迟著称,是Spring Boot 2.x以来的默认连接池;Druid则内置监控统计、SQL防火墙与WallFilter,适合需要可视化监控与审计的场景。二者并非互斥,选型取决于是否需要Druid的监控生态。

二、JdbcTemplate核心API

JdbcTemplate是Spring JDBC的核心类,它持有DataSource,负责模板化流程:获取连接→创建Statement→执行→处理结果→释放资源→转换异常。常用API如下:

// 查询单值
Integer count = jdbcTemplate.queryForObject(
    "select count(*) from t_user where status = ?", Integer.class, 1);

// 查询对象列表
List<User> users = jdbcTemplate.query(
    "select id, name, email from t_user where age > ?",
    (rs, rowNum) -> new User(rs.getLong("id"),
                            rs.getString("name"),
                            rs.getString("email")),
    18);

// 更新
int affected = jdbcTemplate.update(
    "update t_user set email = ? where id = ?", "a@x.com", 1001L);

其中RowMapper负责把ResultSet的一行映射为对象,queryForObject在结果多于一行时会抛出IncorrectResultSizeDataAccessException,这是常见的排错点。批量操作使用batchUpdate,配合BatchPreparedStatementSetter可显著降低网络往返。

三、Spring Boot自动配置与多数据源初步认知

Spring Boot通过DataSourceAutoConfiguration自动装配DataSource。当类路径存在HikariCP时,默认创建HikariDataSource;若配置了spring.datasource.url等属性,则直接绑定。典型配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useSSL=false
    username: app
    password: secret
    hikari:
      maximum-pool-size: 20
      connection-timeout: 3000

当需要访问多个库时,自动配置不再适用,需要手动定义多个DataSource Bean,并用@Primary标记主数据源,再分别创建对应的JdbcTemplate。此时应关闭自动配置的单一数据源推断,避免Bean冲突。

Spring JDBC 数据访问链路与数据源选型 业务代码 Service / DAO JdbcTemplate RowMapper / update / query DataSource 接口 getConnection() HikariCP(默认) 轻量 · 低延迟 · 高吞吐 maximumPoolSize / maxLifetime Spring Boot 2.x+ 首选 Druid 监控统计 · SQL防火墙 WallFilter / StatFilter 适合审计与可视化场景 MySQL / PostgreSQL 物理连接 · TCP · 认证 多数据源 @Primary 主库 @Qualifier 从库 AbstractRoutingDataSource

上图展示了从业务代码到数据库的完整链路:业务代码依赖JdbcTemplate,JdbcTemplate依赖DataSource接口,接口背后由HikariCP或Druid提供连接池能力,最终落到物理数据库。右侧虚线框提示了多数据源场景下的扩展方向。

实践建议:在Spring Boot中优先使用HikariCP默认配置,仅当需要SQL监控、慢查询统计或防火墙时才引入Druid;多数据源场景下务必为每个DataSource显式命名并指定JdbcTemplate,避免自动配置覆盖。

掌握本节内容后,应能回答三个问题:DataSource为什么是接口而非具体类;HikariCP与Druid各自适合什么场景;JdbcTemplate的query、update、batchUpdate分别对应什么操作。这些是后续事务管理、MyBatis整合与多数据源路由的基础。