当前位置: 首页 > news >正文

Spring源码解读之 JdbcTemplate源码

Spring的JdbcTemplate是Spring框架中用于简化JDBC操作的核心类之一。它通过封装底层的JDBC API,提供了统一、简洁的数据库操作方式,极大地减少了开发人员在数据库操作中需要处理的样板代码,如连接获取、关闭、异常处理等。下面将从源码进行详细解读。

/** 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 数据访问异常*/@Overridepublic <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/1075public void execute(String sql) throws DataAccessException {if (logger.isDebugEnabled()) {logger.debug("Executing SQL statement [" + sql + "]");}// 定义SQL执行的回调class ExecuteStatementCallback implements StatementCallback<@Nullable Object>, SqlProvider {@Overridepublic @Nullable Object doInStatement(Statement stmt) throws SQLException {stmt.execute(sql);return null;}@Overridepublic 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/1075public <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 {@Overridepublic @Nullable T doInStatement(Statement stmt) throws SQLException {ResultSet rs = null;try {rs = stmt.executeQuery(sql);return rse.extractData(rs);}finally {JdbcUtils.closeResultSet(rs);}}@Overridepublic 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/1075public 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 数据访问异常*/@Overridepublic <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 数据访问异常*/@Overridepublic <T> Stream<T> queryForStream(String sql, RowMapper<T> rowMapper) throws DataAccessException {class StreamStatementCallback implements StatementCallback<Stream<T>>, SqlProvider {@Overridepublic 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());});}@Overridepublic String getSql() {return sql;}}return result(execute(new StreamStatementCallback(), false));}/*** 使用查询SQL并将结果映射为一个Map(每列的列名为键,列值为值)。* * @param sql 查询SQL语句* @return 返回查询结果的Map,键是列名,值是对应列的值* @throws DataAccessException 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic <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 数据访问异常*/@Overridepublic <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/1075public <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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic SqlRowSet queryForRowSet(String sql) throws DataAccessException {return result(query(sql, new SqlRowSetResultSetExtractor()));}/*** 执行SQL更新语句,并返回受影响的行数。* <p>该方法用于执行类似INSERT、UPDATE、DELETE等不返回结果集的SQL语句。* * @param sql 要执行的SQL更新语句* @return 受影响的行数* @throws DataAccessException 数据访问异常*/@Overridepublic 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 {@Overridepublic Integer doInStatement(Statement stmt) throws SQLException {int rows = stmt.executeUpdate(sql);if (logger.isTraceEnabled()) {logger.trace("SQL update affected " + rows + " rows");}return rows;}@Overridepublic String getSql() {return sql;}}return updateCount(execute(new UpdateStatementCallback(), true));}/*** 执行批量更新操作,返回每个更新语句的影响行数。* <p>该方法适用于执行多个类似的更新语句(例如批量插入、批量更新等)。* * @param sql 需要执行的SQL更新语句数组* @return 每个SQL语句影响的行数数组* @throws DataAccessException 数据访问异常*/@Overridepublic 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;@Overridepublic 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);}@Overridepublic @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 如果查询失败*/@Overridepublic <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")@Overridepublic <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 如果查询失败*/@Overridepublic <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/1075public 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/1075public 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 如果查询失败*/@Overridepublic 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")@Overridepublic 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 如果查询失败*/@Overridepublic 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 如果查询失败*/@Overridepublic <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 如果查询失败*/@Overridepublic <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 如果查询失败*/@Overridepublic <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")@Overridepublic <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 如果查询失败*/@Overridepublic <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 如果查询失败*/@Overridepublic <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")@Overridepublic <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));// 返回结果的单个对象,若没有符合条件的结果则返回 nullreturn DataAccessUtils.nullableSingleResult(results);}/*** 该方法用于执行 SQL 查询,并将结果映射为单个对象。* 使用了变参 `args` 来传递查询参数。* @param sql 执行的 SQL 查询语句* @param rowMapper 用于映射查询结果的 RowMapper* @param args 查询参数,可以传递多个参数* @return 返回查询结果的单个对象,若结果为空或不符合条件,则返回 null* @throws DataAccessException 数据访问异常*/@Overridepublic <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));// 返回结果的单个对象,若没有符合条件的结果则返回 nullreturn DataAccessUtils.nullableSingleResult(results);}/*** 该方法执行 SQL 查询并返回单个对象。支持通过 `argTypes` 指定参数类型。* @param sql 执行的 SQL 查询语句* @param args 查询参数,可能为 null* @param argTypes 参数类型的数组,必须与参数数量一致* @param requiredType 结果对象的类型* @return 返回查询结果的单个对象,若没有符合条件的结果则返回 null* @throws DataAccessException 数据访问异常*/@Overridepublic <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")@Overridepublic <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 数据访问异常*/@Overridepublic <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 数据访问异常*/@Overridepublic Map<String, @Nullable Object> queryForMap(String sql, @Nullable Object @Nullable [] args, int[] argTypes) throws DataAccessException {// 调用查询方法并返回 Map 结果,使用 ColumnMapRowMapper 将查询结果转换为 Mapreturn result(queryForObject(sql, args, argTypes, getColumnMapRowMapper()));}/*** 执行 SQL 查询,并返回一个 Map,Map 的键是列名,值是列的值。* 该方法使用变参形式接收查询参数。* @param sql 执行的 SQL 查询语句* @param args 查询参数,可能为 null* @return 返回包含列名和对应值的 Map* @throws DataAccessException 数据访问异常*/@Overridepublic Map<String, @Nullable Object> queryForMap(String sql, @Nullable Object @Nullable ... args) throws DataAccessException {// 调用查询方法并返回 Map 结果,使用 ColumnMapRowMapper 将查询结果转换为 Mapreturn 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 checkspublic <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 checkspublic <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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic SqlRowSet queryForRowSet(String sql, @Nullable Object @Nullable [] args, int[] argTypes) throws DataAccessException {// 执行查询并返回一个 `SqlRowSet` 对象,结果集通过 `SqlRowSetResultSetExtractor` 提取return result(query(sql, args, argTypes, new SqlRowSetResultSetExtractor()));}@Overridepublic 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 数据访问异常*/@Overridepublic int update(PreparedStatementCreator psc) throws DataAccessException {// 无需指定 PreparedStatementSetter,调用上述重载方法return update(psc, (PreparedStatementSetter) null);}/*** 执行 SQL 更新操作,并返回生成的主键。** @param psc               PreparedStatementCreator,用于创建 PreparedStatement* @param generatedKeyHolder 用于存储生成的主键* @return 更新操作影响的行数* @throws DataAccessException 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic 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 数据访问异常*/@Overridepublic int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) throws DataAccessException {// 如果批次参数列表为空,直接返回空数组if (batchArgs.isEmpty()) {return new int[0];}// 调用 batchUpdate 方法,构造一个新的 BatchPreparedStatementSetterreturn batchUpdate(sql,new BatchPreparedStatementSetter() {@Overridepublic 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_UNKNOWNif (argTypes.length < colIndex) {colType = SqlTypeValue.TYPE_UNKNOWN;}else {colType = argTypes[colIndex - 1];}// 使用 StatementCreatorUtils 设置参数StatementCreatorUtils.setParameterValue(ps, colIndex, colType, value);}}}@Overridepublic int getBatchSize() {// 返回批次的大小(即参数列表的长度)return batchArgs.size();}});}/*** 执行 SQL 批量更新操作。* * @param sql SQL 更新语句* @param batchArgs 批量参数集合* @param batchSize 每批次的大小* @param pss 用于设置 SQL 参数的回调接口* @param <T> 参数类型* @return 每批次更新的影响行数数组* @throws DataAccessException 数据访问异常*/@Overridepublic <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 数据访问异常*/@Overridepublic <T extends @Nullable Object> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action)throws DataAccessException {// 确保传入的参数不为 nullAssert.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 + "]" : ""));}// 获取数据库连接并创建 CallableStatementConnection 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); // 关闭 Statementcs = 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 数据访问异常*/@Overridepublic <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 数据访问异常*/@Overridepublic 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);}// 创建存储过程结果的 MapMap<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 {// 结果集存储 MapMap<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/950protected Map<@Nullable String, @Nullable Object> processResultSet(@Nullable ResultSet rs, ResultSetSupportingSqlParameter param) throws SQLException {if (rs != null) {try {// 根据参数配置,使用不同的方式处理 ResultSetif (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 {// 确保关闭 ResultSetJdbcUtils.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,则创建一个 LinkedCaseInsensitiveMapif (isResultsMapCaseInsensitive()) {return new LinkedCaseInsensitiveMap<>();}else {// 否则,创建一个默认的 LinkedHashMapreturn 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,则应用到 Statementint fetchSize = getFetchSize();if (fetchSize != -1) {stmt.setFetchSize(fetchSize);}// 获取设置的 maxRows,如果不为 -1,则应用到 Statementint 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;}@Overridepublic @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 对 ConnectionProxy 接口的调用...return switch (method.getName()) {// 只有代理对象相同才认为是 equalscase "equals" -> (proxy == args[0]);// 使用 Connection 代理的 hashCodecase "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;}@Overridepublic PreparedStatement createPreparedStatement(Connection con) throws SQLException {return con.prepareStatement(this.sql);}@Overridepublic 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;}@Overridepublic CallableStatement createCallableStatement(Connection con) throws SQLException {return con.prepareCall(this.callString);}@Overridepublic 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;}@Overridepublic @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;}@Overridepublic 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);}}@Overridepublic @Nullable Spliterator<T> trySplit() {return null;}@Overridepublic long estimateSize() {return Long.MAX_VALUE;}@Overridepublic int characteristics() {return Spliterator.ORDERED;}public Stream<T> stream() {return StreamSupport.stream(this, false);}}}
http://www.dtcms.com/a/303129.html

相关文章:

  • 【基础篇三】WebSocket:实时通信的革命
  • 基于DeepSeek大模型和STM32的矿井“围压-温度-开采扰动“三位一体智能监测系统设计
  • 排序算法 (Sorting Algorithms)-JS示例
  • 安装及使用vscode
  • Unity教程(二十四)技能系统 投剑技能(中)技能变种实现
  • 【Unity游戏】——1.俄罗斯方块
  • Apache Ignite的分布式计算(Distributed Computing)
  • 基于Milvus和BGE-VL模型实现以图搜图
  • 第17章——多元函数积分学的预备知识
  • odoo欧度小程序——修改用户和密码
  • RabbitMQ+内网穿透远程访问教程:实现异地AMQP通信+Web管理
  • 基于springboot的大创管理系统(源码+论文+开题报告)
  • 项目任务如何分配?核心原则
  • 银行个人贷款接受度分析
  • el-upload开启picture形式列表展示上传的非图片文件自定义缩略图
  • 网络层描述
  • Leetcode_349.两个数组的交集
  • Word VBA快速制作试卷(2/2)
  • 【华为机试】5. 最长回文子串
  • 学习人工智能所需知识体系及路径详解
  • 记录几个SystemVerilog的语法——随机
  • 五自由度磁悬浮轴承转子:基于自适应陷波器的零振动攻克不平衡质量扰动的终极策略
  • (45) QT 提供了一个功能,以同步现代操作系统的编辑功能,在标题栏上显示 * 占位符,以显示窗体上发生了未被保存的修改
  • 三维插件 Forest 深度解析:打造高效逼真的自然环境
  • 命令执行漏洞
  • 计算机毕设分享-基于SpringBoot的健身房管理系统(开题报告+前后端源码+Lun文+开发文档+数据库设计文档)
  • USRP-X440 雷达目标发生器
  • 深入解析 Java Stream 设计:从四幕剧看流水线设计与执行机制
  • 对于ui=f(state)的理解(react)
  • Redis四种GetShell方式完整教程