001/*
002 * Copyright 2002-2018 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.transaction.support;
018
019import java.lang.reflect.UndeclaredThrowableException;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024import org.springframework.beans.factory.InitializingBean;
025import org.springframework.transaction.PlatformTransactionManager;
026import org.springframework.transaction.TransactionDefinition;
027import org.springframework.transaction.TransactionException;
028import org.springframework.transaction.TransactionStatus;
029import org.springframework.transaction.TransactionSystemException;
030
031/**
032 * Template class that simplifies programmatic transaction demarcation and
033 * transaction exception handling.
034 *
035 * <p>The central method is {@link #execute}, supporting transactional code that
036 * implements the {@link TransactionCallback} interface. This template handles
037 * the transaction lifecycle and possible exceptions such that neither the
038 * TransactionCallback implementation nor the calling code needs to explicitly
039 * handle transactions.
040 *
041 * <p>Typical usage: Allows for writing low-level data access objects that use
042 * resources such as JDBC DataSources but are not transaction-aware themselves.
043 * Instead, they can implicitly participate in transactions handled by higher-level
044 * application services utilizing this class, making calls to the low-level
045 * services via an inner-class callback object.
046 *
047 * <p>Can be used within a service implementation via direct instantiation with
048 * a transaction manager reference, or get prepared in an application context
049 * and passed to services as bean reference. Note: The transaction manager should
050 * always be configured as bean in the application context: in the first case given
051 * to the service directly, in the second case given to the prepared template.
052 *
053 * <p>Supports setting the propagation behavior and the isolation level by name,
054 * for convenient configuration in context definitions.
055 *
056 * @author Juergen Hoeller
057 * @since 17.03.2003
058 * @see #execute
059 * @see #setTransactionManager
060 * @see org.springframework.transaction.PlatformTransactionManager
061 */
062@SuppressWarnings("serial")
063public class TransactionTemplate extends DefaultTransactionDefinition
064                implements TransactionOperations, InitializingBean {
065
066        /** Logger available to subclasses */
067        protected final Log logger = LogFactory.getLog(getClass());
068
069        private PlatformTransactionManager transactionManager;
070
071
072        /**
073         * Construct a new TransactionTemplate for bean usage.
074         * <p>Note: The PlatformTransactionManager needs to be set before
075         * any {@code execute} calls.
076         * @see #setTransactionManager
077         */
078        public TransactionTemplate() {
079        }
080
081        /**
082         * Construct a new TransactionTemplate using the given transaction manager.
083         * @param transactionManager the transaction management strategy to be used
084         */
085        public TransactionTemplate(PlatformTransactionManager transactionManager) {
086                this.transactionManager = transactionManager;
087        }
088
089        /**
090         * Construct a new TransactionTemplate using the given transaction manager,
091         * taking its default settings from the given transaction definition.
092         * @param transactionManager the transaction management strategy to be used
093         * @param transactionDefinition the transaction definition to copy the
094         * default settings from. Local properties can still be set to change values.
095         */
096        public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
097                super(transactionDefinition);
098                this.transactionManager = transactionManager;
099        }
100
101
102        /**
103         * Set the transaction management strategy to be used.
104         */
105        public void setTransactionManager(PlatformTransactionManager transactionManager) {
106                this.transactionManager = transactionManager;
107        }
108
109        /**
110         * Return the transaction management strategy to be used.
111         */
112        public PlatformTransactionManager getTransactionManager() {
113                return this.transactionManager;
114        }
115
116        @Override
117        public void afterPropertiesSet() {
118                if (this.transactionManager == null) {
119                        throw new IllegalArgumentException("Property 'transactionManager' is required");
120                }
121        }
122
123
124        @Override
125        public <T> T execute(TransactionCallback<T> action) throws TransactionException {
126                if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
127                        return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
128                }
129                else {
130                        TransactionStatus status = this.transactionManager.getTransaction(this);
131                        T result;
132                        try {
133                                result = action.doInTransaction(status);
134                        }
135                        catch (RuntimeException ex) {
136                                // Transactional code threw application exception -> rollback
137                                rollbackOnException(status, ex);
138                                throw ex;
139                        }
140                        catch (Error err) {
141                                // Transactional code threw error -> rollback
142                                rollbackOnException(status, err);
143                                throw err;
144                        }
145                        catch (Throwable ex) {
146                                // Transactional code threw unexpected exception -> rollback
147                                rollbackOnException(status, ex);
148                                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
149                        }
150                        this.transactionManager.commit(status);
151                        return result;
152                }
153        }
154
155        /**
156         * Perform a rollback, handling rollback exceptions properly.
157         * @param status object representing the transaction
158         * @param ex the thrown application exception or error
159         * @throws TransactionException in case of a rollback error
160         */
161        private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
162                logger.debug("Initiating transaction rollback on application exception", ex);
163                try {
164                        this.transactionManager.rollback(status);
165                }
166                catch (TransactionSystemException ex2) {
167                        logger.error("Application exception overridden by rollback exception", ex);
168                        ex2.initApplicationException(ex);
169                        throw ex2;
170                }
171                catch (RuntimeException ex2) {
172                        logger.error("Application exception overridden by rollback exception", ex);
173                        throw ex2;
174                }
175                catch (Error err) {
176                        logger.error("Application exception overridden by rollback error", ex);
177                        throw err;
178                }
179        }
180
181
182        @Override
183        public boolean equals(Object other) {
184                return (this == other || (super.equals(other) && (!(other instanceof TransactionTemplate) ||
185                                getTransactionManager() == ((TransactionTemplate) other).getTransactionManager())));
186        }
187
188}