首页

基于springframework的JdbcDaoSupport、JdbcTemplate实现JDBC数据库连接并实现常见增删改查的重写AbstractJdbcDao源码分享

标签:springframework,jdbc,JdbcDaoSupport,数据库连接,工具类,JdbcTemplate,DataSourceUtils,getConnection     发布时间:2017-11-29   

一、前言

基于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@}