# Spring JDBC
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version> <!-- 5.1.17.RELEASE -->
</dependency>
Spring 为了提供对 Jdbc 的支持,在 Jdbc API 的基础上封装了一套实现,以此建立一个 JDBC 存取框架。
(作为 Spring JDBC 框架的核心)JDBC Template 的设计目的主要是两个:
简化 JDBC 的操作代码。
可以将事务的管理工作委托给 Spring,进一步简化代码。
JdbcTemplate 使用很简单:要求 Spring『帮』我们创建一个 JdbcTemplate 的单例对象,随后,我们在代码(DAO)中,注入这个 JdbcTemplate 单例对象,使用它即可。
需要注意的是,Spirng 创建 JdbcTemplte 单例对象时,需要传入 DataSource 单例对象。传入 DataSource 的目的在于,JdbcTempate 会自己从 DataSource 中取 Connection 对象进行数据库操作。从而不再需要我们从 Service 层中传入 Connection 对象。
# 配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- for Service -->
<context:component-scan base-package="xxx.yyy.zzz.service"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"/>
</bean>
<!-- for DAO -->
<context:component-scan base-package="xxx.yyy.zzz.dao" />
<bean id="ds" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/scott?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>
</beans>
# Dao
@Slf4j
@Repository
public class DepartmentDAO {
@Autowired
private JdbcTemplate template;
public void delete(int id) {
log.info("DAO: delete");
template.update("delete from department where id = ?", id);
if (id % 2 == 0)
throw new RuntimeException();
}
}
# Spring-JDBC API:增删改
JdbcTemplate 为 DAO 中的 增删改操作提供了 .update 方法。
String sql = "INSERT INTO exam_user VALUE(NULL, ?, ?)";
template.update(sql, username, password);
String sql = "DELETE FROM exam_user WHERE uid = ?";
template.update(sql, uid);
String sql = "UPDATE exam_user SET username = ?, password = ? WHERE uid = ?";
template.update(sql, newUsername, newPassword, uid);
# Spring-JDBC API:查询
String sql = "SELECT * FROM exam_user WHERE username = ?";
User user = template.queryForObject(
sql,
new BeanPropertyRowMapper<>(User.class),
username);
String sql = "select * from exam_user";
List<User> list = template.query(
sql,
new BeanPropertyRowMapper<>(User.class));
在 JavaBean 的属性名与数据库名一致的情况下,Spring Jdbc 提供了自带的一个 BeanPropertyRowMapper 类,用于将 ResultSet 中的数据库数据『映射/转换』成 JavaBean 。
JdbcTemplate#queryForObject 方法有一个『问题』,由于涉及到 ResultSet 到 JavaBean 的转换,JdbcTemplate#queryForObject 方法强制要求查询结果『应该』有数据。如果你的 SQL 语句在数据库中查不到任何数据(也许本来就没有这样的一条数据),那么 JdbcTemplate#queryForObject 方法会抛出异常:EmptyResultDataAccessException 。
当然,你也可以全部使用 JdbcTemplate#query 方法查询,得到一个 List 后,再通过 List 的 List#size 方法的返回结果来确定查没查到数据,并进行后续处理。
# 自定义映射结果集
对于数据库中的字段的名字与 JavaBean 的属性名『不一致』的情况,如果无法将其两者统一,那么在使用 .query 方法和 .queryForObject 方法时,就需要自己『定制』ResultSet 到 JavaBean 的转换规则,即,实现 RowMapper 接口。
template.query("...", (resultSet, n) -> {
User user = new User();
user.setUid(resultSet.getLong("uid"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
});
spring-jdbc 会循环遍历 ResultSet,对于每一轮循环它将调用你这里的第二个参数,并将 ResultSet 和当前的『轮数』传入进去。