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}