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