001package io.ebean;
002
003import io.ebean.annotation.PersistBatch;
004import io.ebean.annotation.TxIsolation;
005import io.ebean.annotation.TxOption;
006import io.ebean.annotation.TxType;
007
008import java.util.ArrayList;
009import java.util.concurrent.Callable;
010
011/**
012 * Holds the definition of how a transactional method should run.
013 * <p>
014 * This information matches the features of the Transactional annotation. You
015 * can use it directly with Runnable or Callable via
016 * {@link DB#execute(TxScope, Runnable)} or
017 * {@link DB#executeCall(TxScope, Callable)}.
018 * </p>
019 * <p>
020 * This object is used internally with the enhancement of a method with
021 * Transactional annotation.
022 * </p>
023 *
024 * @see DB#execute(TxScope, Runnable)
025 * @see DB#executeCall(TxScope, Callable)
026 */
027public final class TxScope {
028
029  private int profileId;
030
031  private TxType type;
032
033  private String serverName;
034
035  private TxIsolation isolation;
036
037  private TxOption autoPersistUpdates;
038
039  private PersistBatch batch;
040
041  private PersistBatch batchOnCascade;
042
043  private int batchSize;
044
045  private boolean skipGeneratedKeys;
046
047  private boolean readOnly;
048
049  /**
050   * Set this to false if the JDBC batch should not be automatically be flushed when a query is executed.
051   */
052  private boolean flushOnQuery = true;
053
054  private boolean skipCache;
055
056  private String label;
057
058  private ArrayList<Class<? extends Throwable>> rollbackFor;
059
060  private ArrayList<Class<? extends Throwable>> noRollbackFor;
061
062  private ProfileLocation profileLocation;
063
064  /**
065   * Helper method to create a TxScope with REQUIRES.
066   */
067  public static TxScope required() {
068    return new TxScope(TxType.REQUIRED);
069  }
070
071  /**
072   * Helper method to create a TxScope with REQUIRES_NEW.
073   */
074  public static TxScope requiresNew() {
075    return new TxScope(TxType.REQUIRES_NEW);
076  }
077
078  /**
079   * Helper method to create a TxScope with MANDATORY.
080   */
081  public static TxScope mandatory() {
082    return new TxScope(TxType.MANDATORY);
083  }
084
085  /**
086   * Helper method to create a TxScope with SUPPORTS.
087   */
088  public static TxScope supports() {
089    return new TxScope(TxType.SUPPORTS);
090  }
091
092  /**
093   * Helper method to create a TxScope with NOT_SUPPORTED.
094   */
095  public static TxScope notSupported() {
096    return new TxScope(TxType.NOT_SUPPORTED);
097  }
098
099  /**
100   * Helper method to create a TxScope with NEVER.
101   */
102  public static TxScope never() {
103    return new TxScope(TxType.NEVER);
104  }
105
106  /**
107   * Create a REQUIRED transaction scope.
108   */
109  public TxScope() {
110    this.type = TxType.REQUIRED;
111  }
112
113  /**
114   * Create with a given transaction scope type.
115   */
116  public TxScope(TxType type) {
117    this.type = type;
118  }
119
120  /**
121   * Describes this TxScope instance.
122   */
123  @Override
124  public String toString() {
125    return "TxScope[" + type + "] readOnly[" + readOnly + "] isolation[" + isolation
126      + "] serverName[" + serverName + "] rollbackFor[" + rollbackFor + "] noRollbackFor[" + noRollbackFor + "]";
127  }
128
129  /**
130   * Return the AutoPersistUpdates mode as a nullable Boolean.
131   */
132  public Boolean getAutoPersistUpdates() {
133    return autoPersistUpdates == null ? null : autoPersistUpdates.asBoolean();
134  }
135
136  /**
137   * Return true if PersistBatch has been set.
138   */
139  public boolean isBatchSet() {
140    return batch != null && batch != PersistBatch.INHERIT;
141  }
142
143  /**
144   * Return true if batch on cascade has been set.
145   */
146  public boolean isBatchOnCascadeSet() {
147    return batchOnCascade != null && batchOnCascade != PersistBatch.INHERIT;
148  }
149
150  /**
151   * Return true if batch size has been set.
152   */
153  public boolean isBatchSizeSet() {
154    return batchSize > 0;
155  }
156
157  /**
158   * Check for batchSize being set without batch mode and use this to imply PersistBatch.ALL.
159   */
160  public void checkBatchMode() {
161    if (batchSize > 0 && notSet(batch) && notSet(batchOnCascade)) {
162      // Use setting the batchSize as implying PersistBatch.ALL for @Transactional
163      batch = PersistBatch.ALL;
164    }
165  }
166
167  /**
168   * Return true if the mode is considered not set.
169   */
170  private boolean notSet(PersistBatch batchMode) {
171    return batchMode == null || batchMode == PersistBatch.INHERIT;
172  }
173
174  /**
175   * Return the transaction type.
176   */
177  public TxType getType() {
178    return type;
179  }
180
181  /**
182   * Set the transaction type.
183   */
184  public TxScope setType(TxType type) {
185    this.type = type;
186    return this;
187  }
188
189  /**
190   * Set the autoPersistUpdates mode.
191   */
192  public TxScope setAutoPersistUpdates(TxOption autoPersistUpdates) {
193    this.autoPersistUpdates = autoPersistUpdates;
194    return this;
195  }
196
197  /**
198   * Return the transaction profile id.
199   */
200  public int getProfileId() {
201    return profileId;
202  }
203
204  /**
205   * Set the transaction profile id.
206   */
207  public TxScope setProfileId(int profileId) {
208    this.profileId = profileId;
209    return this;
210  }
211
212  /**
213   * Return the profile location.
214   */
215  public ProfileLocation getProfileLocation() {
216    return profileLocation;
217  }
218
219  /**
220   * Set the profile location.
221   */
222  public TxScope setProfileLocation(ProfileLocation profileLocation) {
223    this.profileLocation = profileLocation;
224    return this;
225  }
226
227  /**
228   * Return true if the L2 cache should be skipped for this transaction.
229   */
230  public boolean isSkipCache() {
231    return skipCache;
232  }
233
234  /**
235   * Set to true if the transaction should skip L2 cache access.
236   */
237  public TxScope setSkipCache(boolean skipCache) {
238    this.skipCache = skipCache;
239    return this;
240  }
241
242  /**
243   * Return the label for the transaction.
244   */
245  public String getLabel() {
246    return label;
247  }
248
249  /**
250   * Set a label for the transaction.
251   */
252  public TxScope setLabel(String label) {
253    this.label = label;
254    return this;
255  }
256
257  /**
258   * Return the batch mode.
259   */
260  public PersistBatch getBatch() {
261    return batch;
262  }
263
264  /**
265   * Set the batch mode to use.
266   */
267  public TxScope setBatch(PersistBatch batch) {
268    this.batch = batch;
269    return this;
270  }
271
272  /**
273   * Return the batch on cascade mode.
274   */
275  public PersistBatch getBatchOnCascade() {
276    return batchOnCascade;
277  }
278
279  /**
280   * Set the batch on cascade mode.
281   */
282  public TxScope setBatchOnCascade(PersistBatch batchOnCascade) {
283    this.batchOnCascade = batchOnCascade;
284    return this;
285  }
286
287  /**
288   * Return the batch size. 0 means use the default value.
289   */
290  public int getBatchSize() {
291    return batchSize;
292  }
293
294  /**
295   * Set the batch size to use.
296   */
297  public TxScope setBatchSize(int batchSize) {
298    this.batchSize = batchSize;
299    return this;
300  }
301
302  /**
303   * Set if the transaction should skip reading generated keys for inserts.
304   */
305  public TxScope setSkipGeneratedKeys() {
306    this.skipGeneratedKeys = true;
307    return this;
308  }
309
310  /**
311   * Return true if getGeneratedKeys should be skipped for this transaction.
312   */
313  public boolean isSkipGeneratedKeys() {
314    return skipGeneratedKeys;
315  }
316
317  /**
318   * Return if the transaction should be treated as read only.
319   */
320  public boolean isReadonly() {
321    return readOnly;
322  }
323
324  /**
325   * Set if the transaction should be treated as read only.
326   */
327  public TxScope setReadOnly(boolean readOnly) {
328    this.readOnly = readOnly;
329    return this;
330  }
331
332  /**
333   * Return false if the JDBC batch buffer should not be flushed automatically when a query is executed.
334   */
335  public boolean isFlushOnQuery() {
336    return flushOnQuery;
337  }
338
339  /**
340   * Set flushOnQuery to be false to stop automatically flushing the JDBC batch buffer when a query is executed.
341   */
342  public TxScope setFlushOnQuery(boolean flushOnQuery) {
343    this.flushOnQuery = flushOnQuery;
344    return this;
345  }
346
347  /**
348   * Return the isolation level.
349   */
350  public int getIsolationLevel() {
351    return isolation != null ? isolation.getLevel() : -1;
352  }
353
354  /**
355   * Return the Isolation level this transaction should run with.
356   */
357  public TxIsolation getIsolation() {
358    return isolation;
359  }
360
361  /**
362   * Set the transaction isolation level this transaction should run with.
363   */
364  public TxScope setIsolation(TxIsolation isolation) {
365    this.isolation = isolation;
366    return this;
367  }
368
369  /**
370   * Return the serverName for this transaction. If this is null then the
371   * default server (default DataSource) will be used.
372   */
373  public String getServerName() {
374    return serverName;
375  }
376
377  /**
378   * Set the serverName (DataSource name) for which this transaction will be. If
379   * the serverName is not specified (left null) then the default server will be
380   * used.
381   */
382  public TxScope setServerName(String serverName) {
383    this.serverName = serverName;
384    return this;
385  }
386
387  /**
388   * Return the throwable's that should cause a rollback.
389   */
390  public ArrayList<Class<? extends Throwable>> getRollbackFor() {
391    return rollbackFor;
392  }
393
394  /**
395   * Set a Throwable that should explicitly cause a rollback.
396   */
397  public TxScope setRollbackFor(Class<? extends Throwable> rollbackThrowable) {
398    if (rollbackFor == null) {
399      rollbackFor = new ArrayList<>(2);
400    }
401    rollbackFor.add(rollbackThrowable);
402    return this;
403  }
404
405  /**
406   * Set multiple throwable's that will cause a rollback.
407   */
408  @SuppressWarnings("unchecked")
409  public TxScope setRollbackFor(Class<?>[] rollbackThrowables) {
410    if (rollbackFor == null) {
411      rollbackFor = new ArrayList<>(rollbackThrowables.length);
412    }
413    for (Class<?> rollbackThrowable : rollbackThrowables) {
414      rollbackFor.add((Class<? extends Throwable>) rollbackThrowable);
415    }
416    return this;
417  }
418
419  /**
420   * Return the throwable's that should NOT cause a rollback.
421   */
422  public ArrayList<Class<? extends Throwable>> getNoRollbackFor() {
423    return noRollbackFor;
424  }
425
426  /**
427   * Add a Throwable to a list that will NOT cause a rollback. You are able to
428   * call this method multiple times with different throwable's and they will
429   * added to a list.
430   */
431  public TxScope setNoRollbackFor(Class<? extends Throwable> noRollback) {
432    if (noRollbackFor == null) {
433      noRollbackFor = new ArrayList<>(2);
434    }
435    this.noRollbackFor.add(noRollback);
436    return this;
437  }
438
439  /**
440   * Set multiple throwable's that will NOT cause a rollback.
441   */
442  @SuppressWarnings("unchecked")
443  public TxScope setNoRollbackFor(Class<?>[] noRollbacks) {
444    if (noRollbackFor == null) {
445      noRollbackFor = new ArrayList<>(noRollbacks.length);
446    }
447    for (Class<?> noRollback : noRollbacks) {
448      noRollbackFor.add((Class<? extends Throwable>) noRollback);
449    }
450    return this;
451  }
452
453  public boolean isBatchMode() {
454    return PersistBatch.ALL.equals(batch);
455  }
456
457  public boolean isBatchOnCascade() {
458    return PersistBatch.ALL.equals(batchOnCascade);
459  }
460}