一、JdbcTemplate类介绍
JdbcTemplate 是 Spring JDBC 模块的核心类,它通过模板方法模式封装了传统 JDBC 的复杂操作。主要解决了以下痛点:
资源管理自动化:自动处理连接、语句和结果集的获取与释放
异常统一处理:将受检的 SQLException 转换为 Spring 的运行时异常体系
事务集成:无缝整合 Spring 声明式事务管理
二、 JdbcTemplate核心属性
首先从JdbcTemplate类定义可以看到
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations{
...
}
JdbcTemplate继承了JdbcAccessor基类并实现了JdbcOperation接口
JdbcAccessor
public abstract class JdbcAccessor implements InitializingBean {
@Nullable
private DataSource dataSource;
@Nullable
private volatile SQLExceptionTranslator exceptionTranslator;
private boolean lazyInit = true;
}
可以看到JdbcAccessor 类有两个主要属性dataSource和exceptionTranslator,另外还有一个懒加载标识lazyInit :
- DataSource 作为所有数据库操作的连接工厂,统一管理数据库连接
- SQLExceptionTranslator 异常转换器,将底层的 SQLException 转换为 Spring 统一的 DataAccessException 体系
- lazyInit 延迟初始化标志,控制 exceptionTranslator 的初始化时机:true(默认):首次需要异常转换时才初始化,false:在 JdbcTemplate 初始化时立即创建,避免无数据库操作时的资源浪费
JdbcOperations
JdbcOperations 是 Spring JDBC 模块的核心接口,定义了所有基础的 JDBC 操作抽象
public interface JdbcOperations {
<T> T execute(ConnectionCallback<T> action) throws DataAccessException;
<T> T execute(StatementCallback<T> action) throws DataAccessException;
void execute(String sql) throws DataAccessException;
<T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException;
void query(String sql, RowCallbackHandler rch) throws DataAccessException;
<T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException;
<T> Stream<T> queryForStream(String sql, RowMapper<T> rowMapper) throws DataAccessException;
<T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException;
int update(String sql) throws DataAccessException;
...
}
另外还有一些,配置相关属性
private boolean ignoreWarnings = true; // 是否忽略SQL警告
private int fetchSize = -1; // 结果集获取大小
private int maxRows = -1; // 最大行数限制
private int queryTimeout = -1; // 查询超时时间(秒)
三、核心方法解析
1.执行方法
1).连接级执行 (ConnectionCallback)
@Override
@Nullable
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
// 1. 获取连接
Connection con = DataSourceUtils.getConnection(obtainDataSource());
try {
// Create close-suppressing Connection proxy, also preparing returned Statements.
Connection conToUse = createConnectionProxy(con);
// 2. 执行用户回调逻辑
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
// 3. 异常转换(SQLException -> DataAccessException)
throw translateException("ConnectionCallback", sql, ex);
}
finally {
// 4. 安全释放连接
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
- 预处理语句执行 (PreparedStatementCallback)
@Nullable
private <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action, boolean closeResources)
throws DataAccessException {
Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
if (logger.isDebugEnabled()) {
String sql = getSql(psc);
logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
}
// 1. 获取连接
Connection con = DataSourceUtils.getConnection(obtainDataSource());
PreparedStatement ps = null;
try {
// 2. 创建语句
ps = psc.createPreparedStatement(con);
applyStatementSettings(ps);
T result = action.doInPreparedStatement(ps);
handleWarnings(ps);
// 3. 执行用户回调
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
// 异常转换
throw translateException("PreparedStatementCallback", sql, ex);
}
finally {
if (closeResources) {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
// 4. 关闭语句,释放连接
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
}
2.数据查询操作 query方法
@Nullable
public <T> T query(
PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {
Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared SQL query");
return execute(psc, new PreparedStatementCallback<T>() {
@Override
@Nullable
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
// 设置参数
if (pss != null) {
pss.setValues(ps);
}
// 执行查询
rs = ps.executeQuery();
// 处理结果集
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
}, true);
}
另外还有:
public <T> T query(String sql, ResultSetExtractor<T> rse, @Nullable Object... args)
public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args)
public List<Map<String, Object>> queryForList(String sql, @Nullable Object... args)
这些方法执行查询操作,其中:
ResultSetExtractor
用于处理整个ResultSetRowMapper
用于将单行记录映射为对象queryForObject
专用于查询单行记录queryForList()
返回一个List,其中每个元素是一个Map,表示一行记录
3.数据修改操作 (update)
protected int update(final PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss)
throws DataAccessException {
logger.debug("Executing prepared SQL update");
return updateCount(execute(psc, ps -> {
try {
if (pss != null) {
pss.setValues(ps);
}
int rows = ps.executeUpdate();
if (logger.isTraceEnabled()) {
logger.trace("SQL update affected " + rows + " rows");
}
return rows;
}
finally {
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}, true));
}
update操作主要执行INSERT、UPDATE、DELETE等DML语句,返回受影响的行数,另外还有:
public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)// 第二个重载方法可以处理需要获取生成主键的情况
public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss)// batchUpdate主要用来执行批量更新操作
4.存储过程调用
public Map<String, Object> call(CallableStatementCreator csc, List<SqlParameter> declaredParameters)
throws DataAccessException {
final List<SqlParameter> updateCountParameters = new ArrayList<>();
final List<SqlParameter> resultSetParameters = new ArrayList<>();
final List<SqlParameter> callParameters = new ArrayList<>();
for (SqlParameter parameter : declaredParameters) {
if (parameter.isResultsParameter()) {
if (parameter instanceof SqlReturnResultSet) {
resultSetParameters.add(parameter);
}
else {
updateCountParameters.add(parameter);
}
}
else {
callParameters.add(parameter);
}
}
Map<String, Object> result = execute(csc, cs -> {
boolean retVal = cs.execute();
int updateCount = cs.getUpdateCount();
if (logger.isTraceEnabled()) {
logger.trace("CallableStatement.execute() returned '" + retVal + "'");
logger.trace("CallableStatement.getUpdateCount() returned " + updateCount);
}
Map<String, Object> resultsMap = createResultsMap();
if (retVal || updateCount != -1) {
resultsMap.putAll(extractReturnedResults(cs, updateCountParameters, resultSetParameters, updateCount));
}
resultsMap.putAll(extractOutputParameters(cs, callParameters));
return resultsMap;
});
Assert.state(result != null, "No result map");
return result;
}
用于调用存储过程,支持输入输出参数。
5. 其中涉及到的一些类
PreparedStatementCreator
用于创建PreparedStatement,允许完全控制PreparedStatement的创建过程。PreparedStatementSetter
用于设置PreparedStatement的参数RowMapper
将ResultSet中的一行数据映射为一个对象ResultSetExtractor
处理整个ResultSet,通常用于复杂的结果集处理。
四、 JdbcTemplate的核心工作流程
- 从DataSource获取连接
- 创建Statement/PreparedStatement/CallableStatement
- 设置参数(如果有)
- 执行SQL
- 处理结果集(如果是查询)
- 处理异常(转换为Spring的DataAccessException)
- 清理资源(关闭ResultSet、Statement等)
- 返回结果