SpringJDBC源码初探-JdbcTemplate类
一、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 {@Nullableprivate DataSource dataSource;@Nullableprivate 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@Nullablepublic <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)
@Nullableprivate <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方法
@Nullablepublic <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@Nullablepublic 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等)
- 返回结果