Open Source Repository

Home /spring/spring-jdbc-3.0.5 | Repository Home



org/springframework/jdbc/datasource/ConnectionHolder.java
/*
 * Copyright 2002-2010 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.datasource;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Savepoint;

import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.util.Assert;

/**
 * Connection holder, wrapping a JDBC Connection.
 {@link DataSourceTransactionManager} binds instances of this class
 * to the thread, for a specific DataSource.
 *
 <p>Inherits rollback-only support for nested JDBC transactions
 * and reference count functionality from the base class.
 *
 <p>Note: This is an SPI class, not intended to be used by applications.
 *
 @author Juergen Hoeller
 @since 06.05.2003
 @see DataSourceTransactionManager
 @see DataSourceUtils
 */
public class ConnectionHolder extends ResourceHolderSupport {

  public static final String SAVEPOINT_NAME_PREFIX = "SAVEPOINT_";


  private ConnectionHandle connectionHandle;

  private Connection currentConnection;

  private boolean transactionActive = false;

  private Boolean savepointsSupported;

  private int savepointCounter = 0;


  /**
   * Create a new ConnectionHolder for the given ConnectionHandle.
   @param connectionHandle the ConnectionHandle to hold
   */
  public ConnectionHolder(ConnectionHandle connectionHandle) {
    Assert.notNull(connectionHandle, "ConnectionHandle must not be null");
    this.connectionHandle = connectionHandle;
  }

  /**
   * Create a new ConnectionHolder for the given JDBC Connection,
   * wrapping it with a {@link SimpleConnectionHandle},
   * assuming that there is no ongoing transaction.
   @param connection the JDBC Connection to hold
   @see SimpleConnectionHandle
   @see #ConnectionHolder(java.sql.Connection, boolean)
   */
  public ConnectionHolder(Connection connection) {
    this.connectionHandle = new SimpleConnectionHandle(connection);
  }

  /**
   * Create a new ConnectionHolder for the given JDBC Connection,
   * wrapping it with a {@link SimpleConnectionHandle}.
   @param connection the JDBC Connection to hold
   @param transactionActive whether the given Connection is involved
   * in an ongoing transaction
   @see SimpleConnectionHandle
   */
  public ConnectionHolder(Connection connection, boolean transactionActive) {
    this(connection);
    this.transactionActive = transactionActive;
  }


  /**
   * Return the ConnectionHandle held by this ConnectionHolder.
   */
  public ConnectionHandle getConnectionHandle() {
    return this.connectionHandle;
  }

  /**
   * Return whether this holder currently has a Connection.
   */
  protected boolean hasConnection() {
    return (this.connectionHandle != null);
  }

  /**
   * Set whether this holder represents an active, JDBC-managed transaction.
   @see DataSourceTransactionManager
   */
  protected void setTransactionActive(boolean transactionActive) {
    this.transactionActive = transactionActive;
  }

  /**
   * Return whether this holder represents an active, JDBC-managed transaction.
   */
  protected boolean isTransactionActive() {
    return this.transactionActive;
  }


  /**
   * Override the existing Connection handle with the given Connection.
   * Reset the handle if given <code>null</code>.
   <p>Used for releasing the Connection on suspend (with a <code>null</code>
   * argument) and setting a fresh Connection on resume.
   */
  protected void setConnection(Connection connection) {
    if (this.currentConnection != null) {
      this.connectionHandle.releaseConnection(this.currentConnection);
      this.currentConnection = null;
    }
    if (connection != null) {
      this.connectionHandle = new SimpleConnectionHandle(connection);
    }
    else {
      this.connectionHandle = null;
    }
  }

  /**
   * Return the current Connection held by this ConnectionHolder.
   <p>This will be the same Connection until <code>released</code>
   * gets called on the ConnectionHolder, which will reset the
   * held Connection, fetching a new Connection on demand.
   @see ConnectionHandle#getConnection()
   @see #released()
   */
  public Connection getConnection() {
    Assert.notNull(this.connectionHandle, "Active Connection is required");
    if (this.currentConnection == null) {
      this.currentConnection = this.connectionHandle.getConnection();
    }
    return this.currentConnection;
  }

  /**
   * Return whether JDBC 3.0 Savepoints are supported.
   * Caches the flag for the lifetime of this ConnectionHolder.
   @throws SQLException if thrown by the JDBC driver
   */
  public boolean supportsSavepoints() throws SQLException {
    if (this.savepointsSupported == null) {
      this.savepointsSupported = new Boolean(getConnection().getMetaData().supportsSavepoints());
    }
    return this.savepointsSupported.booleanValue();
  }

  /**
   * Create a new JDBC 3.0 Savepoint for the current Connection,
   * using generated savepoint names that are unique for the Connection.
   @return the new Savepoint
   @throws SQLException if thrown by the JDBC driver
   */
  public Savepoint createSavepoint() throws SQLException {
    this.savepointCounter++;
    return getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter);
  }

  /**
   * Releases the current Connection held by this ConnectionHolder.
   <p>This is necessary for ConnectionHandles that expect "Connection borrowing",
   * where each returned Connection is only temporarily leased and needs to be
   * returned once the data operation is done, to make the Connection available
   * for other operations within the same transaction. This is the case with
   * JDO 2.0 DataStoreConnections, for example.
   @see org.springframework.orm.jdo.DefaultJdoDialect#getJdbcConnection
   */
  @Override
  public void released() {
    super.released();
    if (!isOpen() && this.currentConnection != null) {
      this.connectionHandle.releaseConnection(this.currentConnection);
      this.currentConnection = null;
    }
  }


  @Override
  public void clear() {
    super.clear();
    this.transactionActive = false;
    this.savepointsSupported = null;
    this.savepointCounter = 0;
  }

}