一、前言
基于org.springframework.jdbc.core.support.JdbcDaoSupport(spring-jdbc-xx.jar),定义AbstractJdbcDao子类通过org.springframework.jdbc.core.JdbcTemplate的实现数据库jdbc获取连接,从而可以直接指向数据库sql语句来实现依赖sql功能的业务逻辑。
二、代码
1.AbstractJdbcDao类
import java.sql.Array;@b@import java.sql.PreparedStatement;@b@import java.sql.SQLException;@b@import java.util.List;@b@import java.util.Map;@b@@b@import org.springframework.dao.DataAccessException;@b@import org.springframework.jdbc.core.BatchPreparedStatementSetter;@b@import org.springframework.jdbc.core.JdbcTemplate;@b@import org.springframework.jdbc.core.PreparedStatementCallback;@b@import org.springframework.jdbc.core.support.JdbcDaoSupport;@b@@b@import com.woopa.common.util.ClassUtils;@b@@b@/**@b@ * @Date 2011-2-10 下午01:57:09@b@ * @version 1.0@b@ */@b@@SuppressWarnings({"unchecked", "rawtypes"})@b@public abstract class AbstractJdbcDao extends JdbcDaoSupport {@b@ @b@ JdbcTemplate template;@b@ @b@ protected void initDao(){@b@ template = super.getJdbcTemplate();@b@ }@b@ @b@ public <T> T getUnique(Class<T> type, String sql, Object[] values) {@b@ if(! ClassUtils.isMappingType(type)){@b@ return super.getJdbcTemplate().queryForObject(sql, values, type);@b@ }@b@ return super.getJdbcTemplate().queryForObject(sql, values, RowMapperFactory.getRowMapper(type));@b@ }@b@ @b@ public <T> T get(Class<T> type, String sql, Object[] values) {@b@ return super.getJdbcTemplate().queryForObject(sql, values, RowMapperFactory.getRowMapper(type));@b@ }@b@ @b@ public <T> List<T> list(Class<T> entityClass, String sql, Object... values){@b@ return super.getJdbcTemplate().query(sql, values, RowMapperFactory.getRowMapper(entityClass));@b@ }@b@ @b@ public <T> List<T> listForPage(Class<T> entityClass, int start, int limit,@b@ String sql, Object... values) {@b@ return super.getJdbcTemplate().query(wrapToPageSql(sql, start, limit),@b@ values, RowMapperFactory.getRowMapper(entityClass));@b@ }@b@ @b@ public boolean execute(String sql, Object value) {@b@ return execute(sql, new Object[]{value});@b@ }@b@@b@ public boolean execute(final String sql,final Object[] values) {@b@ return (Boolean)super.getJdbcTemplate().execute(sql, new PreparedStatementCallback(){@b@ @b@ public Object doInPreparedStatement(PreparedStatement ps)@b@ throws SQLException, DataAccessException {@b@ if(values != null){@b@ for(int i=0; i<values.length; i++){@b@ setValue(ps, values[i], i+1);@b@ }@b@ }@b@ return ps.execute();@b@ }@b@ @b@ });@b@ }@b@@b@ public void execute(String sql) {@b@ getJdbcTemplate().execute(sql);@b@ }@b@ @b@ public int[] batchUpdate(String sql,final Object[] values){@b@ return getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter(){@b@@b@ public int getBatchSize() {@b@ return values.length;@b@ }@b@@b@ public void setValues(PreparedStatement ps, int index)@b@ throws SQLException {@b@ Object value = values[index];@b@ if(value.getClass().isArray()){@b@ Object[] subValues = (Object[]) value;@b@ for(int i=0; i<subValues.length; i++){@b@ setValue(ps, subValues[i], i+1);@b@ }@b@ }else{@b@ setValue(ps, value, 1);@b@ }@b@ }@b@ @b@ });@b@ }@b@ @b@ protected abstract String wrapToPageSql(String sql, int start, int limit);@b@ @b@ /**@b@ * 根据值的定义类型调用PreparedStatement相应的set方法,将值加入到SQL参数中@b@ * @param ps PreparedStatement实例@b@ * @param value 准备加入到SQL参数中的值@b@ * @param index 当前参数索引@b@ * @throws SQLException@b@ */@b@ protected void setValue(PreparedStatement ps , Object value, int index)@b@ throws SQLException {@b@ if(value == null){@b@ ps.setNull(index, java.sql.Types.NULL);@b@ return;@b@ }@b@ Class type = value.getClass();@b@ if(ClassUtils.isPrimitiveWrapper(type)){@b@ type = ClassUtils.resolvePrimitiveClassName(type);@b@ }@b@ if(int.class.isAssignableFrom(type)){@b@ ps.setInt(index, (Integer)value);@b@ }else if(String.class.isAssignableFrom(type)){@b@ ps.setString(index, (String)value);@b@ logger.debug(value);@b@ }else if(long.class.isAssignableFrom(type)){@b@ ps.setLong(index, (Long)value);@b@ }else if(boolean.class.isAssignableFrom(type)){@b@ ps.setBoolean(index, (Boolean)value);@b@ }else if(double.class.isAssignableFrom(type)){@b@ ps.setDouble(index, (Double)value);@b@ }else if(char.class.isAssignableFrom(type)){@b@ ps.setString(index, (Character)value+"");@b@ }else if(short.class.isAssignableFrom(type)){@b@ ps.setShort(index, (Short)value);@b@ }else if(float.class.isAssignableFrom(type)){@b@ ps.setFloat(index, (Float)value);@b@ }else if(byte.class.isAssignableFrom(type)){@b@ ps.setByte(index, (Byte)value);@b@ }else if(type.isArray()){@b@ ps.setArray(index, (Array)value);@b@ }else if(value instanceof java.sql.Timestamp){@b@ ps.setTimestamp(index, (java.sql.Timestamp) value);@b@ }else if(value instanceof java.sql.Date){@b@ ps.setDate(index, (java.sql.Date)value );@b@ }else if(value instanceof java.util.Date){@b@ ps.setDate(index, new java.sql.Date( ((java.util.Date)value).getTime() ));@b@ }else if(value instanceof java.sql.Time){@b@ ps.setTime(index, (java.sql.Time)value );@b@ }@b@ }@b@ @b@ /*------------兼容就版本asc-common1.1.2.RC------2013-5-31------chenzhenling---------------*/@b@ @b@ public long queryForLong(String sql, Object... value) {@b@ return getJdbcTemplate().queryForObject(sql, value, Long.class);@b@ }@b@ @b@ public <T> T queryForObject(String sql, Object[] args, Class<T> requiredType) {@b@ return getJdbcTemplate().queryForObject(sql, args, requiredType);@b@ }@b@ @b@ public List<Map<String, Object>> queryForMultiFields(String sql,@b@ Object[] values) {@b@ return getJdbcTemplate().queryForList(sql, values);@b@ }@b@@b@ public List<Map<String, Object>> queryForMultiFields(String sql) {@b@ return getJdbcTemplate().queryForList(sql);@b@ }@b@@b@ public int queryForInt(String sql, Object... value) {@b@ return getJdbcTemplate().queryForObject(sql, value, Integer.class);@b@ }@b@@b@ public int queryForInt(String sql) {@b@ return getJdbcTemplate().queryForObject(sql, Integer.class);@b@ }@b@ @b@ public <T> List<T> queryForList(Class<T> type, String sql) {@b@ return getJdbcTemplate().queryForList(sql, type);@b@ }@b@@b@ public <T> List<T> queryForList(Class<T> type, String sql,@b@ Object[] values) {@b@ return getJdbcTemplate().queryForList(sql, values, type);@b@ }@b@ @b@}
2.org.springframework.jdbc.core.support.JdbcDaoSupport源码
package org.springframework.jdbc.core.support;@b@@b@import java.sql.Connection;@b@@b@import javax.sql.DataSource;@b@@b@import org.springframework.dao.support.DaoSupport;@b@import org.springframework.jdbc.CannotGetJdbcConnectionException;@b@import org.springframework.jdbc.core.JdbcTemplate;@b@import org.springframework.jdbc.datasource.DataSourceUtils;@b@import org.springframework.jdbc.support.SQLExceptionTranslator;@b@@b@/**@b@ * Convenient super class for JDBC-based data access objects.@b@ *@b@ * <p>Requires a {@link javax.sql.DataSource} to be set, providing a@b@ * {@link org.springframework.jdbc.core.JdbcTemplate} based on it to@b@ * subclasses through the {@link #getJdbcTemplate()} method.@b@ *@b@ * <p>This base class is mainly intended for JdbcTemplate usage but can@b@ * also be used when working with a Connection directly or when using@b@ * {@code org.springframework.jdbc.object} operation objects.@b@ *@b@ * @author Juergen Hoeller@b@ * @since 28.07.2003@b@ * @see #setDataSource@b@ * @see #getJdbcTemplate@b@ * @see org.springframework.jdbc.core.JdbcTemplate@b@ */@b@public abstract class JdbcDaoSupport extends DaoSupport {@b@@b@ private JdbcTemplate jdbcTemplate;@b@@b@@b@ /**@b@ * Set the JDBC DataSource to be used by this DAO.@b@ */@b@ public final void setDataSource(DataSource dataSource) {@b@ if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {@b@ this.jdbcTemplate = createJdbcTemplate(dataSource);@b@ initTemplateConfig();@b@ }@b@ }@b@@b@ /**@b@ * Create a JdbcTemplate for the given DataSource.@b@ * Only invoked if populating the DAO with a DataSource reference!@b@ * <p>Can be overridden in subclasses to provide a JdbcTemplate instance@b@ * with different configuration, or a custom JdbcTemplate subclass.@b@ * @param dataSource the JDBC DataSource to create a JdbcTemplate for@b@ * @return the new JdbcTemplate instance@b@ * @see #setDataSource@b@ */@b@ protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {@b@ return new JdbcTemplate(dataSource);@b@ }@b@@b@ /**@b@ * Return the JDBC DataSource used by this DAO.@b@ */@b@ public final DataSource getDataSource() {@b@ return (this.jdbcTemplate != null ? this.jdbcTemplate.getDataSource() : null);@b@ }@b@@b@ /**@b@ * Set the JdbcTemplate for this DAO explicitly,@b@ * as an alternative to specifying a DataSource.@b@ */@b@ public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {@b@ this.jdbcTemplate = jdbcTemplate;@b@ initTemplateConfig();@b@ }@b@@b@ /**@b@ * Return the JdbcTemplate for this DAO,@b@ * pre-initialized with the DataSource or set explicitly.@b@ */@b@ public final JdbcTemplate getJdbcTemplate() {@b@ return this.jdbcTemplate;@b@ }@b@@b@ /**@b@ * Initialize the template-based configuration of this DAO.@b@ * Called after a new JdbcTemplate has been set, either directly@b@ * or through a DataSource.@b@ * <p>This implementation is empty. Subclasses may override this@b@ * to configure further objects based on the JdbcTemplate.@b@ * @see #getJdbcTemplate()@b@ */@b@ protected void initTemplateConfig() {@b@ }@b@@b@ @Override@b@ protected void checkDaoConfig() {@b@ if (this.jdbcTemplate == null) {@b@ throw new IllegalArgumentException("'dataSource' or 'jdbcTemplate' is required");@b@ }@b@ }@b@@b@@b@ /**@b@ * Return the SQLExceptionTranslator of this DAO's JdbcTemplate,@b@ * for translating SQLExceptions in custom JDBC access code.@b@ * @see org.springframework.jdbc.core.JdbcTemplate#getExceptionTranslator()@b@ */@b@ protected final SQLExceptionTranslator getExceptionTranslator() {@b@ return getJdbcTemplate().getExceptionTranslator();@b@ }@b@@b@ /**@b@ * Get a JDBC Connection, either from the current transaction or a new one.@b@ * @return the JDBC Connection@b@ * @throws CannotGetJdbcConnectionException if the attempt to get a Connection failed@b@ * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection(javax.sql.DataSource)@b@ */@b@ protected final Connection getConnection() throws CannotGetJdbcConnectionException {@b@ return DataSourceUtils.getConnection(getDataSource());@b@ }@b@@b@ /**@b@ * Close the given JDBC Connection, created via this DAO's DataSource,@b@ * if it isn't bound to the thread.@b@ * @param con Connection to close@b@ * @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection@b@ */@b@ protected final void releaseConnection(Connection con) {@b@ DataSourceUtils.releaseConnection(con, getDataSource());@b@ }@b@@b@}
3.org.springframework.jdbc.datasource.DataSourceUtils源码
package org.springframework.jdbc.datasource;@b@@b@import java.sql.Connection;@b@import java.sql.SQLException;@b@import java.sql.Statement;@b@import javax.sql.DataSource;@b@@b@import org.apache.commons.logging.Log;@b@import org.apache.commons.logging.LogFactory;@b@@b@import org.springframework.jdbc.CannotGetJdbcConnectionException;@b@import org.springframework.transaction.TransactionDefinition;@b@import org.springframework.transaction.support.TransactionSynchronizationAdapter;@b@import org.springframework.transaction.support.TransactionSynchronizationManager;@b@import org.springframework.util.Assert;@b@@b@/**@b@ * Helper class that provides static methods for obtaining JDBC Connections from@b@ * a {@link javax.sql.DataSource}. Includes special support for Spring-managed@b@ * transactional Connections, e.g. managed by {@link DataSourceTransactionManager}@b@ * or {@link org.springframework.transaction.jta.JtaTransactionManager}.@b@ *@b@ * <p>Used internally by Spring's {@link org.springframework.jdbc.core.JdbcTemplate},@b@ * Spring's JDBC operation objects and the JDBC {@link DataSourceTransactionManager}.@b@ * Can also be used directly in application code.@b@ *@b@ * @author Rod Johnson@b@ * @author Juergen Hoeller@b@ * @see #getConnection@b@ * @see #releaseConnection@b@ * @see DataSourceTransactionManager@b@ * @see org.springframework.transaction.jta.JtaTransactionManager@b@ * @see org.springframework.transaction.support.TransactionSynchronizationManager@b@ */@b@public abstract class DataSourceUtils {@b@@b@ /**@b@ * Order value for TransactionSynchronization objects that clean up JDBC Connections.@b@ */@b@ public static final int CONNECTION_SYNCHRONIZATION_ORDER = 1000;@b@@b@ private static final Log logger = LogFactory.getLog(DataSourceUtils.class);@b@@b@@b@ /**@b@ * Obtain a Connection from the given DataSource. Translates SQLExceptions into@b@ * the Spring hierarchy of unchecked generic data access exceptions, simplifying@b@ * calling code and making any exception that is thrown more meaningful.@b@ * <p>Is aware of a corresponding Connection bound to the current thread, for example@b@ * when using {@link DataSourceTransactionManager}. Will bind a Connection to the@b@ * thread if transaction synchronization is active, e.g. when running within a@b@ * {@link org.springframework.transaction.jta.JtaTransactionManager JTA} transaction).@b@ * @param dataSource the DataSource to obtain Connections from@b@ * @return a JDBC Connection from the given DataSource@b@ * @throws org.springframework.jdbc.CannotGetJdbcConnectionException@b@ * if the attempt to get a Connection failed@b@ * @see #releaseConnection@b@ */@b@ public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {@b@ try {@b@ return doGetConnection(dataSource);@b@ }@b@ catch (SQLException ex) {@b@ throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);@b@ }@b@ }@b@@b@ /**@b@ * Actually obtain a JDBC Connection from the given DataSource.@b@ * Same as {@link #getConnection}, but throwing the original SQLException.@b@ * <p>Is aware of a corresponding Connection bound to the current thread, for example@b@ * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread@b@ * if transaction synchronization is active (e.g. if in a JTA transaction).@b@ * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.@b@ * @param dataSource the DataSource to obtain Connections from@b@ * @return a JDBC Connection from the given DataSource@b@ * @throws SQLException if thrown by JDBC methods@b@ * @see #doReleaseConnection@b@ */@b@ public static Connection doGetConnection(DataSource dataSource) throws SQLException {@b@ Assert.notNull(dataSource, "No DataSource specified");@b@@b@ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);@b@ if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {@b@ conHolder.requested();@b@ if (!conHolder.hasConnection()) {@b@ logger.debug("Fetching resumed JDBC Connection from DataSource");@b@ conHolder.setConnection(dataSource.getConnection());@b@ }@b@ return conHolder.getConnection();@b@ }@b@ // Else we either got no holder or an empty thread-bound holder here.@b@@b@ logger.debug("Fetching JDBC Connection from DataSource");@b@ Connection con = dataSource.getConnection();@b@@b@ if (TransactionSynchronizationManager.isSynchronizationActive()) {@b@ logger.debug("Registering transaction synchronization for JDBC Connection");@b@ // Use same Connection for further JDBC actions within the transaction.@b@ // Thread-bound object will get removed by synchronization at transaction completion.@b@ ConnectionHolder holderToUse = conHolder;@b@ if (holderToUse == null) {@b@ holderToUse = new ConnectionHolder(con);@b@ }@b@ else {@b@ holderToUse.setConnection(con);@b@ }@b@ holderToUse.requested();@b@ TransactionSynchronizationManager.registerSynchronization(@b@ new ConnectionSynchronization(holderToUse, dataSource));@b@ holderToUse.setSynchronizedWithTransaction(true);@b@ if (holderToUse != conHolder) {@b@ TransactionSynchronizationManager.bindResource(dataSource, holderToUse);@b@ }@b@ }@b@@b@ return con;@b@ }@b@@b@ /**@b@ * Prepare the given Connection with the given transaction semantics.@b@ * @param con the Connection to prepare@b@ * @param definition the transaction definition to apply@b@ * @return the previous isolation level, if any@b@ * @throws SQLException if thrown by JDBC methods@b@ * @see #resetConnectionAfterTransaction@b@ */@b@ public static Integer prepareConnectionForTransaction(Connection con, TransactionDefinition definition)@b@ throws SQLException {@b@@b@ Assert.notNull(con, "No Connection specified");@b@@b@ // Set read-only flag.@b@ if (definition != null && definition.isReadOnly()) {@b@ try {@b@ if (logger.isDebugEnabled()) {@b@ logger.debug("Setting JDBC Connection [" + con + "] read-only");@b@ }@b@ con.setReadOnly(true);@b@ }@b@ catch (SQLException ex) {@b@ Throwable exToCheck = ex;@b@ while (exToCheck != null) {@b@ if (exToCheck.getClass().getSimpleName().contains("Timeout")) {@b@ // Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0@b@ throw ex;@b@ }@b@ exToCheck = exToCheck.getCause();@b@ }@b@ // "read-only not supported" SQLException -> ignore, it's just a hint anyway@b@ logger.debug("Could not set JDBC Connection read-only", ex);@b@ }@b@ catch (RuntimeException ex) {@b@ Throwable exToCheck = ex;@b@ while (exToCheck != null) {@b@ if (exToCheck.getClass().getSimpleName().contains("Timeout")) {@b@ // Assume it's a connection timeout that would otherwise get lost: e.g. from Hibernate@b@ throw ex;@b@ }@b@ exToCheck = exToCheck.getCause();@b@ }@b@ // "read-only not supported" UnsupportedOperationException -> ignore, it's just a hint anyway@b@ logger.debug("Could not set JDBC Connection read-only", ex);@b@ }@b@ }@b@@b@ // Apply specific isolation level, if any.@b@ Integer previousIsolationLevel = null;@b@ if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {@b@ if (logger.isDebugEnabled()) {@b@ logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +@b@ definition.getIsolationLevel());@b@ }@b@ int currentIsolation = con.getTransactionIsolation();@b@ if (currentIsolation != definition.getIsolationLevel()) {@b@ previousIsolationLevel = currentIsolation;@b@ con.setTransactionIsolation(definition.getIsolationLevel());@b@ }@b@ }@b@@b@ return previousIsolationLevel;@b@ }@b@@b@ /**@b@ * Reset the given Connection after a transaction,@b@ * regarding read-only flag and isolation level.@b@ * @param con the Connection to reset@b@ * @param previousIsolationLevel the isolation level to restore, if any@b@ * @see #prepareConnectionForTransaction@b@ */@b@ public static void resetConnectionAfterTransaction(Connection con, Integer previousIsolationLevel) {@b@ Assert.notNull(con, "No Connection specified");@b@ try {@b@ // Reset transaction isolation to previous value, if changed for the transaction.@b@ if (previousIsolationLevel != null) {@b@ if (logger.isDebugEnabled()) {@b@ logger.debug("Resetting isolation level of JDBC Connection [" +@b@ con + "] to " + previousIsolationLevel);@b@ }@b@ con.setTransactionIsolation(previousIsolationLevel);@b@ }@b@@b@ // Reset read-only flag.@b@ if (con.isReadOnly()) {@b@ if (logger.isDebugEnabled()) {@b@ logger.debug("Resetting read-only flag of JDBC Connection [" + con + "]");@b@ }@b@ con.setReadOnly(false);@b@ }@b@ }@b@ catch (Throwable ex) {@b@ logger.debug("Could not reset JDBC Connection after transaction", ex);@b@ }@b@ }@b@@b@ /**@b@ * Determine whether the given JDBC Connection is transactional, that is,@b@ * bound to the current thread by Spring's transaction facilities.@b@ * @param con the Connection to check@b@ * @param dataSource the DataSource that the Connection was obtained from@b@ * (may be {@code null})@b@ * @return whether the Connection is transactional@b@ */@b@ public static boolean isConnectionTransactional(Connection con, DataSource dataSource) {@b@ if (dataSource == null) {@b@ return false;@b@ }@b@ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);@b@ return (conHolder != null && connectionEquals(conHolder, con));@b@ }@b@@b@ /**@b@ * Apply the current transaction timeout, if any,@b@ * to the given JDBC Statement object.@b@ * @param stmt the JDBC Statement object@b@ * @param dataSource the DataSource that the Connection was obtained from@b@ * @throws SQLException if thrown by JDBC methods@b@ * @see java.sql.Statement#setQueryTimeout@b@ */@b@ public static void applyTransactionTimeout(Statement stmt, DataSource dataSource) throws SQLException {@b@ applyTimeout(stmt, dataSource, 0);@b@ }@b@@b@ /**@b@ * Apply the specified timeout - overridden by the current transaction timeout,@b@ * if any - to the given JDBC Statement object.@b@ * @param stmt the JDBC Statement object@b@ * @param dataSource the DataSource that the Connection was obtained from@b@ * @param timeout the timeout to apply (or 0 for no timeout outside of a transaction)@b@ * @throws SQLException if thrown by JDBC methods@b@ * @see java.sql.Statement#setQueryTimeout@b@ */@b@ public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {@b@ Assert.notNull(stmt, "No Statement specified");@b@ Assert.notNull(dataSource, "No DataSource specified");@b@ ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);@b@ if (holder != null && holder.hasTimeout()) {@b@ // Remaining transaction timeout overrides specified value.@b@ stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());@b@ }@b@ else if (timeout > 0) {@b@ // No current transaction timeout -> apply specified value.@b@ stmt.setQueryTimeout(timeout);@b@ }@b@ }@b@@b@ /**@b@ * Close the given Connection, obtained from the given DataSource,@b@ * if it is not managed externally (that is, not bound to the thread).@b@ * @param con the Connection to close if necessary@b@ * (if this is {@code null}, the call will be ignored)@b@ * @param dataSource the DataSource that the Connection was obtained from@b@ * (may be {@code null})@b@ * @see #getConnection@b@ */@b@ public static void releaseConnection(Connection con, DataSource dataSource) {@b@ try {@b@ doReleaseConnection(con, dataSource);@b@ }@b@ catch (SQLException ex) {@b@ logger.debug("Could not close JDBC Connection", ex);@b@ }@b@ catch (Throwable ex) {@b@ logger.debug("Unexpected exception on closing JDBC Connection", ex);@b@ }@b@ }@b@@b@ /**@b@ * Actually close the given Connection, obtained from the given DataSource.@b@ * Same as {@link #releaseConnection}, but throwing the original SQLException.@b@ * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.@b@ * @param con the Connection to close if necessary@b@ * (if this is {@code null}, the call will be ignored)@b@ * @param dataSource the DataSource that the Connection was obtained from@b@ * (may be {@code null})@b@ * @throws SQLException if thrown by JDBC methods@b@ * @see #doGetConnection@b@ */@b@ public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {@b@ if (con == null) {@b@ return;@b@ }@b@ if (dataSource != null) {@b@ ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);@b@ if (conHolder != null && connectionEquals(conHolder, con)) {@b@ // It's the transactional Connection: Don't close it.@b@ conHolder.released();@b@ return;@b@ }@b@ }@b@ logger.debug("Returning JDBC Connection to DataSource");@b@ doCloseConnection(con, dataSource);@b@ }@b@@b@ /**@b@ * Close the Connection, unless a {@link SmartDataSource} doesn't want us to.@b@ * @param con the Connection to close if necessary@b@ * @param dataSource the DataSource that the Connection was obtained from@b@ * @throws SQLException if thrown by JDBC methods@b@ * @see Connection#close()@b@ * @see SmartDataSource#shouldClose(Connection)@b@ */@b@ public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {@b@ if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {@b@ con.close();@b@ }@b@ }@b@@b@ /**@b@ * Determine whether the given two Connections are equal, asking the target@b@ * Connection in case of a proxy. Used to detect equality even if the@b@ * user passed in a raw target Connection while the held one is a proxy.@b@ * @param conHolder the ConnectionHolder for the held Connection (potentially a proxy)@b@ * @param passedInCon the Connection passed-in by the user@b@ * (potentially a target Connection without proxy)@b@ * @return whether the given Connections are equal@b@ * @see #getTargetConnection@b@ */@b@ private static boolean connectionEquals(ConnectionHolder conHolder, Connection passedInCon) {@b@ if (!conHolder.hasConnection()) {@b@ return false;@b@ }@b@ Connection heldCon = conHolder.getConnection();@b@ // Explicitly check for identity too: for Connection handles that do not implement@b@ // "equals" properly, such as the ones Commons DBCP exposes).@b@ return (heldCon == passedInCon || heldCon.equals(passedInCon) ||@b@ getTargetConnection(heldCon).equals(passedInCon));@b@ }@b@@b@ /**@b@ * Return the innermost target Connection of the given Connection. If the given@b@ * Connection is a proxy, it will be unwrapped until a non-proxy Connection is@b@ * found. Otherwise, the passed-in Connection will be returned as-is.@b@ * @param con the Connection proxy to unwrap@b@ * @return the innermost target Connection, or the passed-in one if no proxy@b@ * @see ConnectionProxy#getTargetConnection()@b@ */@b@ public static Connection getTargetConnection(Connection con) {@b@ Connection conToUse = con;@b@ while (conToUse instanceof ConnectionProxy) {@b@ conToUse = ((ConnectionProxy) conToUse).getTargetConnection();@b@ }@b@ return conToUse;@b@ }@b@@b@ /**@b@ * Determine the connection synchronization order to use for the given@b@ * DataSource. Decreased for every level of nesting that a DataSource@b@ * has, checked through the level of DelegatingDataSource nesting.@b@ * @param dataSource the DataSource to check@b@ * @return the connection synchronization order to use@b@ * @see #CONNECTION_SYNCHRONIZATION_ORDER@b@ */@b@ private static int getConnectionSynchronizationOrder(DataSource dataSource) {@b@ int order = CONNECTION_SYNCHRONIZATION_ORDER;@b@ DataSource currDs = dataSource;@b@ while (currDs instanceof DelegatingDataSource) {@b@ order--;@b@ currDs = ((DelegatingDataSource) currDs).getTargetDataSource();@b@ }@b@ return order;@b@ }@b@@b@@b@ /**@b@ * Callback for resource cleanup at the end of a non-native JDBC transaction@b@ * (e.g. when participating in a JtaTransactionManager transaction).@b@ * @see org.springframework.transaction.jta.JtaTransactionManager@b@ */@b@ private static class ConnectionSynchronization extends TransactionSynchronizationAdapter {@b@@b@ private final ConnectionHolder connectionHolder;@b@@b@ private final DataSource dataSource;@b@@b@ private int order;@b@@b@ private boolean holderActive = true;@b@@b@ public ConnectionSynchronization(ConnectionHolder connectionHolder, DataSource dataSource) {@b@ this.connectionHolder = connectionHolder;@b@ this.dataSource = dataSource;@b@ this.order = getConnectionSynchronizationOrder(dataSource);@b@ }@b@@b@ @Override@b@ public int getOrder() {@b@ return this.order;@b@ }@b@@b@ @Override@b@ public void suspend() {@b@ if (this.holderActive) {@b@ TransactionSynchronizationManager.unbindResource(this.dataSource);@b@ if (this.connectionHolder.hasConnection() && !this.connectionHolder.isOpen()) {@b@ // Release Connection on suspend if the application doesn't keep@b@ // a handle to it anymore. We will fetch a fresh Connection if the@b@ // application accesses the ConnectionHolder again after resume,@b@ // assuming that it will participate in the same transaction.@b@ releaseConnection(this.connectionHolder.getConnection(), this.dataSource);@b@ this.connectionHolder.setConnection(null);@b@ }@b@ }@b@ }@b@@b@ @Override@b@ public void resume() {@b@ if (this.holderActive) {@b@ TransactionSynchronizationManager.bindResource(this.dataSource, this.connectionHolder);@b@ }@b@ }@b@@b@ @Override@b@ public void beforeCompletion() {@b@ // Release Connection early if the holder is not open anymore@b@ // (that is, not used by another resource like a Hibernate Session@b@ // that has its own cleanup via transaction synchronization),@b@ // to avoid issues with strict JTA implementations that expect@b@ // the close call before transaction completion.@b@ if (!this.connectionHolder.isOpen()) {@b@ TransactionSynchronizationManager.unbindResource(this.dataSource);@b@ this.holderActive = false;@b@ if (this.connectionHolder.hasConnection()) {@b@ releaseConnection(this.connectionHolder.getConnection(), this.dataSource);@b@ }@b@ }@b@ }@b@@b@ @Override@b@ public void afterCompletion(int status) {@b@ // If we haven't closed the Connection in beforeCompletion,@b@ // close it now. The holder might have been used for other@b@ // cleanup in the meantime, for example by a Hibernate Session.@b@ if (this.holderActive) {@b@ // The thread-bound ConnectionHolder might not be available anymore,@b@ // since afterCompletion might get called from a different thread.@b@ TransactionSynchronizationManager.unbindResourceIfPossible(this.dataSource);@b@ this.holderActive = false;@b@ if (this.connectionHolder.hasConnection()) {@b@ releaseConnection(this.connectionHolder.getConnection(), this.dataSource);@b@ // Reset the ConnectionHolder: It might remain bound to the thread.@b@ this.connectionHolder.setConnection(null);@b@ }@b@ }@b@ this.connectionHolder.reset();@b@ }@b@ }@b@@b@}