📄 jdbctemplate.java
字号:
/*
* Copyright 2002-2004 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
*
* http://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.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.CollectionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.TypeMismatchDataAccessException;
import org.springframework.jdbc.SQLWarningException;
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.nativejdbc.NativeJdbcExtractor;
import org.springframework.util.NumberUtils;
/**
* <b>This is the central class in the JDBC core package.</b>
* It simplifies the use of JDBC and helps to avoid common errors.
* It executes core JDBC workflow, leaving application code to provide SQL
* and extract results. This class executes SQL queries or updates, initiating
* iteration over ResultSets and catching JDBC exceptions and translating
* them to the generic, more informative exception hierarchy defined in the
* <code>org.springframework.dao</code> package.
*
* <p>Code using this class need only implement callback interfaces, giving
* them a clearly defined contract. The PreparedStatementCreator callback
* interface creates a prepared statement given a Connection provided by this
* class, providing SQL and any necessary parameters. The RowCallbackHandler
* interface extracts values from each row of a ResultSet.
*
* <p>Can be used within a service implementation via direct instantiation
* with a DataSource reference, or get prepared in an application context
* and given to services as bean reference. Note: The DataSource should
* always be configured as a bean in the application context, in the first case
* given to the service directly, in the second case to the prepared template.
*
* <p>The motivation and design of this class is discussed
* in detail in
* <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0764543857/">Expert One-On-One J2EE Design and Development</a>
* by Rod Johnson (Wrox, 2002).
*
* <p>Because this class is parameterizable by the callback interfaces and
* the SQLExceptionTranslator interface, it isn't necessary to subclass it.
* All SQL issued by this class is logged.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Thomas Risberg
* @since May 3, 2001
* @see ResultSetExtractor
* @see RowCallbackHandler
* @see RowMapper
* @see org.springframework.dao
* @see org.springframework.jdbc.datasource
* @see org.springframework.jdbc.object
*/
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations, InitializingBean {
/** Custom NativeJdbcExtractor */
private NativeJdbcExtractor nativeJdbcExtractor;
/** If this variable is false, we will throw exceptions on SQL warnings */
private boolean ignoreWarnings = true;
/**
* If this variable is set to a non-zero value, it will be used for setting the
* fetchSize property on statements used for query processing.
*/
private int fetchSize = 0;
/**
* If this variable is set to a non-zero value, it will be used for setting the
* maxRows property on statements used for query processing.
*/
private int maxRows = 0;
/**
* Construct a new JdbcTemplate for bean usage.
* Note: The DataSource has to be set before using the instance.
* This constructor can be used to prepare a JdbcTemplate via a BeanFactory,
* typically setting the DataSource via setDataSource.
* @see #setDataSource
*/
public JdbcTemplate() {
}
/**
* Construct a new JdbcTemplate, given a DataSource to obtain connections from.
* Note: This will trigger eager initialization of the exception translator.
* @param dataSource JDBC DataSource to obtain connections from
*/
public JdbcTemplate(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
/**
* Set a NativeJdbcExtractor to extract native JDBC objects from wrapped handles.
* Useful if native Statement and/or ResultSet handles are expected for casting
* to database-specific implementation classes, but a connection pool that wraps
* JDBC objects is used (note: <i>any</i> pool will return wrapped Connections).
*/
public void setNativeJdbcExtractor(NativeJdbcExtractor extractor) {
this.nativeJdbcExtractor = extractor;
}
/**
* Return the current NativeJdbcExtractor implementation.
*/
public NativeJdbcExtractor getNativeJdbcExtractor() {
return this.nativeJdbcExtractor;
}
/**
* Set whether or not we want to ignore SQLWarnings.
* Default is true.
*/
public void setIgnoreWarnings(boolean ignoreWarnings) {
this.ignoreWarnings = ignoreWarnings;
}
/**
* Return whether or not we ignore SQLWarnings. Default is true.
*/
public boolean isIgnoreWarnings() {
return ignoreWarnings;
}
/**
* Return whether or not we ignore SQLWarnings. Default is true.
* @deprecated in favor of <code>isIgnoreWarnings</code>
* @see #isIgnoreWarnings
*/
public boolean getIgnoreWarnings() {
return ignoreWarnings;
}
/**
* Set the fetch size for this JdbcTemplate. This is important for processing
* large result sets: Setting this higher than the default value will increase
* processing speed at the cost of memory consumption; setting this lower can
* avoid transferring row data that will never be read by the application.
* <p>Default is 0, indicating to use the driver's default.
*/
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
}
/**
* Return the fetch size specified for this JdbcTemplate.
*/
public int getFetchSize() {
return fetchSize;
}
/**
* Set the maximum number of rows for this JdbcTemplate. This is important
* for processing subsets of large result sets, avoiding to read and hold
* the entire result set in the database or in the JDBC driver.
* <p>Default is 0, indicating to use the driver's default.
*/
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
/**
* Return the maximum number of rows specified for this JdbcTemplate.
*/
public int getMaxRows() {
return maxRows;
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (getFetchSize() > getMaxRows()) {
throw new IllegalArgumentException("fetchSize must not be higher than maxRows");
}
}
//-------------------------------------------------------------------------
// Methods dealing with a plain java.sql.Connection
//-------------------------------------------------------------------------
public Object execute(final ConnectionCallback action) throws DataAccessException {
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
throw getExceptionTranslator().translate("executing ConnectionCallback", getSql(action), ex);
}
finally {
DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
}
}
//-------------------------------------------------------------------------
// Methods dealing with static SQL (java.sql.Statement)
//-------------------------------------------------------------------------
public Object execute(final StatementCallback action) throws DataAccessException {
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
stmt = conToUse.createStatement();
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
Object result = action.doInStatement(stmtToUse);
SQLWarning warning = stmt.getWarnings();
throwExceptionOnWarningIfNotIgnoringWarnings(warning);
return result;
}
catch (SQLException ex) {
throw getExceptionTranslator().translate("executing StatementCallback", getSql(action), ex);
}
finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
}
}
public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
class ExecuteStatementCallback implements StatementCallback, SqlProvider {
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback());
}
public Object query(final String sql, final ResultSetExtractor rse) throws DataAccessException {
if (sql == null) {
throw new InvalidDataAccessApiUsageException("SQL must not be null");
}
if (JdbcUtils.countParameterPlaceholders(sql, '?', "'\"") > 0) {
throw new InvalidDataAccessApiUsageException(
"Cannot execute [" + sql + "] as a static query: it contains bind variables");
}
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback, SqlProvider {
public Object doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
if (getFetchSize() > 0) {
stmt.setFetchSize(getFetchSize());
}
if (getMaxRows() > 0) {
stmt.setMaxRows(getMaxRows());
}
rs = stmt.executeQuery(sql);
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback());
}
public List query(String sql, RowCallbackHandler rch) throws DataAccessException {
return (List) query(sql, new RowCallbackHandlerResultSetExtractor(rch));
}
public List query(String sql, RowMapper rowMapper) throws DataAccessException {
return query(sql, new RowMapperResultReader(rowMapper));
}
public List queryForList(String sql) throws DataAccessException {
return (List) query(sql, new ListResultSetExtractor());
}
public Object queryForObject(String sql, Class requiredType) throws DataAccessException {
return query(sql, new ObjectResultSetExtractor(requiredType));
}
public long queryForLong(String sql) throws DataAccessException {
Number number = (Number) queryForObject(sql, Number.class);
return (number != null ? number.longValue() : 0);
}
public int queryForInt(String sql) throws DataAccessException {
Number number = (Number) queryForObject(sql, Number.class);
return (number != null ? number.intValue() : 0);
}
public int update(final String sql) throws DataAccessException {
if (sql == null) {
throw new InvalidDataAccessApiUsageException("SQL must not be null");
}
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL update [" + sql + "]");
}
class UpdateStatementCallback implements StatementCallback, SqlProvider {
public Object doInStatement(Statement stmt) throws SQLException {
int rows = stmt.executeUpdate(sql);
if (logger.isDebugEnabled()) {
logger.debug("SQL update affected " + rows + " rows");
}
return new Integer(rows);
}
public String getSql() {
return sql;
}
}
return ((Integer) execute(new UpdateStatementCallback())).intValue();
}
public int[] batchUpdate(final String[] sql) throws DataAccessException {
if (sql == null) {
throw new InvalidDataAccessApiUsageException("SQL must not be null");
}
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL batch update of " + sql.length + " statements");
}
class BatchUpdateStatementCallback implements StatementCallback, SqlProvider {
private String currSql;
public Object doInStatement(Statement stmt) throws SQLException, DataAccessException {
int[] rowsAffected = new int[sql.length];
if (JdbcUtils.supportsBatchUpdates(stmt.getConnection())) {
for (int i = 0; i < sql.length; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -