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