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