Spring JDBC 是 Spring 框架提供的JDBC 抽象層,核心類別 JdbcTemplate 大幅簡化了資料庫操作樣板碼(連線獲取、Statement 建立、資源釋放、異常轉換)。本文示範從 XML 配置到註解驅動的完整整合流程,並涵蓋 CRUD、事務管理、RowMapper 等核心技術。
src/main/java
├── cn.com.bean
│ └── Person.java # 實體類
├── cn.com.service
│ └── PersonService.java # 服務介面
├── cn.com.service.impl
│ ├── PersonServiceImpl.java # 服務實作
│ └── PersonRowMapper.java # RowMapper 實作
└── cn.com.config
└── DataSourceConfig.java # Java Config 配置類 (替代 XML)
src/main/resources
├── application.properties # 資料庫連線參數
└── schema.sql # 建表腳本
package cn.com.bean;
public class Person {
private Integer id;
private String name;
public Person() {}
public Person(String name) { this.name = name; }
// Getter/Setter/toString 略...
}
package cn.com.service.impl; import cn.com.bean.Person; import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; public class PersonRowMapper implements RowMapper{ @Override public Person mapRow(ResultSet rs, int rowNum) throws SQLException { Person person = new Person(); person.setId(rs.getInt("id")); person.setName(rs.getString("name")); return person; } }
package cn.com.service;
import cn.com.bean.Person;
import java.util.List;
public interface PersonService {
void save(Person person);
void update(Person person);
Person getById(Integer id);
List getAll();
void deleteById(Integer id);
}
package cn.com.service.impl;
import cn.com.bean.Person;
import cn.com.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional // 類別層級事務,所有 public 方法自動加入事務
public class PersonServiceImpl implements PersonService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public PersonServiceImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void save(Person person) {
String sql = "INSERT INTO person (name) VALUES (?)";
jdbcTemplate.update(sql, person.getName());
}
@Override
public void update(Person person) {
String sql = "UPDATE person SET name = ? WHERE id = ?";
jdbcTemplate.update(sql, person.getName(), person.getId());
}
@Override
public Person getById(Integer id) {
String sql = "SELECT id, name FROM person WHERE id = ?";
try {
return jdbcTemplate.queryForObject(sql, new PersonRowMapper(), id);
} catch (EmptyResultDataAccessException e) {
return null; // 或拋出自定義異常
}
}
@Override
public List getAll() {
String sql = "SELECT id, name FROM person";
return jdbcTemplate.query(sql, new PersonRowMapper());
}
@Override
public void deleteById(Integer id) {
String sql = "DELETE FROM person WHERE id = ?";
jdbcTemplate.update(sql, id);
}
}
package cn.com.config;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@ComponentScan("cn.com")
@EnableTransactionManagement // 啟用 @Transactional 支援
@PropertySource("classpath:application.properties")
public class DataSourceConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driver}")
private String driver;
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setDriverClassName(driver);
// 連線池優化參數
ds.setMaximumPoolSize(20);
ds.setMinimumIdle(5);
ds.setConnectionTimeout(30000);
ds.setIdleTimeout(600000);
ds.setMaxLifetime(1800000);
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8 jdbc.username=root jdbc.password=123456
CREATE TABLE IF NOT EXISTS `person` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
| 方法 | 用途 | 回傳值 |
|---|---|---|
update(sql, args...) | INSERT/UPDATE/DELETE | int (影響行數) |
queryForObject(sql, RowMapper, args...) | 查詢單筆 | T (無結果拋 EmptyResultDataAccessException) |
query(sql, RowMapper, args...) | 查詢多筆 | List |
queryForMap(sql, args...) | 查詢單筆轉 Map | Map |
queryForList(sql, args...) | 查詢多筆轉 List | List |
batchUpdate(sql, List | 批次更新 | int[] |
Service 實作類 或 public 方法,避免加在介面(CGLIB 代理限制)@Transactional(rollbackFor = Exception.class) 涵蓋受檢異常REQUIRED (預設)、REQUIRES_NEW (獨立事務)、NESTED (巢狀保存點)READ_COMMITTED (大多數資料庫預設)、REPEATABLE_READ (MySQL 預設)@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class PersonServiceTest {
@Autowired
private PersonService personService;
@Test
public void testCrud() {
// Save
Person p = new Person("張三");
personService.save(p);
assertNotNull(p.getId()); // 自動回寫自增 ID 需配合 GeneratedKeyHolder
// Get
Person found = personService.getById(p.getId());
assertEquals("張三", found.getName());
// Update
found.setName("李四");
personService.update(found);
assertEquals("李四", personService.getById(p.getId()).getName());
// List
assertTrue(personService.getAll().size() >= 1);
// Delete
personService.deleteById(p.getId());
assertNull(personService.getById(p.getId()));
}
}
@Override
public void save(Person person) {
String sql = "INSERT INTO person (name) VALUES (?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, person.getName());
return ps;
}, keyHolder);
person.setId(keyHolder.getKey().intValue());
}
Spring JDBC + JdbcTemplate 是 Java 資料庫操作的黃金平衡點:比 MyBatis 輕量、比原生 JDBC 安全簡潔、比 JPA 更可控。掌握 RowMapper、PreparedStatementCreator、TransactionTemplate 等核心介面,即可應對 90% 的關聯式資料庫操作場景。