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.io.Serializable; 020 021import org.springframework.core.Constants; 022import org.springframework.lang.Nullable; 023import org.springframework.transaction.TransactionDefinition; 024 025/** 026 * Default implementation of the {@link TransactionDefinition} interface, 027 * offering bean-style configuration and sensible default values 028 * (PROPAGATION_REQUIRED, ISOLATION_DEFAULT, TIMEOUT_DEFAULT, readOnly=false). 029 * 030 * <p>Base class for both {@link TransactionTemplate} and 031 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}. 032 * 033 * @author Juergen Hoeller 034 * @since 08.05.2003 035 */ 036@SuppressWarnings("serial") 037public class DefaultTransactionDefinition implements TransactionDefinition, Serializable { 038 039 /** Prefix for the propagation constants defined in TransactionDefinition. */ 040 public static final String PREFIX_PROPAGATION = "PROPAGATION_"; 041 042 /** Prefix for the isolation constants defined in TransactionDefinition. */ 043 public static final String PREFIX_ISOLATION = "ISOLATION_"; 044 045 /** Prefix for transaction timeout values in description strings. */ 046 public static final String PREFIX_TIMEOUT = "timeout_"; 047 048 /** Marker for read-only transactions in description strings. */ 049 public static final String READ_ONLY_MARKER = "readOnly"; 050 051 052 /** Constants instance for TransactionDefinition. */ 053 static final Constants constants = new Constants(TransactionDefinition.class); 054 055 private int propagationBehavior = PROPAGATION_REQUIRED; 056 057 private int isolationLevel = ISOLATION_DEFAULT; 058 059 private int timeout = TIMEOUT_DEFAULT; 060 061 private boolean readOnly = false; 062 063 @Nullable 064 private String name; 065 066 067 /** 068 * Create a new DefaultTransactionDefinition, with default settings. 069 * Can be modified through bean property setters. 070 * @see #setPropagationBehavior 071 * @see #setIsolationLevel 072 * @see #setTimeout 073 * @see #setReadOnly 074 * @see #setName 075 */ 076 public DefaultTransactionDefinition() { 077 } 078 079 /** 080 * Copy constructor. Definition can be modified through bean property setters. 081 * @see #setPropagationBehavior 082 * @see #setIsolationLevel 083 * @see #setTimeout 084 * @see #setReadOnly 085 * @see #setName 086 */ 087 public DefaultTransactionDefinition(TransactionDefinition other) { 088 this.propagationBehavior = other.getPropagationBehavior(); 089 this.isolationLevel = other.getIsolationLevel(); 090 this.timeout = other.getTimeout(); 091 this.readOnly = other.isReadOnly(); 092 this.name = other.getName(); 093 } 094 095 /** 096 * Create a new DefaultTransactionDefinition with the given 097 * propagation behavior. Can be modified through bean property setters. 098 * @param propagationBehavior one of the propagation constants in the 099 * TransactionDefinition interface 100 * @see #setIsolationLevel 101 * @see #setTimeout 102 * @see #setReadOnly 103 */ 104 public DefaultTransactionDefinition(int propagationBehavior) { 105 this.propagationBehavior = propagationBehavior; 106 } 107 108 109 /** 110 * Set the propagation behavior by the name of the corresponding constant in 111 * TransactionDefinition, e.g. "PROPAGATION_REQUIRED". 112 * @param constantName name of the constant 113 * @throws IllegalArgumentException if the supplied value is not resolvable 114 * to one of the {@code PROPAGATION_} constants or is {@code null} 115 * @see #setPropagationBehavior 116 * @see #PROPAGATION_REQUIRED 117 */ 118 public final void setPropagationBehaviorName(String constantName) throws IllegalArgumentException { 119 if (!constantName.startsWith(PREFIX_PROPAGATION)) { 120 throw new IllegalArgumentException("Only propagation constants allowed"); 121 } 122 setPropagationBehavior(constants.asNumber(constantName).intValue()); 123 } 124 125 /** 126 * Set the propagation behavior. Must be one of the propagation constants 127 * in the TransactionDefinition interface. Default is PROPAGATION_REQUIRED. 128 * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or 129 * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started 130 * transactions. Consider switching the "validateExistingTransactions" flag to 131 * "true" on your transaction manager if you'd like isolation level declarations 132 * to get rejected when participating in an existing transaction with a different 133 * isolation level. 134 * <p>Note that a transaction manager that does not support custom isolation levels 135 * will throw an exception when given any other level than {@link #ISOLATION_DEFAULT}. 136 * @throws IllegalArgumentException if the supplied value is not one of the 137 * {@code PROPAGATION_} constants 138 * @see #PROPAGATION_REQUIRED 139 */ 140 public final void setPropagationBehavior(int propagationBehavior) { 141 if (!constants.getValues(PREFIX_PROPAGATION).contains(propagationBehavior)) { 142 throw new IllegalArgumentException("Only values of propagation constants allowed"); 143 } 144 this.propagationBehavior = propagationBehavior; 145 } 146 147 @Override 148 public final int getPropagationBehavior() { 149 return this.propagationBehavior; 150 } 151 152 /** 153 * Set the isolation level by the name of the corresponding constant in 154 * TransactionDefinition, e.g. "ISOLATION_DEFAULT". 155 * @param constantName name of the constant 156 * @throws IllegalArgumentException if the supplied value is not resolvable 157 * to one of the {@code ISOLATION_} constants or is {@code null} 158 * @see #setIsolationLevel 159 * @see #ISOLATION_DEFAULT 160 */ 161 public final void setIsolationLevelName(String constantName) throws IllegalArgumentException { 162 if (!constantName.startsWith(PREFIX_ISOLATION)) { 163 throw new IllegalArgumentException("Only isolation constants allowed"); 164 } 165 setIsolationLevel(constants.asNumber(constantName).intValue()); 166 } 167 168 /** 169 * Set the isolation level. Must be one of the isolation constants 170 * in the TransactionDefinition interface. Default is ISOLATION_DEFAULT. 171 * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or 172 * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started 173 * transactions. Consider switching the "validateExistingTransactions" flag to 174 * "true" on your transaction manager if you'd like isolation level declarations 175 * to get rejected when participating in an existing transaction with a different 176 * isolation level. 177 * <p>Note that a transaction manager that does not support custom isolation levels 178 * will throw an exception when given any other level than {@link #ISOLATION_DEFAULT}. 179 * @throws IllegalArgumentException if the supplied value is not one of the 180 * {@code ISOLATION_} constants 181 * @see #ISOLATION_DEFAULT 182 */ 183 public final void setIsolationLevel(int isolationLevel) { 184 if (!constants.getValues(PREFIX_ISOLATION).contains(isolationLevel)) { 185 throw new IllegalArgumentException("Only values of isolation constants allowed"); 186 } 187 this.isolationLevel = isolationLevel; 188 } 189 190 @Override 191 public final int getIsolationLevel() { 192 return this.isolationLevel; 193 } 194 195 /** 196 * Set the timeout to apply, as number of seconds. 197 * Default is TIMEOUT_DEFAULT (-1). 198 * <p>Exclusively designed for use with {@link #PROPAGATION_REQUIRED} or 199 * {@link #PROPAGATION_REQUIRES_NEW} since it only applies to newly started 200 * transactions. 201 * <p>Note that a transaction manager that does not support timeouts will throw 202 * an exception when given any other timeout than {@link #TIMEOUT_DEFAULT}. 203 * @see #TIMEOUT_DEFAULT 204 */ 205 public final void setTimeout(int timeout) { 206 if (timeout < TIMEOUT_DEFAULT) { 207 throw new IllegalArgumentException("Timeout must be a positive integer or TIMEOUT_DEFAULT"); 208 } 209 this.timeout = timeout; 210 } 211 212 @Override 213 public final int getTimeout() { 214 return this.timeout; 215 } 216 217 /** 218 * Set whether to optimize as read-only transaction. 219 * Default is "false". 220 * <p>The read-only flag applies to any transaction context, whether backed 221 * by an actual resource transaction ({@link #PROPAGATION_REQUIRED}/ 222 * {@link #PROPAGATION_REQUIRES_NEW}) or operating non-transactionally at 223 * the resource level ({@link #PROPAGATION_SUPPORTS}). In the latter case, 224 * the flag will only apply to managed resources within the application, 225 * such as a Hibernate {@code Session}. 226 * <p>This just serves as a hint for the actual transaction subsystem; 227 * it will <i>not necessarily</i> cause failure of write access attempts. 228 * A transaction manager which cannot interpret the read-only hint will 229 * <i>not</i> throw an exception when asked for a read-only transaction. 230 */ 231 public final void setReadOnly(boolean readOnly) { 232 this.readOnly = readOnly; 233 } 234 235 @Override 236 public final boolean isReadOnly() { 237 return this.readOnly; 238 } 239 240 /** 241 * Set the name of this transaction. Default is none. 242 * <p>This will be used as transaction name to be shown in a 243 * transaction monitor, if applicable (for example, WebLogic's). 244 */ 245 public final void setName(String name) { 246 this.name = name; 247 } 248 249 @Override 250 @Nullable 251 public final String getName() { 252 return this.name; 253 } 254 255 256 /** 257 * This implementation compares the {@code toString()} results. 258 * @see #toString() 259 */ 260 @Override 261 public boolean equals(@Nullable Object other) { 262 return (this == other || (other instanceof TransactionDefinition && toString().equals(other.toString()))); 263 } 264 265 /** 266 * This implementation returns {@code toString()}'s hash code. 267 * @see #toString() 268 */ 269 @Override 270 public int hashCode() { 271 return toString().hashCode(); 272 } 273 274 /** 275 * Return an identifying description for this transaction definition. 276 * <p>The format matches the one used by 277 * {@link org.springframework.transaction.interceptor.TransactionAttributeEditor}, 278 * to be able to feed {@code toString} results into bean properties of type 279 * {@link org.springframework.transaction.interceptor.TransactionAttribute}. 280 * <p>Has to be overridden in subclasses for correct {@code equals} 281 * and {@code hashCode} behavior. Alternatively, {@link #equals} 282 * and {@link #hashCode} can be overridden themselves. 283 * @see #getDefinitionDescription() 284 * @see org.springframework.transaction.interceptor.TransactionAttributeEditor 285 */ 286 @Override 287 public String toString() { 288 return getDefinitionDescription().toString(); 289 } 290 291 /** 292 * Return an identifying description for this transaction definition. 293 * <p>Available to subclasses, for inclusion in their {@code toString()} result. 294 */ 295 protected final StringBuilder getDefinitionDescription() { 296 StringBuilder result = new StringBuilder(); 297 result.append(constants.toCode(this.propagationBehavior, PREFIX_PROPAGATION)); 298 result.append(','); 299 result.append(constants.toCode(this.isolationLevel, PREFIX_ISOLATION)); 300 if (this.timeout != TIMEOUT_DEFAULT) { 301 result.append(','); 302 result.append(PREFIX_TIMEOUT).append(this.timeout); 303 } 304 if (this.readOnly) { 305 result.append(','); 306 result.append(READ_ONLY_MARKER); 307 } 308 return result; 309 } 310 311}