001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jdbc.datasource;
018:
019: import java.sql.Connection;
020: import java.sql.SQLException;
021:
022: import org.springframework.transaction.support.ResourceHolderSupport;
023: import org.springframework.util.Assert;
024:
025: /**
026: * Connection holder, wrapping a JDBC Connection.
027: * {@link DataSourceTransactionManager} binds instances of this class
028: * to the thread, for a specific DataSource.
029: *
030: * <p>Inherits rollback-only support for nested JDBC transactions
031: * and reference count functionality from the base class.
032: *
033: * <p>Note: This is an SPI class, not intended to be used by applications.
034: *
035: * @author Juergen Hoeller
036: * @since 06.05.2003
037: * @see DataSourceTransactionManager
038: * @see DataSourceUtils
039: */
040: public class ConnectionHolder extends ResourceHolderSupport {
041:
042: public static final String SAVEPOINT_NAME_PREFIX = "SAVEPOINT_";
043:
044: private ConnectionHandle connectionHandle;
045:
046: private Connection currentConnection;
047:
048: private boolean transactionActive = false;
049:
050: private Boolean savepointsSupported;
051:
052: private int savepointCounter = 0;
053:
054: /**
055: * Create a new ConnectionHolder for the given ConnectionHandle.
056: * @param connectionHandle the ConnectionHandle to hold
057: */
058: public ConnectionHolder(ConnectionHandle connectionHandle) {
059: Assert.notNull(connectionHandle,
060: "ConnectionHandle must not be null");
061: this .connectionHandle = connectionHandle;
062: }
063:
064: /**
065: * Create a new ConnectionHolder for the given JDBC Connection,
066: * wrapping it with a {@link SimpleConnectionHandle},
067: * assuming that there is no ongoing transaction.
068: * @param connection the JDBC Connection to hold
069: * @see SimpleConnectionHandle
070: * @see #ConnectionHolder(java.sql.Connection, boolean)
071: */
072: public ConnectionHolder(Connection connection) {
073: this .connectionHandle = new SimpleConnectionHandle(connection);
074: }
075:
076: /**
077: * Create a new ConnectionHolder for the given JDBC Connection,
078: * wrapping it with a {@link SimpleConnectionHandle}.
079: * @param connection the JDBC Connection to hold
080: * @param transactionActive whether the given Connection is involved
081: * in an ongoing transaction
082: * @see SimpleConnectionHandle
083: */
084: public ConnectionHolder(Connection connection,
085: boolean transactionActive) {
086: this (connection);
087: this .transactionActive = transactionActive;
088: }
089:
090: /**
091: * Return the ConnectionHandle held by this ConnectionHolder.
092: */
093: public ConnectionHandle getConnectionHandle() {
094: return this .connectionHandle;
095: }
096:
097: /**
098: * Return whether this holder currently has a Connection.
099: */
100: protected boolean hasConnection() {
101: return (this .connectionHandle != null);
102: }
103:
104: /**
105: * Set whether this holder represents an active, JDBC-managed transaction.
106: * @see DataSourceTransactionManager
107: */
108: protected void setTransactionActive(boolean transactionActive) {
109: this .transactionActive = transactionActive;
110: }
111:
112: /**
113: * Return whether this holder represents an active, JDBC-managed transaction.
114: */
115: protected boolean isTransactionActive() {
116: return this .transactionActive;
117: }
118:
119: /**
120: * Override the existing Connection handle with the given Connection.
121: * Reset the handle if given <code>null</code>.
122: * <p>Used for releasing the Connection on suspend (with a <code>null</code>
123: * argument) and setting a fresh Connection on resume.
124: */
125: protected void setConnection(Connection connection) {
126: if (this .currentConnection != null) {
127: this .connectionHandle
128: .releaseConnection(this .currentConnection);
129: this .currentConnection = null;
130: }
131: if (connection != null) {
132: this .connectionHandle = new SimpleConnectionHandle(
133: connection);
134: } else {
135: this .connectionHandle = null;
136: }
137: }
138:
139: /**
140: * Return the current Connection held by this ConnectionHolder.
141: * <p>This will be the same Connection until <code>released</code>
142: * gets called on the ConnectionHolder, which will reset the
143: * held Connection, fetching a new Connection on demand.
144: * @see ConnectionHandle#getConnection()
145: * @see #released()
146: */
147: public Connection getConnection() {
148: Assert.notNull(this .connectionHandle,
149: "Active Connection is required");
150: if (this .currentConnection == null) {
151: this .currentConnection = this .connectionHandle
152: .getConnection();
153: }
154: return this .currentConnection;
155: }
156:
157: /**
158: * Return whether JDBC 3.0 Savepoints are supported.
159: * Caches the flag for the lifetime of this ConnectionHolder.
160: * @throws SQLException if thrown by the JDBC driver
161: */
162: public boolean supportsSavepoints() throws SQLException {
163: if (this .savepointsSupported == null) {
164: this .savepointsSupported = new Boolean(getConnection()
165: .getMetaData().supportsSavepoints());
166: }
167: return this .savepointsSupported.booleanValue();
168: }
169:
170: /**
171: * Create a new JDBC 3.0 Savepoint for the current Connection,
172: * using generated savepoint names that are unique for the Connection.
173: * @return the new Savepoint (typed as Object for JDK 1.3 compatibility)
174: * @throws SQLException if thrown by the JDBC driver
175: */
176: public Object createSavepoint() throws SQLException {
177: this .savepointCounter++;
178: return getConnection().setSavepoint(
179: SAVEPOINT_NAME_PREFIX + this .savepointCounter);
180: }
181:
182: /**
183: * Releases the current Connection held by this ConnectionHolder.
184: * <p>This is necessary for ConnectionHandles that expect "Connection borrowing",
185: * where each returned Connection is only temporarily leased and needs to be
186: * returned once the data operation is done, to make the Connection available
187: * for other operations within the same transaction. This is the case with
188: * JDO 2.0 DataStoreConnections, for example.
189: * @see org.springframework.orm.jdo.DefaultJdoDialect#getJdbcConnection
190: */
191: public void released() {
192: super .released();
193: if (this .currentConnection != null) {
194: this .connectionHandle
195: .releaseConnection(this .currentConnection);
196: this .currentConnection = null;
197: }
198: }
199:
200: public void clear() {
201: super .clear();
202: this .transactionActive = false;
203: this .savepointsSupported = null;
204: this .savepointCounter = 0;
205: }
206:
207: }
|