Spring 提供了強大的 JDBC 抽象層,消除樣板代碼,讓數據庫操作更簡潔、安全。本文系統講解三大核心模板類的遞進關係、基礎用法、DAO 支持類以及聲明式配置最佳實踐。
| 模板類 | 核心特性 | 適用場景 |
|---|---|---|
| JdbcTemplate | 最基礎模板,使用索引參數 ? 綁定值,封裝連接獲取、語句創建、資源釋放、異常轉換全流程。 | 簡單查詢、更新、批量操作,無需命名參數時。 |
| NamedParameterJdbcTemplate | 內部持有 JdbcTemplate,支持 命名參數 (如 :name),可讀性更強,支持 Map/List 批量綁定。 | SQL 參數較多、動態構建 SQL、需提高可維護性時。 |
| SimpleJdbcTemplate (已過時) | 利用 Java 5 泛型、可變參數、自動拆裝箱簡化 API,內部委託 NamedParameterJdbcTemplate。 | Spring 3.1+ 已標記 @Deprecated,建議直接使用 JdbcTemplate 或 NamedParameterJdbcTemplate 配合 Java 8+ 語法。 |
遞進關係:SimpleJdbcTemplate > NamedParameterJdbcTemplate > JdbcTemplate (功能遞增)。現代項目推薦直接使用前兩者。
// 1. 配置數據源 (示例使用 DriverManagerDataSource,生產環境請用連接池)
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/sampledb");
ds.setUsername("root");
ds.setPassword("");
// 2. 創建 JdbcTemplate 並注入數據源
JdbcTemplate jdbc = new JdbcTemplate();
jdbc.setDataSource(ds);
// 3. 執行 DDL / DML
String sql = "CREATE TABLE t_user1 (user_id INT PRIMARY KEY, user_name VARCHAR(60))";
jdbc.execute(sql); // 執行任意 SQL
// 4. 參數化查詢/更新
jdbc.update("INSERT INTO t_user1 VALUES (?, ?)", 1, "Alice");
List<Map<String, Object>> users = jdbc.queryForList("SELECT * FROM t_user1");
注意:DriverManagerDataSource 非真正連接池,每次獲取連接都新建物理連接,生產嚴禁使用,請改用 HikariCP、Druid、DBCP2 等。
若在每個 DAO 中重複 new JdbcTemplate()、setDataSource(),代碼極其冗餘。Spring 提供 JdbcDaoSupport 基類:
public class ForumDAO extends JdbcDaoSupport {
public void initDb() {
String sql = "CREATE TABLE t_user1 (user_id INT PRIMARY KEY, user_name VARCHAR(60))";
getJdbcTemplate().execute(sql); // 直接使用模板
}
}
只需在 Spring 容器中為 DAO 注入 dataSource 或 jdbcTemplate,基類自動完成初始化。
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1. 真正的連接池數據源 (以 DBCP2 為例) -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sampledb"/>
<property name="username" value="root"/>
<property name="password" value=""/>
<property name="initialSize" value="5"/>
<property name="maxTotal" value="20"/>
</bean>
<!-- 2. JdbcTemplate Bean -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 3. DAO Bean,繼承 JdbcDaoSupport 自動獲得模板 -->
<bean id="forumDAO" class="dao.jdbc.ForumDAO">
<property name="dataSource" ref="dataSource"/> <!-- 或注入 jdbcTemplate -->
</bean>
</beans>
@Configuration
@ComponentScan("dao.jdbc")
public class JdbcConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/sampledb");
ds.setUsername("root");
ds.setPassword("");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
// DAO 直接 @Autowired JdbcTemplate 或繼承 JdbcDaoSupport
}
update(sql, params...)、batchUpdate(sqlList)、execute(sql)queryForObject(sql, params, ReturnType)queryForObject(sql, params, RowMapper<T>)query(sql, params, RowMapper<T>)、queryForList(sql, params)ResultSetExtractor 或 PagingQueryProvider (Spring Data JDBC)。Spring 將 JDBC 的 SQLException 轉換為非受檢的 DataAccessException 體系,統一異常處理。配合 @Transactional 或 DataSourceTransactionManager 實現聲明式事務:
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
jdbc.update("UPDATE account SET balance = balance - ? WHERE id = ?", amount, fromId);
jdbc.update("UPDATE account SET balance = balance + ? WHERE id = ?", amount, toId);
}
NamedParameterJdbcTemplate:SQL 可讀性高,參數綁定靈活。