001/*
002 * Copyright 2002-2017 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.SQLException;
020import java.sql.Savepoint;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.transaction.CannotCreateTransactionException;
026import org.springframework.transaction.NestedTransactionNotSupportedException;
027import org.springframework.transaction.SavepointManager;
028import org.springframework.transaction.TransactionException;
029import org.springframework.transaction.TransactionSystemException;
030import org.springframework.transaction.TransactionUsageException;
031import org.springframework.transaction.support.SmartTransactionObject;
032
033/**
034 * Convenient base class for JDBC-aware transaction objects. Can contain a
035 * {@link ConnectionHolder} with a JDBC {@code Connection}, and implements the
036 * {@link SavepointManager} interface based on that {@code ConnectionHolder}.
037 *
038 * <p>Allows for programmatic management of JDBC {@link java.sql.Savepoint Savepoints}.
039 * Spring's {@link org.springframework.transaction.support.DefaultTransactionStatus}
040 * automatically delegates to this, as it autodetects transaction objects which
041 * implement the {@link SavepointManager} interface.
042 *
043 * @author Juergen Hoeller
044 * @since 1.1
045 * @see DataSourceTransactionManager
046 */
047public abstract class JdbcTransactionObjectSupport implements SavepointManager, SmartTransactionObject {
048
049        private static final Log logger = LogFactory.getLog(JdbcTransactionObjectSupport.class);
050
051
052        private ConnectionHolder connectionHolder;
053
054        private Integer previousIsolationLevel;
055
056        private boolean savepointAllowed = false;
057
058
059        public void setConnectionHolder(ConnectionHolder connectionHolder) {
060                this.connectionHolder = connectionHolder;
061        }
062
063        public ConnectionHolder getConnectionHolder() {
064                return this.connectionHolder;
065        }
066
067        public boolean hasConnectionHolder() {
068                return (this.connectionHolder != null);
069        }
070
071        public void setPreviousIsolationLevel(Integer previousIsolationLevel) {
072                this.previousIsolationLevel = previousIsolationLevel;
073        }
074
075        public Integer getPreviousIsolationLevel() {
076                return this.previousIsolationLevel;
077        }
078
079        public void setSavepointAllowed(boolean savepointAllowed) {
080                this.savepointAllowed = savepointAllowed;
081        }
082
083        public boolean isSavepointAllowed() {
084                return this.savepointAllowed;
085        }
086
087        @Override
088        public void flush() {
089                // no-op
090        }
091
092
093        //---------------------------------------------------------------------
094        // Implementation of SavepointManager
095        //---------------------------------------------------------------------
096
097        /**
098         * This implementation creates a JDBC 3.0 Savepoint and returns it.
099         * @see java.sql.Connection#setSavepoint
100         */
101        @Override
102        public Object createSavepoint() throws TransactionException {
103                ConnectionHolder conHolder = getConnectionHolderForSavepoint();
104                try {
105                        if (!conHolder.supportsSavepoints()) {
106                                throw new NestedTransactionNotSupportedException(
107                                                "Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
108                        }
109                        return conHolder.createSavepoint();
110                }
111                catch (SQLException ex) {
112                        throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
113                }
114        }
115
116        /**
117         * This implementation rolls back to the given JDBC 3.0 Savepoint.
118         * @see java.sql.Connection#rollback(java.sql.Savepoint)
119         */
120        @Override
121        public void rollbackToSavepoint(Object savepoint) throws TransactionException {
122                ConnectionHolder conHolder = getConnectionHolderForSavepoint();
123                try {
124                        conHolder.getConnection().rollback((Savepoint) savepoint);
125                }
126                catch (Throwable ex) {
127                        throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
128                }
129        }
130
131        /**
132         * This implementation releases the given JDBC 3.0 Savepoint.
133         * @see java.sql.Connection#releaseSavepoint
134         */
135        @Override
136        public void releaseSavepoint(Object savepoint) throws TransactionException {
137                ConnectionHolder conHolder = getConnectionHolderForSavepoint();
138                try {
139                        conHolder.getConnection().releaseSavepoint((Savepoint) savepoint);
140                }
141                catch (Throwable ex) {
142                        logger.debug("Could not explicitly release JDBC savepoint", ex);
143                }
144        }
145
146        protected ConnectionHolder getConnectionHolderForSavepoint() throws TransactionException {
147                if (!isSavepointAllowed()) {
148                        throw new NestedTransactionNotSupportedException(
149                                        "Transaction manager does not allow nested transactions");
150                }
151                if (!hasConnectionHolder()) {
152                        throw new TransactionUsageException(
153                                        "Cannot create nested transaction when not exposing a JDBC transaction");
154                }
155                return getConnectionHolder();
156        }
157
158}