SpringJDBC源码初探-JdbcTemplate类

一、JdbcTemplate类介绍

JdbcTemplate 是 Spring JDBC 模块的核心类,它通过模板方法模式封装了传统 JDBC 的复杂操作。主要解决了以下痛点:

资源管理自动化 :自动处理连接、语句和结果集的获取与释放
异常统一处理 :将受检的 SQLException 转换为 Spring 的运行时异常体系
事务集成:无缝整合 Spring 声明式事务管理

二、 JdbcTemplate核心属性

首先从JdbcTemplate类定义可以看到

java 复制代码
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations{
...
}

JdbcTemplate继承了JdbcAccessor基类并实现了JdbcOperation接口

JdbcAccessor

java 复制代码
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 操作抽象

java 复制代码
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;
...
}

另外还有一些,配置相关属性

java 复制代码
private boolean ignoreWarnings = true;  // 是否忽略SQL警告
private int fetchSize = -1;  // 结果集获取大小
private int maxRows = -1;  // 最大行数限制
private int queryTimeout = -1;  // 查询超时时间(秒)

三、核心方法解析

1.执行方法

1).连接级执行 (ConnectionCallback)

java 复制代码
@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());
		}
	}
  1. 预处理语句执行 (PreparedStatementCallback)
java 复制代码
@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方法

java 复制代码
@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);
	}

另外还有:

java 复制代码
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 用于处理整个ResultSet
  • RowMapper 用于将单行记录映射为对象
  • queryForObject 专用于查询单行记录
  • queryForList() 返回一个List,其中每个元素是一个Map,表示一行记录

3.数据修改操作 (update)

java 复制代码
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语句,返回受影响的行数,另外还有:

java 复制代码
public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)// 第二个重载方法可以处理需要获取生成主键的情况
public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss)// batchUpdate主要用来执行批量更新操作

4.存储过程调用

java 复制代码
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的核心工作流程

  1. 从DataSource获取连接
  2. 创建Statement/PreparedStatement/CallableStatement
  3. 设置参数(如果有)
  4. 执行SQL
  5. 处理结果集(如果是查询)
  6. 处理异常(转换为Spring的DataAccessException)
  7. 清理资源(关闭ResultSet、Statement等)
  8. 返回结果
相关推荐
A.说学逗唱的Coke1 天前
【观察者模式】深入 Spring 事件驱动模型:从入门到微服务整合实战
spring·观察者模式·微服务
lzjava20241 天前
Spring AI使用知识库增强对话功能
人工智能·python·spring
ToPossessLight09021 天前
Spring 容器的基本实现
spring
程序定小飞1 天前
基于springboot的学院班级回忆录的设计与实现
java·vue.js·spring boot·后端·spring
郝开1 天前
Spring Boot 2.7.18(最终 2.x 系列版本)1 - 技术选型:连接池技术选型对比;接口文档技术选型对比
java·spring boot·spring
知兀1 天前
【Spring/SpringBoot】SSM(Spring+Spring MVC+Mybatis)方案、各部分职责、与Springboot关系
java·spring boot·spring
伊布拉西莫1 天前
Spring 6.x HTTP interface 使用说明
spring·restclient
YDS8291 天前
苍穹外卖 —— Spring Cache和购物车功能开发
java·spring boot·后端·spring·mybatis
Elieal1 天前
Spring 框架核心技术全解析
java·spring·sqlserver
组合缺一1 天前
(对标 Spring)OpenSolon v3.7.0, v3.6.4, v3.5.8, v3.4.8 发布(支持 LTS)
java·后端·spring·web·solon