jdbcdaosupport.java_基于springframework的JdbcDaoSupport、JdbcTemplate实现JDBC数据库连接并实现常见增删改查的重写AbstractJdbc...

一、前言

基于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 getUnique(Class 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 get(Class type, String sql, Object[] values) {@b@return super.getJdbcTemplate().queryForObject(sql, values, RowMapperFactory.getRowMapper(type));@b@}@b@@b@public  List list(Class entityClass, String sql, Object... values){@b@return super.getJdbcTemplate().query(sql, values, RowMapperFactory.getRowMapper(entityClass));@b@}@b@@b@public  List listForPage(Class 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 T queryForObject(String sql, Object[] args, Class requiredType) {@b@return getJdbcTemplate().queryForObject(sql, args, requiredType);@b@}@b@@b@public List> queryForMultiFields(String sql,@b@Object[] values) {@b@return getJdbcTemplate().queryForList(sql, values);@b@}@b@@b@public List> 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  List queryForList(Class type, String sql) {@b@return getJdbcTemplate().queryForList(sql, type);@b@}@b@@b@public  List queryForList(Class 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@ * 

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@ * 

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@ * 

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@ * 

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@ * 

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@ * 

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@ * 

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@ * 

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@ * 

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