Spring的JdbcTemplate是Spring框架中用于简化JDBC操作的核心类之一。它通过封装底层的JDBC API,提供了统一、简洁的数据库操作方式,极大地减少了开发人员在数据库操作中需要处理的样板代码,如连接获取、关闭、异常处理等。下面将从源码进行详细解读。
java
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jdbc.core;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.BatchUpdateException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.InvalidResultSetAccessException;
import org.springframework.jdbc.SQLWarningException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.datasource.ConnectionProxy;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcAccessor;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.StringUtils;
/**
* <b>JdbcTemplate是Spring JDBC核心包中的委托类。</b>
* 它提供了一个简化的JDBC操作框架,支持执行各种类型的JDBC操作。
* 通过这个类,开发者可以避免直接操作JDBC API中的繁琐细节,减少常见错误的发生。对于更简单、更灵活的封装,建议使用{@link org.springframework.jdbc.core.simple.JdbcClient}(自6.1版本起)。
*
* <p>JdbcTemplate负责执行JDBC的核心流程:执行SQL语句、处理结果集、处理SQLException,并将其转化为Spring的通用数据访问异常。
* 该类允许应用程序代码专注于定义SQL和提取结果,JDBC的低级细节(如连接管理、事务处理、异常处理等)由JdbcTemplate自动处理。
*
* <p>开发者使用JdbcTemplate时,只需要提供回调接口(Callback),这些接口为执行具体JDBC操作定义了明确的契约:
* <ul>
* <li>{@link PreparedStatementCreator}用于创建SQL语句及其参数</li>
* <li>{@link ResultSetExtractor}用于从ResultSet中提取数据</li>
* <li>{@link RowMapper}用于将ResultSet的每一行映射到对象</li>
* </ul>
*
* <p>JdbcTemplate是线程安全的,一旦完成配置,可以在应用中重复使用。它可以直接实例化并提供给服务类,也可以作为Bean配置并通过依赖注入使用。需要注意的是,DataSource应始终在应用程序上下文中配置。
*
* <p>此外,JdbcTemplate支持通过自定义SQLExceptionTranslator来翻译数据库异常,避免手动处理SQL错误。
*
* <p>所有执行的SQL操作都在DEBUG日志级别记录,以"org.springframework.jdbc.core.JdbcTemplate"作为日志类别。
*
* <p><b>注意:从6.1版本开始,推荐使用{@link org.springframework.jdbc.core.simple.JdbcClient}作为JDBC访问的统一外观,它提供了更灵活、流畅的API风格。</b>
* {@code JdbcClient}通过委托给JdbcTemplate或NamedParameterJdbcTemplate来执行实际的JDBC操作。
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Thomas Risberg
* @author Yanming Zhou
* @since May 3, 2001
* @see JdbcOperations
* @see PreparedStatementCreator
* @see PreparedStatementSetter
* @see CallableStatementCreator
* @see PreparedStatementCallback
* @see CallableStatementCallback
* @see ResultSetExtractor
* @see RowCallbackHandler
* @see RowMapper
* @see org.springframework.jdbc.support.SQLExceptionTranslator
* @see org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
*/
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
// 结果集的返回前缀标识符
private static final String RETURN_RESULT_SET_PREFIX = "#result-set-";
// 更新计数的返回前缀标识符
private static final String RETURN_UPDATE_COUNT_PREFIX = "#update-count-";
/**
* 指定是否忽略JDBC语句中的SQL警告({@link SQLWarning})。
* 默认值为{@code true},即忽略并记录警告。如果设置为{@code false},JdbcTemplate将抛出SQL警告异常({@link SQLWarningException})。
*/
private boolean ignoreWarnings = true;
/**
* 设置JDBC查询语句的fetchSize。fetchSize决定一次性从数据库中拉取的记录数,特别适用于处理大规模结果集。
* 默认值为-1,表示使用JDBC驱动的默认值。如果设置为一个正值,可以通过调整此参数来优化性能。
*/
private int fetchSize = -1;
/**
* 设置JDBC查询语句的最大行数(maxRows)。用于限制查询结果的最大记录数,适用于分页或防止读取过多不需要的数据。
* 默认值为-1,表示使用JDBC驱动的默认值。
*/
private int maxRows = -1;
/**
* 设置JDBC查询语句的查询超时(单位:秒)。查询超时用于控制查询在执行时的最长时间。
* 默认值为-1,表示使用JDBC驱动的默认超时设置。
*/
private int queryTimeout = -1;
/**
* 指定是否跳过CallableStatement处理中的结果检查。某些旧版Oracle JDBC驱动(如10.1.0.2)存在处理存储过程返回结果的缺陷,
* 该选项可以帮助规避此问题。
*/
private boolean skipResultsProcessing = false;
/**
* 如果该值为true,则跳过那些没有相应{@link SqlOutParameter}声明的存储过程返回结果。适用于处理存储过程时,忽略没有定义输出参数的返回结果。
*/
private boolean skipUndeclaredResults = false;
/**
* 如果该值为true,则返回的结果Map会忽略大小写。这对于一些对大小写不敏感的数据库(如Oracle)特别有用。
*/
private boolean resultsMapCaseInsensitive = false;
/**
* 默认构造函数,用于通过Bean配置JdbcTemplate。
* <p>注意:在使用之前,必须调用{@link #setDataSource}来设置DataSource。
*/
public JdbcTemplate() {
}
/**
* 构造一个新的JdbcTemplate实例,并通过DataSource获取数据库连接。
* <p>初始化JdbcTemplate时不会立即触发异常翻译器的初始化。需要手动调用{@link #afterPropertiesSet()}来进行初始化。
*
* @param dataSource 用于获取数据库连接的DataSource
*/
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
/**
* 构造一个新的JdbcTemplate实例,并通过DataSource获取数据库连接。
* <p>根据"lazyInit"标志的设置,是否延迟初始化SQLExceptionTranslator。
*
* @param dataSource 用于获取数据库连接的DataSource
* @param lazyInit 是否延迟初始化SQLExceptionTranslator
*/
public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
setDataSource(dataSource);
setLazyInit(lazyInit);
afterPropertiesSet();
}
/**
* 复制构造函数,根据现有的JdbcAccessor实例创建一个新的JdbcTemplate。
* <p>此构造函数可以用于派生自其他JdbcTemplate实例的子类。
*
* @param original 复制自的原始JdbcTemplate实例
* @since 7.0
*/
public JdbcTemplate(JdbcAccessor original) {
setDataSource(original.getDataSource());
setExceptionTranslator(original.getExceptionTranslator());
setLazyInit(original.isLazyInit());
if (original instanceof JdbcTemplate originalTemplate) {
setIgnoreWarnings(originalTemplate.isIgnoreWarnings());
setFetchSize(originalTemplate.getFetchSize());
setMaxRows(originalTemplate.getMaxRows());
setQueryTimeout(originalTemplate.getQueryTimeout());
setSkipResultsProcessing(originalTemplate.isSkipResultsProcessing());
setSkipUndeclaredResults(originalTemplate.isSkipUndeclaredResults());
setResultsMapCaseInsensitive(originalTemplate.isResultsMapCaseInsensitive());
}
}
/**
* 设置是否忽略SQL警告({@link SQLWarning})。如果为false,JdbcTemplate会抛出SQL警告异常。
*
* @param ignoreWarnings 是否忽略SQL警告
* @see Statement#getWarnings()
* @see java.sql.SQLWarning
* @see org.springframework.jdbc.SQLWarningException
* @see #handleWarnings(Statement)
*/
public void setIgnoreWarnings(boolean ignoreWarnings) {
this.ignoreWarnings = ignoreWarnings;
}
/**
* 返回是否忽略SQL警告。
*
* @return true表示忽略警告,false表示不忽略警告
*/
public boolean isIgnoreWarnings() {
return this.ignoreWarnings;
}
/**
* 设置JdbcTemplate查询语句的fetchSize。该值控制一次从数据库中检索的记录数量,适用于大数据量的查询。
*
* @param fetchSize 设置的fetchSize值
* @see java.sql.Statement#setFetchSize
*/
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
}
/**
* 获取当前设置的fetchSize。
*
* @return 当前的fetchSize值
*/
public int getFetchSize() {
return this.fetchSize;
}
/**
* 设置JdbcTemplate查询语句的最大行数。对于大数据集的查询,使用此参数可以限制返回的记录数量。
*
* @param maxRows 设置的最大行数
* @see java.sql.Statement#setMaxRows
*/
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
/**
* 返回当前JdbcTemplate实例的最大行数设置。
* <p>此设置限制查询结果中最多返回的行数,适用于大数据集或分页查询。若未显式设置,则默认为-1。
*
* @return 当前最大行数
*/
public int getMaxRows() {
return this.maxRows;
}
/**
* 设置JDBC查询的超时时间(单位:秒)。
* <p>此设置指定在执行SQL查询时,最多等待多少秒。若设置为-1,则使用JDBC驱动程序的默认超时值。
* <p>注意:如果在事务级别设置了超时值,JdbcTemplate的超时设置将被覆盖。
*
* @param queryTimeout 超时时间(秒)
* @see java.sql.Statement#setQueryTimeout
*/
public void setQueryTimeout(int queryTimeout) {
this.queryTimeout = queryTimeout;
}
/**
* 返回当前为JdbcTemplate设置的查询超时时间(单位:秒)。
*
* @return 当前查询超时时间(秒)
*/
public int getQueryTimeout() {
return this.queryTimeout;
}
/**
* 设置是否跳过查询结果处理。
* <p>此设置适用于CallableStatement,若确定不需要返回任何结果时,跳过结果处理可以优化执行性能。
* <p>一些旧版Oracle JDBC驱动(如10.1.0.2)存在处理存储过程返回结果的问题,此设置可以帮助避免这些问题。
*
* @param skipResultsProcessing 是否跳过结果处理
*/
public void setSkipResultsProcessing(boolean skipResultsProcessing) {
this.skipResultsProcessing = skipResultsProcessing;
}
/**
* 返回是否跳过查询结果处理。
*
* @return true表示跳过结果处理,false表示不跳过
*/
public boolean isSkipResultsProcessing() {
return this.skipResultsProcessing;
}
/**
* 设置是否跳过未声明的存储过程结果。
* <p>该设置用于在调用存储过程时忽略那些未显式声明的结果。适用于处理没有定义输出参数的存储过程。
*
* @param skipUndeclaredResults 是否跳过未声明的存储过程结果
*/
public void setSkipUndeclaredResults(boolean skipUndeclaredResults) {
this.skipUndeclaredResults = skipUndeclaredResults;
}
/**
* 返回是否跳过未声明的存储过程结果。
*
* @return true表示跳过未声明的存储过程结果,false表示不跳过
*/
public boolean isSkipUndeclaredResults() {
return this.skipUndeclaredResults;
}
/**
* 设置是否对CallableStatement执行结果使用大小写不敏感的Map。
* <p>此设置适用于处理那些对参数名大小写不敏感的数据库(如Oracle),在这种情况下,结果Map中的参数名将不区分大小写。
*
* @param resultsMapCaseInsensitive 是否使用大小写不敏感的Map
*/
public void setResultsMapCaseInsensitive(boolean resultsMapCaseInsensitive) {
this.resultsMapCaseInsensitive = resultsMapCaseInsensitive;
}
/**
* 返回是否对CallableStatement执行结果使用大小写不敏感的Map。
*
* @return true表示使用大小写不敏感的Map,false表示不使用
*/
public boolean isResultsMapCaseInsensitive() {
return this.resultsMapCaseInsensitive;
}
//-------------------------------------------------------------------------
// Methods dealing with a plain java.sql.Connection
//-------------------------------------------------------------------------
/**
* 执行一个使用Connection回调的操作。
* <p>通过此方法,可以传递一个{@link ConnectionCallback},在获得连接后执行指定操作。
* 该方法会自动处理JDBC连接和异常的管理。
*
* @param action 需要执行的回调操作
* @param <T> 回调操作的返回类型
* @return 回调执行的结果
* @throws DataAccessException 数据访问异常
*/
@Override
public <T extends @Nullable Object> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
// 获取数据库连接并创建代理连接
Connection con = DataSourceUtils.getConnection(obtainDataSource());
try {
// 创建一个Connection代理,处理连接关闭
Connection conToUse = createConnectionProxy(con);
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
// 提前释放连接,避免可能的连接池死锁
String sql = getSql(action);
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("ConnectionCallback", sql, ex);
}
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
/**
* 创建一个连接的代理对象,代理对象会自动处理连接关闭操作。
* <p>该代理还会应用对Statement的设置(如fetchSize、maxRows、queryTimeout等)。
*
* @param con 需要代理的JDBC连接
* @return 代理的Connection对象
* @see java.sql.Connection#close()
* @see #execute(ConnectionCallback)
* @see #applyStatementSettings
*/
protected Connection createConnectionProxy(Connection con) {
return (Connection) Proxy.newProxyInstance(
ConnectionProxy.class.getClassLoader(),
new Class<?>[] {ConnectionProxy.class},
new CloseSuppressingInvocationHandler(con));
}
//-------------------------------------------------------------------------
// Methods dealing with static SQL (java.sql.Statement)
//-------------------------------------------------------------------------
/**
* 执行一个Statement回调操作。
* <p>通过此方法,可以传递一个{@link StatementCallback},并且自动应用语句设置(如fetchSize、maxRows等)。
* 此方法会处理连接和语句的创建、执行和异常管理。
*
* @param action 执行的回调操作
* @param closeResources 是否在操作后关闭资源
* @param <T> 回调操作的返回类型
* @return 回调执行的结果
* @throws DataAccessException 数据访问异常
*/
private <T extends @Nullable Object> T execute(StatementCallback<T> action, boolean closeResources) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
// 获取数据库连接
Connection con = DataSourceUtils.getConnection(obtainDataSource());
Statement stmt = null;
try {
// 创建Statement并应用设置
stmt = con.createStatement();
applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
handleWarnings(stmt);
return result;
}
catch (SQLException ex) {
// 提前释放连接和语句
if (stmt != null) {
handleWarnings(stmt, ex);
}
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("StatementCallback", sql, ex);
}
finally {
if (closeResources) {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
}
/**
* 执行一个简单的SQL语句(如INSERT、UPDATE等)。
* <p>该方法会使用Statement回调来执行SQL语句并进行异常处理。若SQL语句执行时抛出异常,将通过异常翻译进行处理。
*
* @param sql 要执行的SQL语句
* @throws DataAccessException 数据访问异常
*/
@Override
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1075
public void execute(String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
// 定义SQL执行的回调
class ExecuteStatementCallback implements StatementCallback<@Nullable Object>, SqlProvider {
@Override
public @Nullable Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback(), true);
}
/**
* 执行一个SQL查询,并通过ResultSetExtractor处理结果。
* <p>该方法会执行SQL查询并将查询结果通过传入的ResultSetExtractor转换为目标对象。
*
* @param sql SQL查询语句
* @param rse 结果集提取器
* @param <T> 结果类型
* @return 查询结果
* @throws DataAccessException 数据访问异常
*/
@Override
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1075
public <T extends @Nullable Object> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
// 定义SQL查询的回调
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
public @Nullable T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback(), true);
}
/**
* 执行SQL查询,并使用RowCallbackHandler逐行处理结果。
*
* @param sql 查询的SQL语句
* @param rch 结果处理器
* @throws DataAccessException 数据访问异常
*/
@Override
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1075
public void query(String sql, RowCallbackHandler rch) throws DataAccessException {
query(sql, new RowCallbackHandlerResultSetExtractor(rch, this.maxRows));
}
/**
* 执行SQL查询,并将结果映射到指定的对象列表中。
*
* @param sql 查询的SQL语句
* @param rowMapper 结果行映射器
* @param <T> 结果类型
* @return 查询结果的对象列表
* @throws DataAccessException 数据访问异常
*/
@Override
public <T extends @Nullable Object> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper, 0, this.maxRows)));
}
/**
* 执行SQL查询并返回一个流(Stream),用于处理大量数据时,可以按需加载每行数据。
* <p>该方法使用{@link StatementCallback}执行查询,并将结果集转化为Stream。这样可以实现按需加载结果,适合处理大规模查询结果。
*
* @param sql SQL查询语句
* @param rowMapper 结果映射器
* @param <T> 结果类型
* @return 结果流(Stream)
* @throws DataAccessException 数据访问异常
*/
@Override
public <T> Stream<T> queryForStream(String sql, RowMapper<T> rowMapper) throws DataAccessException {
class StreamStatementCallback implements StatementCallback<Stream<T>>, SqlProvider {
@Override
public Stream<T> doInStatement(Statement stmt) throws SQLException {
ResultSet rs = stmt.executeQuery(sql);
Connection con = stmt.getConnection();
return new ResultSetSpliterator<>(rs, rowMapper, JdbcTemplate.this.maxRows)
.stream().onClose(() -> {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
});
}
@Override
public String getSql() {
return sql;
}
}
return result(execute(new StreamStatementCallback(), false));
}
/**
* 使用查询SQL并将结果映射为一个Map(每列的列名为键,列值为值)。
*
* @param sql 查询SQL语句
* @return 返回查询结果的Map,键是列名,值是对应列的值
* @throws DataAccessException 数据访问异常
*/
@Override
public Map<String, @Nullable Object> queryForMap(String sql) throws DataAccessException {
return result(queryForObject(sql, getColumnMapRowMapper()));
}
/**
* 执行查询,返回一个对象结果。
* <p>该方法通过RowMapper将查询结果映射为单个对象。如果查询返回多个结果,方法会抛出异常。
*
* @param sql 查询SQL语句
* @param rowMapper 结果映射器
* @param <T> 结果类型
* @return 查询结果的对象
* @throws DataAccessException 数据访问异常
*/
@Override
public <T extends @Nullable Object> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
List<T> results = query(sql, rowMapper);
return DataAccessUtils.nullableSingleResult(results);
}
/**
* 执行查询,返回一个指定类型的对象。
* <p>该方法用于将查询结果映射为单一类型对象,如果查询返回多个结果,会抛出异常。
*
* @param sql 查询SQL语句
* @param requiredType 结果的类型
* @param <T> 结果类型
* @return 查询结果的对象
* @throws DataAccessException 数据访问异常
*/
@Override
public <T> @Nullable T queryForObject(String sql, Class<T> requiredType) throws DataAccessException {
return queryForObject(sql, getSingleColumnRowMapper(requiredType));
}
/**
* 执行查询,返回一个对象列表。
* <p>该方法将每行的结果映射为指定类型的对象,并返回一个包含所有映射对象的列表。
*
* @param sql 查询SQL语句
* @param elementType 列表中元素的类型
* @param <T> 元素类型
* @return 查询结果的对象列表
* @throws DataAccessException 数据访问异常
*/
@Override
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1075
public <T> List<@Nullable T> queryForList(String sql, Class<T> elementType) throws DataAccessException {
return query(sql, getSingleColumnRowMapper(elementType));
}
/**
* 执行查询,返回一个包含Map的列表,每个Map代表一行数据。
* <p>该方法使用列名作为键,将每一行的查询结果映射为一个Map。
*
* @param sql 查询SQL语句
* @return 查询结果的Map列表
* @throws DataAccessException 数据访问异常
*/
@Override
public List<Map<String, @Nullable Object>> queryForList(String sql) throws DataAccessException {
return query(sql, getColumnMapRowMapper());
}
/**
* 执行查询,并返回SqlRowSet。SqlRowSet类似于ResultSet,但它不依赖于数据库连接。
* <p>适用于需要批量处理结果的场景。
*
* @param sql 查询SQL语句
* @return 查询结果的SqlRowSet
* @throws DataAccessException 数据访问异常
*/
@Override
public SqlRowSet queryForRowSet(String sql) throws DataAccessException {
return result(query(sql, new SqlRowSetResultSetExtractor()));
}
/**
* 执行SQL更新语句,并返回受影响的行数。
* <p>该方法用于执行类似INSERT、UPDATE、DELETE等不返回结果集的SQL语句。
*
* @param sql 要执行的SQL更新语句
* @return 受影响的行数
* @throws DataAccessException 数据访问异常
*/
@Override
public int update(String sql) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL update [" + sql + "]");
}
// 回调执行更新语句
class UpdateStatementCallback implements StatementCallback<Integer>, SqlProvider {
@Override
public Integer doInStatement(Statement stmt) throws SQLException {
int rows = stmt.executeUpdate(sql);
if (logger.isTraceEnabled()) {
logger.trace("SQL update affected " + rows + " rows");
}
return rows;
}
@Override
public String getSql() {
return sql;
}
}
return updateCount(execute(new UpdateStatementCallback(), true));
}
/**
* 执行批量更新操作,返回每个更新语句的影响行数。
* <p>该方法适用于执行多个类似的更新语句(例如批量插入、批量更新等)。
*
* @param sql 需要执行的SQL更新语句数组
* @return 每个SQL语句影响的行数数组
* @throws DataAccessException 数据访问异常
*/
@Override
public int[] batchUpdate(String... sql) throws DataAccessException {
Assert.notEmpty(sql, "SQL array must not be empty");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL batch update of " + sql.length + " statements");
}
// 执行批量更新
class BatchUpdateStatementCallback implements StatementCallback<int[]>, SqlProvider {
private @Nullable String currSql;
@Override
public int[] doInStatement(Statement stmt) throws SQLException, DataAccessException {
int[] rowsAffected = new int[sql.length];
if (JdbcUtils.supportsBatchUpdates(stmt.getConnection())) {
for (String sqlStmt : sql) {
this.currSql = appendSql(this.currSql, sqlStmt);
stmt.addBatch(sqlStmt);
}
try {
rowsAffected = stmt.executeBatch();
}
catch (BatchUpdateException ex) {
String batchExceptionSql = null;
for (int i = 0; i < ex.getUpdateCounts().length; i++) {
if (ex.getUpdateCounts()[i] == Statement.EXECUTE_FAILED) {
batchExceptionSql = appendSql(batchExceptionSql, sql[i]);
}
}
if (StringUtils.hasLength(batchExceptionSql)) {
this.currSql = batchExceptionSql;
}
throw ex;
}
}
else {
for (int i = 0; i < sql.length; i++) {
this.currSql = sql[i];
if (!stmt.execute(sql[i])) {
rowsAffected[i] = stmt.getUpdateCount();
}
else {
throw new InvalidDataAccessApiUsageException("Invalid batch SQL statement: " + sql[i]);
}
}
}
return rowsAffected;
}
private String appendSql(@Nullable String sql, String statement) {
return (StringUtils.hasLength(sql) ? sql + "; " + statement : statement);
}
@Override
public @Nullable String getSql() {
return this.currSql;
}
}
int[] result = execute(new BatchUpdateStatementCallback(), true);
Assert.state(result != null, "No update counts");
return result;
}
/**
* 执行SQL查询,返回通过指定参数设置器(PreparedStatementSetter)绑定参数的结果。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param argTypes 参数类型数组
* @param rse 结果集提取器
* @param <T> 查询结果类型
* @return 查询结果
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> T query(String sql, @Nullable Object @Nullable [] args, int[] argTypes, ResultSetExtractor<T> rse) throws DataAccessException {
return query(sql, newArgTypePreparedStatementSetter(args, argTypes), rse);
}
/**
* 执行SQL查询,已弃用。使用新的参数绑定机制 `query(String, Object[], int[], ResultSetExtractor)` 替代。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param rse 结果集提取器
* @param <T> 查询结果类型
* @return 查询结果
* @throws DataAccessException 如果查询失败
*/
@Deprecated(since = "5.3")
@Override
public <T extends @Nullable Object> T query(String sql, @Nullable Object @Nullable [] args, ResultSetExtractor<T> rse) throws DataAccessException {
return query(sql, newArgPreparedStatementSetter(args), rse);
}
/**
* 执行SQL查询,返回结果,并使用提供的结果集提取器将结果映射为指定的类型。
* @param sql SQL查询语句
* @param rse 结果集提取器
* @param args 查询参数数组
* @param <T> 查询结果类型
* @return 查询结果
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> T query(String sql, ResultSetExtractor<T> rse, @Nullable Object @Nullable ... args) throws DataAccessException {
return query(sql, newArgPreparedStatementSetter(args), rse);
}
/**
* 执行一个查询,使用提供的 `PreparedStatementCreator` 和 `RowCallbackHandler` 处理结果。
* @param psc 生成 `PreparedStatement` 的回调函数
* @param rch 处理每行结果的回调函数
* @throws DataAccessException 如果查询失败
*/
@Override
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1075
public void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException {
query(psc, new RowCallbackHandlerResultSetExtractor(rch, this.maxRows));
}
/**
* 执行查询并处理每行数据,使用 `PreparedStatementSetter` 设置查询参数。
* @param sql SQL查询语句
* @param pss 查询参数设置器
* @param rch 处理每行结果的回调函数
* @throws DataAccessException 如果查询失败
*/
@Override
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/1075
public void query(String sql, @Nullable PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {
query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch, this.maxRows));
}
/**
* 执行查询,处理每行数据,并使用提供的参数类型设置器。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param argTypes 参数类型数组
* @param rch 处理每行结果的回调函数
* @throws DataAccessException 如果查询失败
*/
@Override
public void query(String sql, @Nullable Object @Nullable [] args, int[] argTypes, RowCallbackHandler rch) throws DataAccessException {
query(sql, newArgTypePreparedStatementSetter(args, argTypes), rch);
}
/**
* 已弃用的方法,使用新的参数绑定机制。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param rch 处理每行结果的回调函数
* @throws DataAccessException 如果查询失败
*/
@Deprecated(since = "5.3")
@Override
public void query(String sql, @Nullable Object @Nullable [] args, RowCallbackHandler rch) throws DataAccessException {
query(sql, newArgPreparedStatementSetter(args), rch);
}
/**
* 执行查询并处理每行结果,使用提供的查询参数。
* @param sql SQL查询语句
* @param rch 处理每行结果的回调函数
* @param args 查询参数数组
* @throws DataAccessException 如果查询失败
*/
@Override
public void query(String sql, RowCallbackHandler rch, @Nullable Object @Nullable ... args) throws DataAccessException {
query(sql, newArgPreparedStatementSetter(args), rch);
}
/**
* 执行SQL查询,返回映射为对象列表的结果。使用 `RowMapper` 将每一行结果映射为对象。
* @param psc 生成 `PreparedStatement` 的回调函数
* @param rowMapper 结果集行映射器
* @param <T> 查询结果的类型
* @return 查询结果列表
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(psc, new RowMapperResultSetExtractor<>(rowMapper, 0, this.maxRows)));
}
/**
* 执行SQL查询并使用 `RowMapper` 映射每一行结果。
* @param sql SQL查询语句
* @param pss 查询参数设置器
* @param rowMapper 结果集行映射器
* @param <T> 查询结果的类型
* @return 查询结果列表
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> List<T> query(String sql, @Nullable PreparedStatementSetter pss, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, pss, new RowMapperResultSetExtractor<>(rowMapper, 0, this.maxRows)));
}
/**
* 使用给定的 SQL、参数及参数类型,执行查询并返回映射为对象列表的结果。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param argTypes 参数类型数组
* @param rowMapper 结果集行映射器
* @param <T> 查询结果类型
* @return 查询结果列表
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> List<T> query(String sql, @Nullable Object @Nullable [] args, int[] argTypes, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, args, argTypes, new RowMapperResultSetExtractor<>(rowMapper, 0, this.maxRows)));
}
/**
* 已弃用的方法,建议使用新的参数绑定机制。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param rowMapper 结果集行映射器
* @param <T> 查询结果类型
* @return 查询结果列表
* @throws DataAccessException 如果查询失败
*/
@Deprecated(since = "5.3")
@Override
public <T extends @Nullable Object> List<T> query(String sql, @Nullable Object @Nullable [] args, RowMapper<T> rowMapper) throws DataAccessException {
return result(query(sql, newArgPreparedStatementSetter(args), new RowMapperResultSetExtractor<>(rowMapper, 0, this.maxRows)));
}
/**
* 使用给定的 SQL 查询并返回映射为对象列表的结果。
* @param sql SQL查询语句
* @param rowMapper 结果集行映射器
* @param args 查询参数数组
* @param <T> 查询结果类型
* @return 查询结果列表
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object @Nullable ... args) throws DataAccessException {
return result(query(sql, newArgPreparedStatementSetter(args), new RowMapperResultSetExtractor<>(rowMapper, 0, this.maxRows)));
}
/**
* 执行查询并返回一个对象结果,使用 `RowMapper` 映射查询结果。
* @param sql SQL查询语句
* @param args 查询参数数组
* @param argTypes 参数类型数组
* @param rowMapper 结果集行映射器
* @param <T> 查询结果类型
* @return 查询结果对象
* @throws DataAccessException 如果查询失败
*/
@Override
public <T extends @Nullable Object> T queryForObject(String sql, @Nullable Object @Nullable [] args, int[] argTypes, RowMapper<T> rowMapper)
throws DataAccessException {
List<T> results = query(sql, args, argTypes, new RowMapperResultSetExtractor<>(rowMapper, 1));
return DataAccessUtils.nullableSingleResult(results);
}
// ----------------------------- 查询操作 -----------------------------
/**
* 该方法用于执行 SQL 查询,并将结果映射为单个对象。
* 该方法已被弃用,原因是它通过 `RowMapper` 返回结果,而新版本更推荐使用不同的查询模式。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param rowMapper 用于映射查询结果的 RowMapper
* @return 返回查询结果的单个对象,若结果为空或不符合条件,则返回 null
* @throws DataAccessException 数据访问异常
*/
@Deprecated(since = "5.3")
@Override
public <T extends @Nullable Object> T queryForObject(String sql, @Nullable Object @Nullable [] args, RowMapper<T> rowMapper) throws DataAccessException {
// 执行查询,使用 `RowMapperResultSetExtractor` 将结果映射为指定对象
List<T> results = query(sql, newArgPreparedStatementSetter(args), new RowMapperResultSetExtractor<>(rowMapper, 1));
// 返回结果的单个对象,若没有符合条件的结果则返回 null
return DataAccessUtils.nullableSingleResult(results);
}
/**
* 该方法用于执行 SQL 查询,并将结果映射为单个对象。
* 使用了变参 `args` 来传递查询参数。
* @param sql 执行的 SQL 查询语句
* @param rowMapper 用于映射查询结果的 RowMapper
* @param args 查询参数,可以传递多个参数
* @return 返回查询结果的单个对象,若结果为空或不符合条件,则返回 null
* @throws DataAccessException 数据访问异常
*/
@Override
public <T extends @Nullable Object> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object @Nullable ... args) throws DataAccessException {
// 执行查询,使用 `RowMapperResultSetExtractor` 将结果映射为指定对象
List<T> results = query(sql, newArgPreparedStatementSetter(args), new RowMapperResultSetExtractor<>(rowMapper, 1));
// 返回结果的单个对象,若没有符合条件的结果则返回 null
return DataAccessUtils.nullableSingleResult(results);
}
/**
* 该方法执行 SQL 查询并返回单个对象。支持通过 `argTypes` 指定参数类型。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param argTypes 参数类型的数组,必须与参数数量一致
* @param requiredType 结果对象的类型
* @return 返回查询结果的单个对象,若没有符合条件的结果则返回 null
* @throws DataAccessException 数据访问异常
*/
@Override
public <T> @Nullable T queryForObject(String sql, @Nullable Object @Nullable [] args, int[] argTypes, Class<T> requiredType) throws DataAccessException {
// 调用其他重载方法来执行查询,并传入 `RowMapper` 处理结果映射
return queryForObject(sql, args, argTypes, getSingleColumnRowMapper(requiredType));
}
/**
* 执行 SQL 查询,并将结果映射为单个对象。
* 该方法已被弃用,建议使用新的方法实现。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param requiredType 结果对象的类型
* @return 查询结果的单个对象,若结果为空或不符合条件则返回 null
* @throws DataAccessException 数据访问异常
*/
@Deprecated(since = "5.3")
@Override
public <T> @Nullable T queryForObject(String sql, @Nullable Object @Nullable [] args, Class<T> requiredType) throws DataAccessException {
// 调用带 RowMapper 的查询方法,使用 SingleColumnRowMapper 映射结果为指定类型
return queryForObject(sql, getSingleColumnRowMapper(requiredType), args);
}
/**
* 执行 SQL 查询,并将结果映射为单个对象。支持通过变参传递查询参数。
* @param sql 执行的 SQL 查询语句
* @param requiredType 结果对象的类型
* @param args 查询参数,可能为 null
* @return 查询结果的单个对象,若结果为空或不符合条件则返回 null
* @throws DataAccessException 数据访问异常
*/
@Override
public <T> @Nullable T queryForObject(String sql, Class<T> requiredType, @Nullable Object @Nullable ... args) throws DataAccessException {
// 调用带 RowMapper 的查询方法,使用 SingleColumnRowMapper 映射结果为指定类型
return queryForObject(sql, getSingleColumnRowMapper(requiredType), args);
}
/**
* 执行 SQL 查询,并返回一个 Map,Map 的键是列名,值是列的值。
* 支持通过 `argTypes` 数组指定查询参数类型。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param argTypes 参数类型的数组,必须与查询参数数量一致
* @return 返回包含列名和对应值的 Map
* @throws DataAccessException 数据访问异常
*/
@Override
public Map<String, @Nullable Object> queryForMap(String sql, @Nullable Object @Nullable [] args, int[] argTypes) throws DataAccessException {
// 调用查询方法并返回 Map 结果,使用 ColumnMapRowMapper 将查询结果转换为 Map
return result(queryForObject(sql, args, argTypes, getColumnMapRowMapper()));
}
/**
* 执行 SQL 查询,并返回一个 Map,Map 的键是列名,值是列的值。
* 该方法使用变参形式接收查询参数。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @return 返回包含列名和对应值的 Map
* @throws DataAccessException 数据访问异常
*/
@Override
public Map<String, @Nullable Object> queryForMap(String sql, @Nullable Object @Nullable ... args) throws DataAccessException {
// 调用查询方法并返回 Map 结果,使用 ColumnMapRowMapper 将查询结果转换为 Map
return result(queryForObject(sql, getColumnMapRowMapper(), args));
}
/**
* 执行 SQL 查询,并返回一个包含查询结果的列表,列表元素为指定类型的对象。
* 支持通过 `argTypes` 数组指定查询参数类型。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param argTypes 参数类型的数组,必须与查询参数数量一致
* @param elementType 结果列表中每个元素的类型
* @return 返回一个包含查询结果的列表,列表中的每个元素是指定类型的对象
* @throws DataAccessException 数据访问异常
*/
@Override
@SuppressWarnings("NullAway") // Suppress NullAway warnings, which are used for nullability checks
public <T> List<@Nullable T> queryForList(String sql, @Nullable Object @Nullable [] args, int[] argTypes, Class<T> elementType) throws DataAccessException {
// 执行查询并将结果映射为指定类型的列表,使用 SingleColumnRowMapper 处理查询结果
return query(sql, args, argTypes, getSingleColumnRowMapper(elementType));
}
/**
* 该方法已被弃用,使用新的查询方法。执行 SQL 查询并返回一个包含查询结果的列表。
* 结果列表的元素类型由 `elementType` 参数指定。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param elementType 结果列表中每个元素的类型
* @return 返回一个包含查询结果的列表,列表中的每个元素是指定类型的对象
* @throws DataAccessException 数据访问异常
*/
@Deprecated(since = "5.3")
@Override
@SuppressWarnings("NullAway")
public <T> List<@Nullable T> queryForList(String sql, @Nullable Object @Nullable [] args, Class<T> elementType) throws DataAccessException {
// 调用查询方法并将结果映射为指定类型的列表,使用 SingleColumnRowMapper 处理查询结果
return query(sql, newArgPreparedStatementSetter(args), getSingleColumnRowMapper(elementType));
}
/**
* 执行 SQL 查询,并返回一个包含查询结果的列表,列表元素为指定类型的对象。
* 该方法使用变参形式接收查询参数。
* @param sql 执行的 SQL 查询语句
* @param elementType 结果列表中每个元素的类型
* @param args 查询参数,可能为 null
* @return 返回一个包含查询结果的列表,列表中的每个元素是指定类型的对象
* @throws DataAccessException 数据访问异常
*/
@Override
@SuppressWarnings("NullAway") // Suppress NullAway warnings, which are used for nullability checks
public <T> List<@Nullable T> queryForList(String sql, Class<T> elementType, @Nullable Object @Nullable ... args) throws DataAccessException {
// 执行查询并将结果映射为指定类型的列表,使用 SingleColumnRowMapper 处理查询结果
return query(sql, newArgPreparedStatementSetter(args), getSingleColumnRowMapper(elementType));
}
/**
* 执行 SQL 查询,并返回一个包含查询结果的列表,每一项为列名-列值的映射(Map)。
* 支持通过 `argTypes` 数组指定查询参数类型。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param argTypes 参数类型的数组,必须与查询参数数量一致
* @return 返回一个包含查询结果的列表,每一项为 Map,包含列名和对应的列值
* @throws DataAccessException 数据访问异常
*/
@Override
public List<Map<String, @Nullable Object>> queryForList(String sql, @Nullable Object @Nullable [] args, int[] argTypes) throws DataAccessException {
// 执行查询并返回包含查询结果的 Map 列表,使用 ColumnMapRowMapper 处理查询结果
return query(sql, args, argTypes, getColumnMapRowMapper());
}
/**
* 执行 SQL 查询,并返回一个包含查询结果的列表,每一项为列名-列值的映射(Map)。
* 该方法使用变参形式接收查询参数。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @return 返回一个包含查询结果的列表,每一项为 Map,包含列名和对应的列值
* @throws DataAccessException 数据访问异常
*/
@Override
public List<Map<String, @Nullable Object>> queryForList(String sql, @Nullable Object @Nullable ... args) throws DataAccessException {
// 执行查询并返回包含查询结果的 Map 列表,使用 ColumnMapRowMapper 处理查询结果
return query(sql, newArgPreparedStatementSetter(args), getColumnMapRowMapper());
}
/**
* 执行 SQL 查询并返回一个 `SqlRowSet` 对象,表示查询结果集。
* 支持通过 `argTypes` 数组指定查询参数类型。
* @param sql 执行的 SQL 查询语句
* @param args 查询参数,可能为 null
* @param argTypes 参数类型的数组,必须与查询参数数量一致
* @return 返回查询结果的 `SqlRowSet`,可以进一步操作和遍历
* @throws DataAccessException 数据访问异常
*/
@Override
public SqlRowSet queryForRowSet(String sql, @Nullable Object @Nullable [] args, int[] argTypes) throws DataAccessException {
// 执行查询并返回一个 `SqlRowSet` 对象,结果集通过 `SqlRowSetResultSetExtractor` 提取
return result(query(sql, args, argTypes, new SqlRowSetResultSetExtractor()));
}
@Override
public SqlRowSet queryForRowSet(String sql, @Nullable Object @Nullable ... args) throws DataAccessException {
return result(query(sql, newArgPreparedStatementSetter(args), new SqlRowSetResultSetExtractor()));
}
/**
* 执行 SQL 更新操作,并根据是否提供 PreparedStatementSetter 来设置参数。
*
* @param psc PreparedStatementCreator,用于创建 PreparedStatement
* @param pss PreparedStatementSetter,用于设置 SQL 参数,可为空
* @return 更新操作影响的行数
* @throws DataAccessException 数据访问异常
*/
protected int update(PreparedStatementCreator psc, @Nullable PreparedStatementSetter pss)
throws DataAccessException {
// 打印调试日志,说明正在执行 SQL 更新操作
logger.debug("Executing prepared SQL update");
// 执行更新操作并返回更新的行数
return updateCount(execute(psc, ps -> {
try {
// 如果传入了 PreparedStatementSetter,则设置 SQL 参数
if (pss != null) {
pss.setValues(ps);
}
// 执行更新,返回受影响的行数
int rows = ps.executeUpdate();
// 如果日志级别为 TRACE,打印更新受影响的行数
if (logger.isTraceEnabled()) {
logger.trace("SQL update affected " + rows + " rows");
}
// 返回更新的行数
return rows;
}
finally {
// 如果 pss 是 ParameterDisposer 的实例,进行清理操作
if (pss instanceof ParameterDisposer parameterDisposer) {
parameterDisposer.cleanupParameters();
}
}
}, true));
}
/**
* 执行 SQL 更新操作,不需要指定 PreparedStatementSetter。
*
* @param psc PreparedStatementCreator,用于创建 PreparedStatement
* @return 更新操作影响的行数
* @throws DataAccessException 数据访问异常
*/
@Override
public int update(PreparedStatementCreator psc) throws DataAccessException {
// 无需指定 PreparedStatementSetter,调用上述重载方法
return update(psc, (PreparedStatementSetter) null);
}
/**
* 执行 SQL 更新操作,并返回生成的主键。
*
* @param psc PreparedStatementCreator,用于创建 PreparedStatement
* @param generatedKeyHolder 用于存储生成的主键
* @return 更新操作影响的行数
* @throws DataAccessException 数据访问异常
*/
@Override
public int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)
throws DataAccessException {
// 检查 generatedKeyHolder 是否为 null,如果为 null 则抛出异常
Assert.notNull(generatedKeyHolder, "KeyHolder must not be null");
logger.debug("Executing SQL update and returning generated keys");
// 执行更新操作并返回更新的行数
return updateCount(execute(psc, ps -> {
// 执行更新操作并返回受影响的行数
int rows = ps.executeUpdate();
// 清空 KeyHolder 中的键列表
generatedKeyHolder.getKeyList().clear();
// 将生成的键存储到 KeyHolder 中
storeGeneratedKeys(generatedKeyHolder, ps, 1);
// 如果日志级别为 TRACE,打印更新的行数和生成的键数量
if (logger.isTraceEnabled()) {
logger.trace("SQL update affected " + rows + " rows and returned " + generatedKeyHolder.getKeyList().size() + " keys");
}
// 返回受影响的行数
return rows;
}, true));
}
/**
* 使用 SQL 字符串和 PreparedStatementSetter 执行 SQL 更新操作。
*
* @param sql 要执行的 SQL 更新语句
* @param pss PreparedStatementSetter,用于设置 SQL 参数,可为空
* @return 更新操作影响的行数
* @throws DataAccessException 数据访问异常
*/
@Override
public int update(String sql, @Nullable PreparedStatementSetter pss) throws DataAccessException {
// 使用 SimplePreparedStatementCreator 创建 PreparedStatement,执行更新操作
return update(new SimplePreparedStatementCreator(sql), pss);
}
/**
* 使用 SQL 字符串、参数数组和参数类型数组执行 SQL 更新操作。
*
* @param sql 要执行的 SQL 更新语句
* @param args 参数数组
* @param argTypes 参数类型数组
* @return 更新操作影响的行数
* @throws DataAccessException 数据访问异常
*/
@Override
public int update(String sql, @Nullable Object @Nullable [] args, int[] argTypes) throws DataAccessException {
// 根据传入的参数创建 PreparedStatementSetter,然后执行更新操作
return update(sql, newArgTypePreparedStatementSetter(args, argTypes));
}
/**
* 使用 SQL 字符串和参数数组执行 SQL 更新操作。
*
* @param sql 要执行的 SQL 更新语句
* @param args 参数数组
* @return 更新操作影响的行数
* @throws DataAccessException 数据访问异常
*/
@Override
public int update(String sql, @Nullable Object @Nullable ... args) throws DataAccessException {
// 根据传入的参数创建 PreparedStatementSetter,然后执行更新操作
return update(sql, newArgPreparedStatementSetter(args));
}
/**
* 执行批量更新操作,并返回每个更新操作的影响结果。
*
* @param psc PreparedStatementCreator,用于创建 PreparedStatement
* @param pss BatchPreparedStatementSetter,用于批量设置 SQL 参数
* @param generatedKeyHolder 用于存储生成的主键
* @return 每个更新操作影响的行数数组
* @throws DataAccessException 数据访问异常
*/
@Override
public int[] batchUpdate(PreparedStatementCreator psc, BatchPreparedStatementSetter pss,
KeyHolder generatedKeyHolder) throws DataAccessException {
// 执行批量更新操作,返回更新结果
int[] result = execute(psc, getPreparedStatementCallback(pss, generatedKeyHolder));
// 确保返回的结果数组不为空
Assert.state(result != null, "No result array");
// 返回更新结果
return result;
}
/**
* 执行 SQL 批量更新操作。
*
* @param sql SQL 更新语句
* @param pss BatchPreparedStatementSetter 用于设置每个批次的 SQL 参数
* @return 每个批次操作的影响行数数组
* @throws DataAccessException 数据访问异常
*/
@Override
public int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) throws DataAccessException {
// 如果调试级别日志开启,打印正在执行的 SQL 批量更新语句
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL batch update [" + sql + "]");
}
// 获取批次大小
int batchSize = pss.getBatchSize();
// 如果批次大小为 0,直接返回空数组
if (batchSize == 0) {
return new int[0];
}
// 执行批量更新操作,返回每个批次的影响行数数组
int[] result = execute(sql, getPreparedStatementCallback(pss, null));
// 确保返回的结果数组不为空
Assert.state(result != null, "No result array");
// 返回批量更新操作的影响行数数组
return result;
}
/**
* 使用给定的 SQL 和参数列表执行 SQL 批量更新操作。
*
* @param sql SQL 更新语句
* @param batchArgs 每个批次的参数列表
* @return 每个批次操作的影响行数数组
* @throws DataAccessException 数据访问异常
*/
@Override
public int[] batchUpdate(String sql, List<Object[]> batchArgs) throws DataAccessException {
// 调用重载方法 batchUpdate,并提供一个空的 argTypes 数组
return batchUpdate(sql, batchArgs, new int[0]);
}
/**
* 使用给定的 SQL 和参数列表执行 SQL 批量更新操作,并且指定参数类型。
*
* @param sql SQL 更新语句
* @param batchArgs 每个批次的参数列表
* @param argTypes 参数类型数组
* @return 每个批次操作的影响行数数组
* @throws DataAccessException 数据访问异常
*/
@Override
public int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) throws DataAccessException {
// 如果批次参数列表为空,直接返回空数组
if (batchArgs.isEmpty()) {
return new int[0];
}
// 调用 batchUpdate 方法,构造一个新的 BatchPreparedStatementSetter
return batchUpdate(
sql,
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
// 获取当前批次的参数值
Object[] values = batchArgs.get(i);
int colIndex = 0;
// 为每列参数设置值
for (Object value : values) {
colIndex++;
// 如果值是 SqlParameterValue 类型,则使用 StatementCreatorUtils 设置
if (value instanceof SqlParameterValue paramValue) {
StatementCreatorUtils.setParameterValue(ps, colIndex, paramValue, paramValue.getValue());
}
else {
int colType;
// 如果没有足够的参数类型,默认使用 TYPE_UNKNOWN
if (argTypes.length < colIndex) {
colType = SqlTypeValue.TYPE_UNKNOWN;
}
else {
colType = argTypes[colIndex - 1];
}
// 使用 StatementCreatorUtils 设置参数
StatementCreatorUtils.setParameterValue(ps, colIndex, colType, value);
}
}
}
@Override
public int getBatchSize() {
// 返回批次的大小(即参数列表的长度)
return batchArgs.size();
}
});
}
/**
* 执行 SQL 批量更新操作。
*
* @param sql SQL 更新语句
* @param batchArgs 批量参数集合
* @param batchSize 每批次的大小
* @param pss 用于设置 SQL 参数的回调接口
* @param <T> 参数类型
* @return 每批次更新的影响行数数组
* @throws DataAccessException 数据访问异常
*/
@Override
public <T> int[][] batchUpdate(String sql, Collection<T> batchArgs, int batchSize,
ParameterizedPreparedStatementSetter<T> pss) throws DataAccessException {
// 如果开启了调试日志,打印 SQL 批量更新的信息
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL batch update [" + sql + "] with a batch size of " + batchSize);
}
// 执行 SQL 批量更新操作
int[][] result = execute(sql, (PreparedStatementCallback<int[][]>) ps -> {
List<int[]> rowsAffected = new ArrayList<>();
try {
// 检查当前数据库连接是否支持批量更新
boolean batchSupported = JdbcUtils.supportsBatchUpdates(ps.getConnection());
int n = 0;
// 遍历每个批量参数对象
for (T obj : batchArgs) {
pss.setValues(ps, obj); // 设置 SQL 参数
n++; // 批次计数
if (batchSupported) {
// 如果支持批量更新,添加到批次中
ps.addBatch();
// 当批次达到指定大小或是最后一个参数时,执行批量更新
if (n % batchSize == 0 || n == batchArgs.size()) {
if (logger.isTraceEnabled()) {
// 打印批次信息
int batchIdx = (n % batchSize == 0) ? n / batchSize : (n / batchSize) + 1;
int items = n - ((n % batchSize == 0) ? n / batchSize - 1 : (n / batchSize)) * batchSize;
logger.trace("Sending SQL batch update #" + batchIdx + " with " + items + " items");
}
try {
// 执行批量更新
int[] updateCounts = ps.executeBatch();
rowsAffected.add(updateCounts);
}
catch (BatchUpdateException ex) {
// 如果发生批量更新异常,抛出合并异常
throw new AggregatedBatchUpdateException(rowsAffected.toArray(int[][]::new), ex);
}
}
}
else {
// 如果不支持批量更新,则逐个执行更新操作
int i = ps.executeUpdate();
rowsAffected.add(new int[] {i});
}
}
// 将所有批量操作的更新结果转成二维数组返回
int[][] result1 = new int[rowsAffected.size()][];
for (int i = 0; i < result1.length; i++) {
result1[i] = rowsAffected.get(i);
}
return result1;
}
finally {
// 如果参数设置器实现了 ParameterDisposer,进行参数清理
if (pss instanceof ParameterDisposer parameterDisposer) {
parameterDisposer.cleanupParameters();
}
}
});
// 确保返回的结果不为空
Assert.state(result != null, "No result array");
return result;
}
//-------------------------------------------------------------------------
// 处理可调用语句的方法
//-------------------------------------------------------------------------
/**
* 执行存储过程,提供 CallableStatementCallback 操作。
*
* @param csc CallableStatementCreator,用于创建 CallableStatement
* @param action 存储过程的回调接口
* @param <T> 存储过程返回值类型
* @return 存储过程执行的返回值
* @throws DataAccessException 数据访问异常
*/
@Override
public <T extends @Nullable Object> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action)
throws DataAccessException {
// 确保传入的参数不为 null
Assert.notNull(csc, "CallableStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
// 如果开启了调试日志,打印存储过程调用信息
if (logger.isDebugEnabled()) {
String sql = getSql(csc);
logger.debug("Calling stored procedure" + (sql != null ? " [" + sql + "]" : ""));
}
// 获取数据库连接并创建 CallableStatement
Connection con = DataSourceUtils.getConnection(obtainDataSource());
CallableStatement cs = null;
try {
cs = csc.createCallableStatement(con);
applyStatementSettings(cs); // 应用语句设置
T result = action.doInCallableStatement(cs); // 执行回调操作
handleWarnings(cs); // 处理警告信息
return result; // 返回存储过程执行结果
}
catch (SQLException ex) {
// 捕获 SQLException 异常并处理
if (csc instanceof ParameterDisposer parameterDisposer) {
parameterDisposer.cleanupParameters(); // 清理参数
}
if (cs != null) {
handleWarnings(cs, ex); // 处理警告
}
String sql = getSql(csc);
csc = null;
JdbcUtils.closeStatement(cs); // 关闭 Statement
cs = null;
DataSourceUtils.releaseConnection(con, getDataSource()); // 释放连接
con = null;
// 转换并抛出异常
throw translateException("CallableStatementCallback", sql, ex);
}
finally {
// 在 finally 中确保参数和连接被清理
if (csc instanceof ParameterDisposer parameterDisposer) {
parameterDisposer.cleanupParameters();
}
JdbcUtils.closeStatement(cs);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
/**
* 执行存储过程,提供调用字符串和 CallableStatementCallback 操作。
*
* @param callString 存储过程的调用字符串
* @param action 存储过程回调接口
* @param <T> 存储过程返回值类型
* @return 存储过程执行的返回值
* @throws DataAccessException 数据访问异常
*/
@Override
public <T extends @Nullable Object> T execute(String callString, CallableStatementCallback<T> action) throws DataAccessException {
// 使用 SimpleCallableStatementCreator 创建 CallableStatement,并执行回调
return execute(new SimpleCallableStatementCreator(callString), action);
}
/**
* 调用存储过程并提取输出参数和结果集。
*
* @param csc CallableStatementCreator,用于创建 CallableStatement
* @param declaredParameters 存储过程声明的参数
* @return 存储过程返回的结果 Map
* @throws DataAccessException 数据访问异常
*/
@Override
public Map<String, @Nullable Object> call(CallableStatementCreator csc, List<SqlParameter> declaredParameters)
throws DataAccessException {
List<SqlParameter> updateCountParameters = new ArrayList<>();
List<SqlParameter> resultSetParameters = new ArrayList<>();
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, @Nullable 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
Map<String, @Nullable 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;
}
/**
* 从存储过程的 CallableStatement 中提取返回的结果集。
*
* @param cs 存储过程的 CallableStatement
* @param updateCountParameters 更新计数参数
* @param resultSetParameters 结果集参数
* @param updateCount 更新计数
* @return 返回的结果 Map
* @throws SQLException SQL 异常
*/
protected Map<String, @Nullable Object> extractReturnedResults(CallableStatement cs,
@Nullable List<SqlParameter> updateCountParameters, @Nullable List<SqlParameter> resultSetParameters,
int updateCount) throws SQLException {
// 结果集存储 Map
Map<String, @Nullable Object> results = new LinkedHashMap<>(4);
int rsIndex = 0;
int updateIndex = 0;
boolean moreResults;
// 如果没有跳过结果处理,则提取结果
if (!isSkipResultsProcessing()) {
do {
// 处理结果集或更新计数
if (updateCount == -1) {
// 处理结果集
if (resultSetParameters != null && resultSetParameters.size() > rsIndex) {
SqlReturnResultSet declaredRsParam = (SqlReturnResultSet) resultSetParameters.get(rsIndex);
results.putAll(processResultSet(cs.getResultSet(), declaredRsParam));
rsIndex++;
}
else {
// 如果没有声明的结果集参数,使用默认参数
if (!isSkipUndeclaredResults()) {
String rsName = RETURN_RESULT_SET_PREFIX + (rsIndex + 1);
SqlReturnResultSet undeclaredRsParam = new SqlReturnResultSet(rsName, getColumnMapRowMapper());
if (logger.isTraceEnabled()) {
logger.trace("Added default SqlReturnResultSet parameter named '" + rsName + "'");
}
results.putAll(processResultSet(cs.getResultSet(), undeclaredRsParam));
rsIndex++;
}
}
}
else {
// 处理更新计数
if (updateCountParameters != null && updateCountParameters.size() > updateIndex) {
SqlReturnUpdateCount ucParam = (SqlReturnUpdateCount) updateCountParameters.get(updateIndex);
String declaredUcName = ucParam.getName();
results.put(declaredUcName, updateCount);
updateIndex++;
}
else {
// 如果没有声明的更新计数参数,使用默认参数
if (!isSkipUndeclaredResults()) {
String undeclaredName = RETURN_UPDATE_COUNT_PREFIX + (updateIndex + 1);
if (logger.isTraceEnabled()) {
logger.trace("Added default SqlReturnUpdateCount parameter named '" + undeclaredName + "'");
}
results.put(undeclaredName, updateCount);
updateIndex++;
}
}
}
// 获取是否有更多结果
moreResults = cs.getMoreResults();
updateCount = cs.getUpdateCount();
if (logger.isTraceEnabled()) {
logger.trace("CallableStatement.getUpdateCount() returned " + updateCount);
}
}
while (moreResults || updateCount != -1);
}
return results;
}
/**
* 从已完成的存储过程中提取输出参数。
*
* @param cs 存储过程的 JDBC 包装器
* @param parameters 存储过程的参数列表
* @return 包含返回结果的 Map
* @throws SQLException SQL 异常
*/
protected Map<String, @Nullable Object> extractOutputParameters(CallableStatement cs, List<SqlParameter> parameters)
throws SQLException {
Map<String, @Nullable Object> results = CollectionUtils.newLinkedHashMap(parameters.size());
int sqlColIndex = 1;
for (SqlParameter param : parameters) {
if (param instanceof SqlOutParameter outParam) {
Assert.state(outParam.getName() != null, "Anonymous parameters not allowed");
SqlReturnType returnType = outParam.getSqlReturnType();
// 如果返回类型不为空,则使用该类型获取参数值
if (returnType != null) {
Object out = returnType.getTypeValue(cs, sqlColIndex, outParam.getSqlType(), outParam.getTypeName());
results.put(outParam.getName(), out);
}
else {
// 如果返回类型为空,则直接获取对象值
Object out = cs.getObject(sqlColIndex);
// 如果返回值是 ResultSet 类型,则根据参数配置处理该结果集
if (out instanceof ResultSet resultSet) {
if (outParam.isResultSetSupported()) {
results.putAll(processResultSet(resultSet, outParam));
}
else {
String rsName = outParam.getName();
SqlReturnResultSet rsParam = new SqlReturnResultSet(rsName, getColumnMapRowMapper());
results.putAll(processResultSet(resultSet, rsParam));
// 如果开启了日志,记录添加的默认参数名
if (logger.isTraceEnabled()) {
logger.trace("Added default SqlReturnResultSet parameter named '" + rsName + "'");
}
}
}
else {
results.put(outParam.getName(), out);
}
}
}
// 如果当前参数不是结果参数,继续下一个参数的处理
if (!param.isResultsParameter()) {
sqlColIndex++;
}
}
return results;
}
/**
* 处理存储过程返回的 ResultSet。
*
* @param rs 存储过程返回的 ResultSet
* @param param 对应的存储过程参数
* @return 包含返回结果的 Map
* @throws SQLException SQL 异常
*/
@SuppressWarnings("NullAway") // See https://github.com/uber/NullAway/issues/950
protected Map<@Nullable String, @Nullable Object> processResultSet(
@Nullable ResultSet rs, ResultSetSupportingSqlParameter param) throws SQLException {
if (rs != null) {
try {
// 根据参数配置,使用不同的方式处理 ResultSet
if (param.getRowMapper() != null) {
RowMapper<? extends @Nullable Object> rowMapper = param.getRowMapper();
Object data = (new RowMapperResultSetExtractor<>(rowMapper)).extractData(rs);
return Collections.singletonMap(param.getName(), data);
}
else if (param.getRowCallbackHandler() != null) {
RowCallbackHandler rch = param.getRowCallbackHandler();
(new RowCallbackHandlerResultSetExtractor(rch, -1)).extractData(rs);
return Collections.singletonMap(param.getName(),
"ResultSet returned from stored procedure was processed");
}
else if (param.getResultSetExtractor() != null) {
Object data = param.getResultSetExtractor().extractData(rs);
return Collections.singletonMap(param.getName(), data);
}
}
finally {
// 确保关闭 ResultSet
JdbcUtils.closeResultSet(rs);
}
}
return Collections.emptyMap();
}
//-------------------------------------------------------------------------
// Implementation hooks and helper methods
//-------------------------------------------------------------------------
/**
* 创建一个新的 RowMapper,用于将列读取为键值对。
*
* @return 用于映射的 RowMapper
* @see ColumnMapRowMapper
*/
protected RowMapper<Map<String, @Nullable Object>> getColumnMapRowMapper() {
return new ColumnMapRowMapper();
}
/**
* 创建一个新的 RowMapper,用于从单列中读取结果对象。
*
* @param requiredType 每个结果对象需要匹配的类型
* @return 用于映射的 RowMapper
* @see SingleColumnRowMapper
*/
protected <T> RowMapper<@Nullable T> getSingleColumnRowMapper(Class<T> requiredType) {
return new SingleColumnRowMapper<>(requiredType);
}
/**
* 创建一个 Map 实例,用作结果的存储 Map。
*
* <p>如果 {@link #resultsMapCaseInsensitive} 设置为 true,
* 则创建一个 {@link LinkedCaseInsensitiveMap};否则,创建一个
* {@link LinkedHashMap}。
*
* @return 结果 Map 实例
* @see #setResultsMapCaseInsensitive
* @see #isResultsMapCaseInsensitive
*/
protected Map<String, @Nullable Object> createResultsMap() {
// 如果启用了大小写不敏感的结果 Map,则创建一个 LinkedCaseInsensitiveMap
if (isResultsMapCaseInsensitive()) {
return new LinkedCaseInsensitiveMap<>();
}
else {
// 否则,创建一个默认的 LinkedHashMap
return new LinkedHashMap<>();
}
}
/**
* 准备给定的 JDBC Statement(或 PreparedStatement 或 CallableStatement),
* 应用语句设置,如 fetch size、最大行数和查询超时。
*
* @param stmt 要准备的 JDBC Statement
* @throws SQLException 如果 JDBC API 抛出异常
* @see #setFetchSize
* @see #setMaxRows
* @see #setQueryTimeout
* @see org.springframework.jdbc.datasource.DataSourceUtils#applyTransactionTimeout
*/
protected void applyStatementSettings(Statement stmt) throws SQLException {
// 获取设置的 fetchSize,如果不为 -1,则应用到 Statement
int fetchSize = getFetchSize();
if (fetchSize != -1) {
stmt.setFetchSize(fetchSize);
}
// 获取设置的 maxRows,如果不为 -1,则应用到 Statement
int maxRows = getMaxRows();
if (maxRows != -1) {
stmt.setMaxRows(maxRows);
}
// 应用查询超时设置
DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
}
/**
* 根据传入的参数创建一个新的基于参数的 PreparedStatementSetter。
*
* <p>默认情况下,会创建一个 {@link ArgumentPreparedStatementSetter}。
* 该方法允许子类重写自定义创建过程。
*
* @param args 参数数组
* @return 新的 PreparedStatementSetter
*/
protected PreparedStatementSetter newArgPreparedStatementSetter(@Nullable Object @Nullable [] args) {
// 创建一个新的 ArgumentPreparedStatementSetter 并返回
return new ArgumentPreparedStatementSetter(args);
}
/**
* 根据传入的参数和类型创建一个新的基于参数类型的 PreparedStatementSetter。
*
* <p>默认情况下,会创建一个 {@link ArgumentTypePreparedStatementSetter}。
* 该方法允许子类重写自定义创建过程。
*
* @param args 参数数组
* @param argTypes 与参数对应的 SQL 类型数组
* @return 新的 PreparedStatementSetter
*/
protected PreparedStatementSetter newArgTypePreparedStatementSetter(@Nullable Object @Nullable [] args, int[] argTypes) {
// 创建一个新的 ArgumentTypePreparedStatementSetter 并返回
return new ArgumentTypePreparedStatementSetter(args, argTypes);
}
/**
* 在传播执行语句时的主要 {@code SQLException} 之前处理警告信息。
*
* <p>会调用常规的 {@link #handleWarnings(Statement)} 方法,但捕获
* {@link SQLWarningException},以便将 {@link SQLWarning} 链接到主要异常。
*
* @param stmt 当前的 JDBC Statement
* @param ex 执行语句失败后的主要异常
* @since 5.3.29
* @see #handleWarnings(Statement)
* @see SQLException#setNextException
*/
protected void handleWarnings(Statement stmt, SQLException ex) {
try {
handleWarnings(stmt);
}
catch (SQLWarningException nonIgnoredWarning) {
ex.setNextException(nonIgnoredWarning.getSQLWarning());
}
catch (SQLException warningsEx) {
logger.debug("Failed to retrieve warnings", warningsEx);
}
catch (Throwable warningsEx) {
logger.debug("Failed to process warnings", warningsEx);
}
}
/**
* 处理给定 JDBC 语句的警告(如果有的话)。
*
* <p>如果没有忽略警告,则抛出 {@link SQLWarningException},
* 否则以调试级别记录警告信息。
*
* @param stmt 当前的 JDBC 语句
* @throws SQLException 如果获取警告时失败
* @throws SQLWarningException 如果遇到警告且不忽略该警告时,抛出该异常
* @see #setIgnoreWarnings
* @see #handleWarnings(SQLWarning)
*/
protected void handleWarnings(Statement stmt) throws SQLException, SQLWarningException {
// 如果忽略警告
if (isIgnoreWarnings()) {
// 如果日志级别为调试,则记录警告信息
if (logger.isDebugEnabled()) {
SQLWarning warningToLog = stmt.getWarnings();
while (warningToLog != null) {
logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '" +
warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");
warningToLog = warningToLog.getNextWarning();
}
}
}
else {
// 如果没有忽略警告,调用处理警告的方法
handleWarnings(stmt.getWarnings());
}
}
/**
* 如果遇到实际的警告,则抛出 {@link SQLWarningException}。
*
* @param warning 当前语句的警告对象,可能为 {@code null},如果是 {@code null},则什么都不做
* @throws SQLWarningException 如果遇到实际警告时抛出
*/
protected void handleWarnings(@Nullable SQLWarning warning) throws SQLWarningException {
if (warning != null) {
throw new SQLWarningException("Warning not ignored", warning);
}
}
/**
* 将给定的 {@link SQLException} 转换为通用的 {@link DataAccessException}。
*
* @param task 描述当前尝试执行的任务的可读文本
* @param sql 发生问题的 SQL 查询或更新(可能为 {@code null})
* @param ex 触发问题的 {@code SQLException}
* @return 一个封装了 {@code SQLException} 的 DataAccessException(永远不为 {@code null})
* @since 5.0
* @see #getExceptionTranslator()
*/
protected DataAccessException translateException(String task, @Nullable String sql, SQLException ex) {
DataAccessException dae = getExceptionTranslator().translate(task, sql, ex);
return (dae != null ? dae : new UncategorizedSQLException(task, sql, ex));
}
/**
* 从潜在的提供者对象中获取 SQL 查询。
*
* @param obj 可能是 SqlProvider 类型的对象
* @return SQL 字符串,如果未知则返回 {@code null}
* @see SqlProvider
*/
private static @Nullable String getSql(Object obj) {
return (obj instanceof SqlProvider sqlProvider ? sqlProvider.getSql() : null);
}
/**
* 断言返回结果不为 null,如果为 null 会抛出异常。
*
* @param result 返回的结果
* @return 非 null 的结果
* @throws IllegalStateException 如果结果为 null
*/
private static <T> T result(@Nullable T result) {
Assert.state(result != null, "No result");
return result;
}
/**
* 断言更新计数不为 null,如果为 null 会抛出异常。
*
* @param result 返回的更新计数
* @return 非 null 的更新计数
* @throws IllegalStateException 如果更新计数为 null
*/
private static int updateCount(@Nullable Integer result) {
Assert.state(result != null, "No update count");
return result;
}
/**
* 存储生成的键到 KeyHolder。
*
* @param generatedKeyHolder 存储生成的键的 KeyHolder
* @param ps 执行插入操作的 PreparedStatement
* @param rowsExpected 期望的行数
* @throws SQLException 如果获取生成的键时失败
*/
private void storeGeneratedKeys(KeyHolder generatedKeyHolder, PreparedStatement ps, int rowsExpected)
throws SQLException {
// 获取生成的键并将其存储到 KeyHolder 中
List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList();
ResultSet keys = ps.getGeneratedKeys();
if (keys != null) {
try {
// 使用 RowMapper 解析生成的键并添加到生成的键列表中
RowMapperResultSetExtractor<Map<String, Object>> rse =
new RowMapperResultSetExtractor<>(getColumnMapRowMapper(), rowsExpected);
generatedKeys.addAll(result(rse.extractData(keys)));
}
finally {
JdbcUtils.closeResultSet(keys);
}
}
}
/**
* 创建一个 PreparedStatementCallback 用于批量操作。
*
* @param pss 批量操作的 PreparedStatementSetter
* @param generatedKeyHolder 存储生成键的 KeyHolder(可选)
* @return PreparedStatementCallback
*/
private PreparedStatementCallback<int[]> getPreparedStatementCallback(BatchPreparedStatementSetter pss,
@Nullable KeyHolder generatedKeyHolder) {
return ps -> {
try {
// 获取批量操作的大小
int batchSize = pss.getBatchSize();
// 如果是可中断的批量操作,获取其特定实现
InterruptibleBatchPreparedStatementSetter ipss =
(pss instanceof InterruptibleBatchPreparedStatementSetter ibpss ? ibpss : null);
// 如果提供了 KeyHolder,清空它
if (generatedKeyHolder != null) {
generatedKeyHolder.getKeyList().clear();
}
// 检查数据库是否支持批量更新
if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
for (int i = 0; i < batchSize; i++) {
// 执行批量更新操作
pss.setValues(ps, i);
if (ipss != null && ipss.isBatchExhausted(i)) {
break;
}
ps.addBatch();
}
int[] results = ps.executeBatch();
// 如果提供了 KeyHolder,存储生成的键
if (generatedKeyHolder != null) {
storeGeneratedKeys(generatedKeyHolder, ps, batchSize);
}
return results;
}
else {
// 如果不支持批量更新,逐个执行更新操作
List<Integer> rowsAffected = new ArrayList<>();
for (int i = 0; i < batchSize; i++) {
pss.setValues(ps, i);
if (ipss != null && ipss.isBatchExhausted(i)) {
break;
}
rowsAffected.add(ps.executeUpdate());
// 如果提供了 KeyHolder,存储生成的键
if (generatedKeyHolder != null) {
storeGeneratedKeys(generatedKeyHolder, ps, 1);
}
}
// 转换结果为数组并返回
int[] rowsAffectedArray = new int[rowsAffected.size()];
for (int i = 0; i < rowsAffectedArray.length; i++) {
rowsAffectedArray[i] = rowsAffected.get(i);
}
return rowsAffectedArray;
}
}
finally {
if (pss instanceof ParameterDisposer parameterDisposer) {
parameterDisposer.cleanupParameters();
}
}
};
}
/**
* 一个调用处理器,用于抑制对 JDBC 连接的关闭调用。
* 还会准备返回的 Statement(Prepared/CallbackStatement)对象。
* @see java.sql.Connection#close()
*/
private class CloseSuppressingInvocationHandler implements InvocationHandler {
private final Connection target;
public CloseSuppressingInvocationHandler(Connection target) {
this.target = target;
}
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 对 ConnectionProxy 接口的调用...
return switch (method.getName()) {
// 只有代理对象相同才认为是 equals
case "equals" -> (proxy == args[0]);
// 使用 Connection 代理的 hashCode
case "hashCode" -> System.identityHashCode(proxy);
// 处理 close 方法:抑制,不能执行关闭操作
case "close" -> null;
case "isClosed" -> false;
// 处理 getTargetConnection 方法:返回底层的 Connection 对象
case "getTargetConnection" -> this.target;
// 处理 unwrap 方法:返回底层连接或代理连接
case "unwrap" ->
(((Class<?>) args[0]).isInstance(proxy) ? proxy : this.target.unwrap((Class<?>) args[0]));
// 处理 isWrapperFor 方法:判断是否是代理连接
case "isWrapperFor" ->
(((Class<?>) args[0]).isInstance(proxy) || this.target.isWrapperFor((Class<?>) args[0]));
default -> {
try {
// 调用目标 Connection 的方法
Object retVal = method.invoke(this.target, args);
// 如果返回值是 JDBC Statement,应用语句设置(如 fetch size、max rows、超时等)
if (retVal instanceof Statement statement) {
applyStatementSettings(statement);
}
// 返回结果
yield retVal;
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
};
}
}
/**
* 一个简单的适配器,用于 PreparedStatementCreator,允许使用普通的 SQL 语句。
*/
private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
private final String sql;
public SimplePreparedStatementCreator(String sql) {
Assert.notNull(sql, "SQL must not be null");
this.sql = sql;
}
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(this.sql);
}
@Override
public String getSql() {
return this.sql;
}
}
/**
* 一个简单的适配器,用于 CallableStatementCreator,允许使用普通的 SQL 调用语句。
*/
private static class SimpleCallableStatementCreator implements CallableStatementCreator, SqlProvider {
private final String callString;
public SimpleCallableStatementCreator(String callString) {
Assert.notNull(callString, "Call string must not be null");
this.callString = callString;
}
@Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
return con.prepareCall(this.callString);
}
@Override
public String getSql() {
return this.callString;
}
}
/**
* 适配器,允许在 ResultSetExtractor 中使用 RowCallbackHandler。
* <p>使用普通的 ResultSet,因此在使用时需要小心:不要用来遍历 ResultSet,
* 否则可能会导致不可预测的后果。
*/
private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<@Nullable Object> {
private final RowCallbackHandler rch;
private final int maxRows;
public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch, int maxRows) {
this.rch = rch;
this.maxRows = maxRows;
}
@Override
public @Nullable Object extractData(ResultSet rs) throws SQLException {
int processed = 0;
while (rs.next() && (this.maxRows == -1 || (processed++) < this.maxRows)) {
this.rch.processRow(rs);
}
return null;
}
}
/**
* 用于将 ResultSet 转换为流(Stream)的 Spliterator 适配器。
* @since 5.3
*/
private static class ResultSetSpliterator<T> implements Spliterator<T> {
private final ResultSet rs;
private final RowMapper<T> rowMapper;
private final int maxRows;
private int rowNum = 0;
public ResultSetSpliterator(ResultSet rs, RowMapper<T> rowMapper, int maxRows) {
this.rs = rs;
this.rowMapper = rowMapper;
this.maxRows = maxRows;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
try {
if (this.rs.next() && (this.maxRows == -1 || this.rowNum < this.maxRows)) {
action.accept(this.rowMapper.mapRow(this.rs, this.rowNum++));
return true;
}
return false;
}
catch (SQLException ex) {
throw new InvalidResultSetAccessException(ex);
}
}
@Override
public @Nullable Spliterator<T> trySplit() {
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return Spliterator.ORDERED;
}
public Stream<T> stream() {
return StreamSupport.stream(this, false);
}
}
}