001package com.avaje.ebean;
002
003import com.avaje.ebean.backgroundexecutor.ImmediateBackgroundExecutor;
004import com.avaje.ebean.cache.ServerCacheManager;
005import com.avaje.ebean.delegate.DelegateBulkUpdate;
006import com.avaje.ebean.delegate.DelegateDelete;
007import com.avaje.ebean.delegate.DelegateFind;
008import com.avaje.ebean.delegate.DelegateFindSqlQuery;
009import com.avaje.ebean.delegate.DelegatePublish;
010import com.avaje.ebean.delegate.DelegateQuery;
011import com.avaje.ebean.delegate.DelegateSave;
012import com.avaje.ebean.delegate.InterceptBulkUpdate;
013import com.avaje.ebean.delegate.InterceptDelete;
014import com.avaje.ebean.delegate.InterceptFind;
015import com.avaje.ebean.delegate.InterceptFindSqlQuery;
016import com.avaje.ebean.delegate.InterceptPublish;
017import com.avaje.ebean.delegate.InterceptSave;
018import com.avaje.ebean.meta.MetaInfoManager;
019import com.avaje.ebean.plugin.SpiServer;
020import com.avaje.ebean.text.csv.CsvReader;
021import com.avaje.ebean.text.json.JsonContext;
022import com.avaje.ebeaninternal.api.SpiQuery;
023
024import javax.persistence.OptimisticLockException;
025import javax.persistence.PersistenceException;
026import java.util.Collection;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031/**
032 * Wraps an underlying EbeanServer.
033 * <p>
034 * Can you used for testing purposes when you want to create a test double that
035 * only replaces some of the underlying functionality of the EbeanServer, for example
036 * only overwrites some of the find or save functionality but leaves the rest of the
037 * behavior to be handled normally by the underlying delegate.
038 * <p>
039 * The underlying delegate is most often a fully functional EbeanServer that is using H2
040 * as a test database.
041 * </p>
042 */
043public class DelegateEbeanServer implements EbeanServer, DelegateAwareEbeanServer, DelegateMethodNames {
044
045  /**
046   * The list of methods calls made to this server.
047   */
048  public MethodCalls methodCalls = new MethodCalls();
049
050  /**
051   * The beans sent to the save(), delete() methods etc.
052   */
053  public BeanCapture capturedBeans = new BeanCapture();
054
055  /**
056   * Various find methods that have specific test responses.
057   */
058  protected WhenFind whenFind = new WhenFind();
059
060  /**
061   * Test double replacements for 'Finders' which are static fields on entity beans.
062   */
063  protected WithStaticFinders withStaticFinders = new WithStaticFinders();
064
065  /**
066   * The underlying EbeanServer we delegate to.
067   * <p/>
068   * This will often be a fully functional EbeanSever that uses H2.
069   */
070  protected EbeanServer delegate;
071
072  /**
073   * Expect ImmediateBackgroundExecutor to be a good default. Can use IgnoreBackgroundExecutor or the delegates one.
074   */
075  protected BackgroundExecutor backgroundExecutor = new ImmediateBackgroundExecutor();
076
077  /**
078   * Constructs queries that will call back to this so not really expecting it to be overwritten.
079   */
080  protected DelegateQuery delegateQuery;
081
082  protected InterceptSave save;
083
084  protected InterceptBulkUpdate bulkUpdate;
085
086  protected InterceptDelete delete;
087
088  protected InterceptFind find;
089
090  protected InterceptPublish publish;
091
092  protected InterceptFindSqlQuery findSqlQuery;
093
094  /**
095   * If set to true the 'bulk update' calls are passed through to the underlying delegate.
096   */
097  protected boolean persistBulkUpdates;
098
099  /**
100   * If set to true the 'delete' calls are passed through to the underlying delegate.
101   */
102  protected boolean persistDeletes;
103
104  /**
105   * If set to true the 'insert' calls are passed through to the underlying delegate.
106   */
107  protected boolean persistInserts;
108
109  /**
110   * If set to true the 'save' calls are passed through to the underlying delegate.
111   */
112  protected boolean persistSaves;
113
114  /**
115   * If set to true the 'update' calls are passed through to the underlying delegate.
116   */
117  protected boolean persistUpdates;
118
119  /**
120   * If set to true the publish/draft calls are passed through to the underlying delegate.
121   */
122  protected boolean persistPublish;
123
124  /**
125   * Construct with defaults.
126   */
127  public DelegateEbeanServer() {
128
129  }
130
131  /**
132   * Construct with a EbeanServer to delegate and using ImmediateBackgroundExecutor.
133   * <p>
134   * This delegate will be used on all method calls that are not overwritten.
135   */
136  public DelegateEbeanServer withDelegate(EbeanServer delegate) {
137    this.delegate = delegate;
138    this.delegateQuery = new DelegateQuery(delegate, this);
139    this.save = new DelegateSave(delegate);
140    this.delete = new DelegateDelete(delegate);
141    this.bulkUpdate = new DelegateBulkUpdate(delegate);
142    this.find = new DelegateFind(delegate);
143    this.findSqlQuery = new DelegateFindSqlQuery(delegate);
144    this.publish = new DelegatePublish(delegate);
145    return this;
146  }
147
148  @Override
149  public void beforeRun() {
150    withStaticFinders.beforeRun();
151  }
152
153  @Override
154  public void afterRun() {
155    withStaticFinders.afterRun();
156  }
157
158  public WhenFind whenFind() {
159    return whenFind;
160  }
161
162  /**
163   * Used to specify a test double to replace a static 'finder' field on the given beanType.
164   * <pre>{@code
165   *
166   *   DelegateEbeanServer mock = new DelegateEbeanServer();
167   *   mock.withFinder(Customer.class).as(new TDCustomerFinder());
168   *
169   *   // Note: TDCustomerFinder not set onto Customer until runWithMock()
170   *
171   *   MockiEbean.runWithMock(mock, new Runnable() {
172   *
173   *     public void run() {
174   *       ...
175   *       // Customer.find now is our test double TDCustomerFinder
176   *       Customer found = Customer.find.byUniqueName("foo");
177   *     }
178   *   });
179   *
180   *   // Note: original Customer.find implementation is restored by MockiEbean
181   *
182   * }</pre>
183   */
184  public <T> WithStaticFinder<T> withFinder(Class<T> beanType) {
185    return withStaticFinders.withFinder(beanType);
186  }
187
188
189  /**
190   * Set the underlying delegate to proxy requests to.
191   * <p/>
192   * Used with the default constructor such that this DelegateEbeanServer
193   * can be setup prior to having access to the underlying EbeanServer
194   * that we want to proxy through to.
195   * <p/>
196   * Return true if the underling ebeanServer was set.
197   */
198  public boolean withDelegateIfRequired(EbeanServer delegate) {
199    if (this.delegate == null) {
200      withDelegate(delegate);
201      return true;
202    }
203    if (this.delegate instanceof DelegateAwareEbeanServer) {
204      // probably using ProxyEbeanServer to capture method calls etc
205      return ((DelegateAwareEbeanServer)this.delegate).withDelegateIfRequired(delegate);
206    }
207    // delegate was not set
208    return false;
209  }
210
211  public DelegateEbeanServer withInterceptFind(InterceptFind find) {
212    this.find = find;
213    return this;
214  }
215
216  /**
217   * Set to true for all the persisting calls skip/avoid calling the underlying delegate.
218   * <p>
219   * So when set to true then all the calls to save(), delete() etc do not get passed on the
220   * the underlying delegate.
221   */
222  public DelegateEbeanServer withPersisting(boolean persisting) {
223    persistBulkUpdates = persisting;
224    persistDeletes = persisting;
225    persistInserts = persisting;
226    persistUpdates = persisting;
227    persistSaves = persisting;
228    return this;
229  }
230
231  @Override
232  public SpiServer getPluginApi() {
233    methodCalls.add(MethodCall.of("getPluginApi"));
234    return delegate.getPluginApi();
235  }
236
237  @Override
238  public AutoTune getAutoTune() {
239    methodCalls.add(MethodCall.of("getAutoTune"));
240    return delegate.getAutoTune();
241  }
242
243  @Override
244  public DocumentStore docStore() {
245    methodCalls.add(MethodCall.of("docStore"));
246    return delegate.docStore();
247  }
248
249  /**
250   * Return the BackgroundExecutor.
251   *
252   * Typically for testing we either want these to run immediately or not at all.
253   * Defaults to use ImmediateBackgroundExecutor, use IgnoreBackgroundExecutor if desired.
254   */
255  @Override
256  public BackgroundExecutor getBackgroundExecutor() {
257    methodCalls.add(MethodCall.of("getBackgroundExecutor"));
258    return backgroundExecutor;
259  }
260
261  @Override
262  public ServerCacheManager getServerCacheManager() {
263    methodCalls.add(MethodCall.of("getServerCacheManager"));
264    return delegate.getServerCacheManager();
265  }
266
267  @Override
268  public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) {
269    methodCalls.add(MethodCall.of("shutdown").with("shutdownDataSource", shutdownDataSource, "deregisterDriver", deregisterDriver));
270    delegate.shutdown(shutdownDataSource, deregisterDriver);
271  }
272
273  @Override
274  public JsonContext json() {
275    methodCalls.add(MethodCall.of("json"));
276    return delegate.json();
277  }
278
279  @Override
280  public String getName() {
281    methodCalls.add(MethodCall.of("getName"));
282    return delegate.getName();
283  }
284
285  @Override
286  public ExpressionFactory getExpressionFactory() {
287    methodCalls.add(MethodCall.of("getExpressionFactory"));
288    return delegate.getExpressionFactory();
289  }
290
291  @Override
292  public MetaInfoManager getMetaInfoManager() {
293    methodCalls.add(MethodCall.of("getMetaInfoManager"));
294    return delegate.getMetaInfoManager();
295  }
296
297  @Override
298  public BeanState getBeanState(Object bean) {
299    methodCalls.add(MethodCall.of("getBeanState").with("bean", bean));
300    return delegate.getBeanState(bean);
301  }
302
303  @Override
304  public Object getBeanId(Object bean) {
305    methodCalls.add(MethodCall.of("getBeanId").with("bean", bean));
306    return delegate.getBeanId(bean);
307  }
308
309  @Override
310  public Object setBeanId(Object bean, Object id) {
311    methodCalls.add(MethodCall.of("setBeanId").with("bean", bean).with("id", id));
312    return delegate.setBeanId(bean, id);
313  }
314
315  @Override
316  public Map<String, ValuePair> diff(Object a, Object b) {
317    methodCalls.add(MethodCall.of("diff").with("a", a, "b", b));
318    return delegate.diff(a, b);
319  }
320
321  @Override
322  public <T> T createEntityBean(Class<T> beanType) {
323    methodCalls.add(MethodCall.of("createEntityBean").with("beanType", beanType));
324    return delegate.createEntityBean(beanType);
325  }
326
327  @Override
328  public <T> CsvReader<T> createCsvReader(Class<T> beanType) {
329    methodCalls.add(MethodCall.of("createCsvReader").with("beanType", beanType));
330    return delegate.createCsvReader(beanType);
331  }
332
333  @Override
334  public <T> Filter<T> filter(Class<T> beanType) {
335    methodCalls.add(MethodCall.of("filter").with("beanType", beanType));
336    return delegate.filter(beanType);
337  }
338
339  @Override
340  public <T> void sort(List<T> list, String sortByClause) {
341    methodCalls.add(MethodCall.of("sort").with("list", list, "sortByClause", sortByClause));
342    delegate.sort(list, sortByClause);
343  }
344
345  @Override
346  public void markAsDirty(Object bean) {
347    methodCalls.add(MethodCall.of("markAsDirty").with("bean", bean));
348    delegate.markAsDirty(bean);
349  }
350
351
352  // -- create updates ------------------------
353
354  @Override
355  public <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate) {
356    methodCalls.add(MethodCall.of("createUpdate").with("beanType", beanType, "ormUpdate", ormUpdate));
357    return delegate.createUpdate(beanType, ormUpdate);
358  }
359
360  @Override
361  public SqlUpdate createSqlUpdate(String sql) {
362    methodCalls.add(MethodCall.of("createSqlUpdate").with("sql", sql));
363    return delegate.createSqlUpdate(sql);
364  }
365
366  @Override
367  public CallableSql createCallableSql(String callableSql) {
368    methodCalls.add(MethodCall.of("createCallableSql").with("callableSql", callableSql));
369    return delegate.createCallableSql(callableSql);
370  }
371
372  // -- transaction ------------------------
373
374  @Override
375  public void execute(TxScope scope, TxRunnable runnable) {
376    methodCalls.add(MethodCall.of("bulkUpdate").with("scope", scope, "runnable", runnable));
377    delegate.execute(scope, runnable);
378  }
379
380  @Override
381  public void execute(TxRunnable runnable) {
382    methodCalls.add(MethodCall.of("bulkUpdate").with("runnable", runnable));
383    delegate.execute(runnable);
384  }
385
386  @Override
387  public <T> T execute(TxScope scope, TxCallable<T> callable) {
388    methodCalls.add(MethodCall.of("bulkUpdate").with("scope", scope, "callable", callable));
389    return delegate.execute(scope, callable);
390  }
391
392  @Override
393  public <T> T execute(TxCallable<T> callable) {
394    methodCalls.add(MethodCall.of("bulkUpdate").with("callable", callable));
395    return delegate.execute(callable);
396  }
397
398  @Override
399  public void register(TransactionCallback transactionCallback) throws PersistenceException {
400    methodCalls.add(MethodCall.of("register").with("transactionCallback", transactionCallback));
401    delegate.register(transactionCallback);
402  }
403
404  @Override
405  public Transaction createTransaction() {
406    methodCalls.add(MethodCall.of("createTransaction"));
407    return delegate.createTransaction();
408  }
409
410  @Override
411  public Transaction createTransaction(TxIsolation isolation) {
412    methodCalls.add(MethodCall.of("createTransaction").with("isolation", isolation));
413    return delegate.createTransaction(isolation);
414  }
415
416  @Override
417  public Transaction beginTransaction() {
418    methodCalls.add(MethodCall.of("beginTransaction"));
419    return delegate.beginTransaction();
420  }
421
422  @Override
423  public Transaction beginTransaction(TxIsolation isolation) {
424    methodCalls.add(MethodCall.of("beginTransaction").with("isolation", isolation));
425    return delegate.beginTransaction(isolation);
426  }
427
428  @Override
429  public Transaction beginTransaction(TxScope scope) {
430    methodCalls.add(MethodCall.of("beginTransaction").with("scope", scope));
431    return delegate.beginTransaction(scope);
432  }
433
434  @Override
435  public Transaction currentTransaction() {
436    methodCalls.add(MethodCall.of("currentTransaction"));
437    return delegate.currentTransaction();
438  }
439
440  @Override
441  public void commitTransaction() {
442    methodCalls.add(MethodCall.of("commitTransaction"));
443    delegate.commitTransaction();
444  }
445
446  @Override
447  public void rollbackTransaction() {
448    methodCalls.add(MethodCall.of("rollbackTransaction"));
449    delegate.rollbackTransaction();
450  }
451
452  @Override
453  public void endTransaction() {
454    methodCalls.add(MethodCall.of("endTransaction"));
455    delegate.endTransaction();
456  }
457
458  // -- delegateQuery ------------------------
459
460  @Override
461  public <T> T getReference(Class<T> beanType, Object id) {
462    methodCalls.add(MethodCall.of("getReference").with("beanType", beanType, "id", id));
463    return delegateQuery.getReference(beanType, id);
464  }
465
466  @Override
467  public <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery) {
468    methodCalls.add(MethodCall.of("createNamedQuery").with("beanType", beanType).with("namedQuery", namedQuery));
469    return delegateQuery.createNamedQuery(beanType, namedQuery);
470  }
471
472  @Override
473  public <T> Query<T> createQuery(Class<T> beanType, String eql) {
474    methodCalls.add(MethodCall.of("createQuery").with("beanType", beanType).with("eql", eql));
475    return delegateQuery.createQuery(beanType, eql);
476  }
477
478  @Override
479  public <T> Query<T> createQuery(Class<T> beanType) {
480    methodCalls.add(MethodCall.of("createQuery").with("beanType", beanType));
481    return delegateQuery.createQuery(beanType);
482  }
483
484  @Override
485  public <T> Set<String> validateQuery(Query<T> query) {
486    methodCalls.add(MethodCall.of("validateQuery").with("query", query));
487    return delegateQuery.validateQuery(query);
488  }
489
490  @Override
491  public <T> Query<T> find(Class<T> beanType) {
492    methodCalls.add(MethodCall.of("find").with("beanType", beanType));
493    return delegateQuery.find(beanType);
494  }
495
496  @Override
497  public SqlQuery createSqlQuery(String sql) {
498    methodCalls.add(MethodCall.of("createSqlQuery").with("sql", sql));
499    return delegateQuery.createSqlQuery(sql);
500  }
501
502  // -- refresh ------------------------
503
504  @Override
505  public void refresh(Object bean) {
506    methodCalls.add(MethodCall.of("refresh").with("bean", bean));
507    find.refresh(bean);
508  }
509
510  @Override
511  public void refreshMany(Object bean, String propertyName) {
512    methodCalls.add(MethodCall.of("refreshMany").with("bean", bean, "propertyName", propertyName));
513    find.refreshMany(bean, propertyName);
514  }
515
516  // -- find ------------------------
517
518  @Override
519  public <T> T find(Class<T> beanType, Object id) {
520    methodCalls.add(MethodCall.of("find").with("beanType", beanType, "id", id));
521    WhenBeanReturn match = whenFind.findMatchById(beanType, id);
522    if (match != null) {
523      return (T)match.val();
524    }
525    return find.find(beanType, id, null);
526  }
527
528  @Override
529  public <T> T find(Class<T> beanType, Object id, Transaction transaction) {
530    methodCalls.add(MethodCall.of("find").with("beanType", beanType, "id", id, "transaction", transaction));
531    WhenBeanReturn match = whenFind.findMatchById(beanType, id);
532    if (match != null) {
533      return (T)match.val();
534    }
535    return find.find(beanType, id, transaction);
536  }
537
538  @Override
539  public <T> T findUnique(Query<T> query, Transaction transaction) {
540    methodCalls.add(MethodCall.of("findUnique").with("query", query, "transaction", transaction));
541    WhenBeanReturn match = whenFind.findMatchByUnique(((SpiQuery)query).getBeanType());
542    if (match != null) {
543      return (T)match.val();
544    }
545    return find.findUnique(query, transaction);
546  }
547
548  @Override
549  public <T> int findRowCount(Query<T> query, Transaction transaction) {
550    methodCalls.add(MethodCall.of("findRowCount").with("query", query, "transaction", transaction));
551    return find.findRowCount(query, transaction);
552  }
553
554  @Override
555  public <T> List<Object> findIds(Query<T> query, Transaction transaction) {
556    methodCalls.add(MethodCall.of("findIds").with("query", query, "transaction", transaction));
557    return find.findIds(query, transaction);
558  }
559
560  @Override
561  public <T> void findEach(Query<T> query, QueryEachConsumer<T> consumer, Transaction transaction) {
562    methodCalls.add(MethodCall.of("findEach").with("query", query, "consumer", consumer, "transaction", transaction));
563    find.findEach(query, consumer, transaction);
564  }
565
566  @Override
567  public <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction transaction) {
568    methodCalls.add(MethodCall.of("findEachWhile").with("query", query, "consumer", consumer, "transaction", transaction));
569    find.findEachWhile(query, consumer, transaction);
570  }
571
572  @Override
573  public <T> List<T> findList(Query<T> query, Transaction transaction) {
574    methodCalls.add(MethodCall.of("findList").with("query", query, "transaction", transaction));
575    return find.findList(query, transaction);
576  }
577
578  @Override
579  public <T> FutureRowCount<T> findFutureRowCount(Query<T> query, Transaction transaction) {
580    methodCalls.add(MethodCall.of("findFutureRowCount").with("query", query, "transaction", transaction));
581    return find.findFutureRowCount(query, transaction);
582  }
583
584  @Override
585  public <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction) {
586    methodCalls.add(MethodCall.of("findFutureIds").with("query", query, "transaction", transaction));
587    return find.findFutureIds(query, transaction);
588  }
589
590  @Override
591  public <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction) {
592    methodCalls.add(MethodCall.of("findFutureList").with("query", query, "transaction", transaction));
593    return find.findFutureList(query, transaction);
594  }
595
596  @Override
597  public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction) {
598    methodCalls.add(MethodCall.of("findPagedList").with("query", query, "transaction", transaction));
599    return find.findPagedList(query, transaction);
600  }
601
602  @Override
603  public <T> Set<T> findSet(Query<T> query, Transaction transaction) {
604    methodCalls.add(MethodCall.of("findSet").with("query", query, "transaction", transaction));
605    return find.findSet(query, transaction);
606  }
607
608  @Override
609  public <T> Map<?, T> findMap(Query<T> query, Transaction transaction) {
610    methodCalls.add(MethodCall.of("findMap").with("query", query, "transaction", transaction));
611    return find.findMap(query, transaction);
612  }
613
614  @Override
615  public <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction) {
616    methodCalls.add(MethodCall.of("findVersions").with("query", query, "transaction", transaction));
617    return find.findVersions(query, transaction);
618  }
619
620  // -- find SqlQuery ------------------------
621
622  @Override
623  public List<SqlRow> findList(SqlQuery sqlQuery, Transaction transaction) {
624    methodCalls.add(MethodCall.of("findList").with("sqlQuery", sqlQuery, "transaction", transaction));
625    return findSqlQuery.findList(sqlQuery, transaction);
626  }
627
628  @Override
629  public SqlRow findUnique(SqlQuery sqlQuery, Transaction transaction) {
630    methodCalls.add(MethodCall.of("findUnique").with("sqlQuery", sqlQuery, "transaction", transaction));
631    return findSqlQuery.findUnique(sqlQuery, transaction);
632  }
633
634  @Override
635  public void findEach(SqlQuery sqlQuery, QueryEachConsumer<SqlRow> consumer, Transaction transaction) {
636    methodCalls.add(MethodCall.of("findEach").with("sqlQuery", sqlQuery, "consumer", consumer, "transaction", transaction));
637    findSqlQuery.findEach(sqlQuery, consumer, transaction);
638  }
639
640  @Override
641  public void findEachWhile(SqlQuery sqlQuery, QueryEachWhileConsumer<SqlRow> consumer, Transaction transaction) {
642    methodCalls.add(MethodCall.of("findEachWhile").with("sqlQuery", sqlQuery, "consumer", consumer, "transaction", transaction));
643    findSqlQuery.findEachWhile(sqlQuery, consumer, transaction);
644  }
645
646  // -- save ------------------------
647
648  @Override
649  public Object nextId(Class<?> beanType) {
650    methodCalls.add(MethodCall.of("nextId").with("beanType", beanType));
651    return !persistSaves ? 0 : save.nextId(beanType);
652  }
653
654
655  @Override
656  public void save(Object bean) throws OptimisticLockException {
657    methodCalls.add(MethodCall.of(SAVE).with("bean", bean));
658    capturedBeans.addSaved(bean);
659    if (persistSaves) {
660      save.save(bean, null);
661    }
662  }
663
664  @Override
665  public int saveAll(Collection<?> beans) throws OptimisticLockException {
666    methodCalls.add(MethodCall.of(SAVE_ALL).with("beans", beans));
667    capturedBeans.addSavedAll(beans);
668    return !persistSaves ? 0 : save.saveAll(beans, null);
669  }
670
671
672  @Override
673  public void save(Object bean, Transaction transaction) throws OptimisticLockException {
674    methodCalls.add(MethodCall.of(SAVE).with("bean", bean, "transaction", transaction));
675    capturedBeans.addSaved(bean);
676    if (persistSaves) {
677      save.save(bean, transaction);
678    }
679  }
680
681  @Override
682  public int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
683    methodCalls.add(MethodCall.of(SAVE_ALL).with("beans", beans, "transaction", transaction));
684    capturedBeans.addSavedAll(beans);
685    return !persistSaves ? 0 : save.saveAll(beans, transaction);
686  }
687
688  @Override
689  public <T> UpdateQuery<T> update(Class<T> beanType) {
690    methodCalls.add(MethodCall.of(UPDATE).with("beanType", beanType));
691    return delegate.update(beanType);
692  }
693
694  @Override
695  public <T> int update(Query<T> query, Transaction transaction) {
696    methodCalls.add(MethodCall.of(UPDATE).with("query", query).with("transaction", transaction));
697    if (persistUpdates) {
698      return delegate.update(query, transaction);
699    }
700    return 0;
701  }
702
703  @Override
704  public void update(Object bean) throws OptimisticLockException {
705    methodCalls.add(MethodCall.of(UPDATE).with("bean", bean));
706    capturedBeans.addUpdated(bean);
707    if (persistUpdates) {
708      save.update(bean, null);
709    }
710  }
711
712  @Override
713  public void update(Object bean, Transaction transaction) throws OptimisticLockException {
714    methodCalls.add(MethodCall.of(UPDATE).with("bean", bean, "transaction", transaction));
715    capturedBeans.addUpdated(bean);
716    if (persistUpdates) {
717      save.update(bean, transaction);
718    }
719  }
720
721  @Override
722  public void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException {
723    methodCalls.add(MethodCall.of(UPDATE).with("bean", bean, "transaction", transaction, "deleteMissingChildren", deleteMissingChildren));
724    capturedBeans.addUpdated(bean);
725    if (persistUpdates) {
726      save.update(bean, transaction, deleteMissingChildren);
727    }
728  }
729
730  @Override
731  public void updateAll(Collection<?> beans) throws OptimisticLockException {
732    methodCalls.add(MethodCall.of(UPDATE_ALL).with("beans", beans));
733    capturedBeans.addUpdatedAll(beans);
734    if (persistUpdates) {
735      save.updateAll(beans, null);
736    }
737  }
738
739  @Override
740  public void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
741    methodCalls.add(MethodCall.of(UPDATE_ALL).with("beans", beans, "transaction", transaction));
742    capturedBeans.addUpdatedAll(beans);
743    if (persistUpdates) {
744      save.updateAll(beans, transaction);
745    }
746  }
747
748  @Override
749  public void insert(Object bean) {
750    methodCalls.add(MethodCall.of(INSERT).with("bean", bean));
751    capturedBeans.addInserted(bean);
752    if (persistInserts) {
753      save.insert(bean, null);
754    }
755  }
756
757  @Override
758  public void insert(Object bean, Transaction transaction) {
759    methodCalls.add(MethodCall.of(INSERT).with("bean", bean, "transaction", transaction));
760    capturedBeans.addInserted(bean);
761    if (persistInserts) {
762      save.insert(bean, transaction);
763    }
764  }
765
766  @Override
767  public void insertAll(Collection<?> beans) {
768    methodCalls.add(MethodCall.of(INSERT_ALL).with("beans", beans));
769    capturedBeans.addInsertedAll(beans);
770    if (persistInserts) {
771      save.insertAll(beans, null);
772    }
773  }
774
775  @Override
776  public void insertAll(Collection<?> beans, Transaction transaction) {
777    methodCalls.add(MethodCall.of(INSERT_ALL).with("beans", beans, "transaction", transaction));
778    capturedBeans.addInsertedAll(beans);
779    if (persistInserts) {
780      save.insertAll(beans, transaction);
781    }
782  }
783
784
785  // -- delete ------------------------
786
787
788  @Override
789  public boolean delete(Object bean) throws OptimisticLockException {
790    methodCalls.add(MethodCall.of("bean").with("bean", bean));
791    capturedBeans.addDeleted(bean);
792    if (persistDeletes) {
793      return delete.delete(bean, null);
794    }
795    return true;
796  }
797
798  @Override
799  public int deleteAll(Collection<?> beans) throws OptimisticLockException {
800    methodCalls.add(MethodCall.of(DELETE_ALL).with("beans", beans));
801    capturedBeans.addDeletedAll(beans);
802    return !persistDeletes ? 0 : delete.deleteAll(beans);
803  }
804
805  @Override
806  public int deleteAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
807    methodCalls.add(MethodCall.of(DELETE_ALL).with("beans", beans));
808    capturedBeans.addDeletedAll(beans);
809    return !persistDeletes ? 0 : delete.deleteAll(beans, transaction);
810  }
811
812  @Override
813  public boolean deletePermanent(Object bean) throws OptimisticLockException {
814    methodCalls.add(MethodCall.of(DELETE_PERMANENT).with("bean", bean));
815    capturedBeans.addDeletePermanent(bean);
816    return !persistDeletes ? true : delete.deletePermanent(bean);
817  }
818
819  @Override
820  public boolean deletePermanent(Object bean, Transaction transaction) throws OptimisticLockException {
821    methodCalls.add(MethodCall.of(DELETE_PERMANENT).with("bean", bean));
822    capturedBeans.addDeletePermanent(bean);
823    return !persistDeletes ? true : delete.deletePermanent(bean, transaction);
824  }
825
826  @Override
827  public int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException {
828    methodCalls.add(MethodCall.of(DELETE_ALL_PERMANENT).with("beans", beans));
829    capturedBeans.addDeletedAllPermanent(beans);
830    return !persistDeletes ? 0 : delete.deleteAllPermanent(beans);
831  }
832
833  @Override
834  public int deleteAllPermanent(Collection<?> beans, Transaction transaction) throws OptimisticLockException {
835    methodCalls.add(MethodCall.of(DELETE_ALL_PERMANENT).with("beans", beans));
836    capturedBeans.addDeletedAllPermanent(beans);
837    return !persistDeletes ? 0 : delete.deleteAllPermanent(beans, transaction);
838  }
839
840  @Override
841  public <T> int delete(Query<T> query, Transaction transaction) {
842    methodCalls.add(MethodCall.of(DELETE).with("query", query));
843    return !persistDeletes ? 0 : delete.delete(query, transaction);
844  }
845
846  @Override
847  public int delete(Class<?> beanType, Object id) {
848    MethodCall deleteById = MethodCall.of(DELETE).with("beanType", beanType, "id", id);
849    methodCalls.add(deleteById);
850    capturedBeans.addDeleted(deleteById);
851    return !persistDeletes ? 0 : delete.delete(beanType, id, null);
852  }
853
854  @Override
855  public int delete(Class<?> beanType, Object id, Transaction transaction) {
856    MethodCall deleteById = MethodCall.of(DELETE).with("beanType", beanType, "id", id, "transaction", transaction);
857    methodCalls.add(deleteById);
858    capturedBeans.addDeleted(deleteById);
859    return !persistDeletes ? 0 : delete.delete(beanType, id, transaction);
860  }
861
862  @Override
863  public int deletePermanent(Class<?> beanType, Object id) {
864    MethodCall deleteById = MethodCall.of(DELETE_PERMANENT).with("beanType", beanType, "id", id);
865    methodCalls.add(deleteById);
866    capturedBeans.addDeleted(deleteById);
867    return !persistDeletes ? 0 : delete.deletePermanent(beanType, id);
868  }
869
870  @Override
871  public int deletePermanent(Class<?> beanType, Object id, Transaction transaction) {
872    MethodCall deleteById = MethodCall.of(DELETE_PERMANENT).with("beanType", beanType, "id", id, "transaction", transaction);
873    methodCalls.add(deleteById);
874    capturedBeans.addDeleted(deleteById);
875    return !persistDeletes ? 0 : delete.deletePermanent(beanType, id, transaction);
876  }
877
878  @Override
879  public int deleteAll(Class<?> beanType, Collection<?> ids) {
880    MethodCall deleteByIds = MethodCall.of(DELETE_ALL).with("beanType", beanType, "ids", ids);
881    methodCalls.add(deleteByIds);
882    capturedBeans.addDeleted(deleteByIds);
883    if (persistDeletes) {
884      return delete.deleteAll(beanType, ids, null);
885    }
886    return 0;
887  }
888
889  @Override
890  public int deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction) {
891    MethodCall deleteByIds = MethodCall.of(DELETE_ALL).with("beanType", beanType, "ids", ids, "transaction", transaction);
892    methodCalls.add(deleteByIds);
893    capturedBeans.addDeleted(deleteByIds);
894    if (persistDeletes) {
895      return delete.deleteAll(beanType, ids, transaction);
896    }
897    return 0;
898  }
899
900  @Override
901  public int deleteAllPermanent(Class<?> beanType, Collection<?> ids) {
902    MethodCall deleteByIds = MethodCall.of(DELETE_ALL_PERMANENT).with("beanType", beanType, "ids", ids);
903    methodCalls.add(deleteByIds);
904    capturedBeans.addDeleted(deleteByIds);
905    if (persistDeletes) {
906      return delete.deleteAllPermanent(beanType, ids);
907    }
908    return 0;
909  }
910
911  @Override
912  public int deleteAllPermanent(Class<?> beanType, Collection<?> ids, Transaction transaction) {
913    MethodCall deleteByIds = MethodCall.of(DELETE_ALL_PERMANENT).with("beanType", beanType, "ids", ids, "transaction", transaction);
914    methodCalls.add(deleteByIds);
915    capturedBeans.addDeleted(deleteByIds);
916    if (persistDeletes) {
917      return delete.deleteAllPermanent(beanType, ids, transaction);
918    }
919    return 0;
920  }
921
922  @Override
923  public boolean delete(Object bean, Transaction transaction) throws OptimisticLockException {
924    methodCalls.add(MethodCall.of(DELETE).with("bean", bean, "transaction", transaction));
925    capturedBeans.addDeleted(bean);
926    if (persistDeletes) {
927      return delete.delete(bean, transaction);
928    }
929    return true;
930  }
931
932
933  // -- publish and restore ---------------------------
934
935
936  @Override
937  public <T> T publish(Class<T> beanType, Object id, Transaction transaction) {
938    methodCalls.add(MethodCall.of(PUBLISH).with("beanType", beanType).with("id", id));
939    return !persistPublish ? null : publish.publish(beanType, id, transaction);
940  }
941
942  @Override
943  public <T> T publish(Class<T> beanType, Object id) {
944    methodCalls.add(MethodCall.of(PUBLISH).with("beanType", beanType).with("id", id));
945    return !persistPublish ? null : publish.publish(beanType, id);
946  }
947
948  @Override
949  public <T> List<T> publish(Query<T> query, Transaction transaction) {
950    methodCalls.add(MethodCall.of(PUBLISH).with("query", query));
951    return !persistPublish ? null : publish.publish(query, transaction);
952  }
953
954  @Override
955  public <T> List<T> publish(Query<T> query) {
956    methodCalls.add(MethodCall.of(PUBLISH).with("query", query));
957    return !persistPublish ? null : publish.publish(query);
958  }
959
960  @Override
961  public <T> T draftRestore(Class<T> beanType, Object id, Transaction transaction) {
962    methodCalls.add(MethodCall.of(DRAFT_RESTORE).with("beanType", beanType).with("id", id));
963    return !persistPublish ? null : publish.draftRestore(beanType, id, transaction);
964  }
965
966  @Override
967  public <T> T draftRestore(Class<T> beanType, Object id) {
968    methodCalls.add(MethodCall.of(DRAFT_RESTORE).with("beanType", beanType).with("id", id));
969    return !persistPublish ? null : publish.draftRestore(beanType, id);
970  }
971
972  @Override
973  public <T> List<T> draftRestore(Query<T> query, Transaction transaction) {
974    methodCalls.add(MethodCall.of(DRAFT_RESTORE).with("query", query));
975    return !persistPublish ? null : publish.draftRestore(query, transaction);
976  }
977
978  @Override
979  public <T> List<T> draftRestore(Query<T> query) {
980    methodCalls.add(MethodCall.of(DRAFT_RESTORE).with("query", query));
981    return !persistPublish ? null : publish.draftRestore(query);
982  }
983
984
985  // -- bulkUpdate bulkUpdates ------------------------
986
987
988  @Override
989  public int execute(SqlUpdate sqlUpdate) {
990    methodCalls.add(MethodCall.of("bulkUpdate").with("sqlUpdate", sqlUpdate));
991    return !persistBulkUpdates ? 0 : bulkUpdate.execute(sqlUpdate);
992  }
993
994  @Override
995  public int execute(Update<?> update) {
996    methodCalls.add(MethodCall.of("bulkUpdate").with("update", update));
997    return !persistBulkUpdates ? 0 : bulkUpdate.execute(update);
998  }
999
1000  @Override
1001  public int execute(Update<?> update, Transaction transaction) {
1002    methodCalls.add(MethodCall.of("bulkUpdate").with("update", update, "transaction", transaction));
1003    return !persistBulkUpdates ? 0 : bulkUpdate.execute(update, transaction);
1004  }
1005
1006  @Override
1007  public int execute(CallableSql callableSql) {
1008    methodCalls.add(MethodCall.of("bulkUpdate").with("callableSql", callableSql));
1009    return !persistBulkUpdates ? 0 : bulkUpdate.execute(callableSql);
1010  }
1011
1012  @Override
1013  public int execute(SqlUpdate sqlUpdate, Transaction transaction) {
1014    methodCalls.add(MethodCall.of("bulkUpdate").with("sqlUpdate", sqlUpdate, "transaction", transaction));
1015    return !persistBulkUpdates ? 0 : bulkUpdate.execute(sqlUpdate, transaction);
1016  }
1017
1018  @Override
1019  public int execute(CallableSql callableSql, Transaction transaction) {
1020    methodCalls.add(MethodCall.of("bulkUpdate").with("callableSql", callableSql, "transaction", transaction));
1021    return !persistBulkUpdates ? 0 : bulkUpdate.execute(callableSql, transaction);
1022  }
1023
1024  @Override
1025  public void externalModification(String tableName, boolean inserted, boolean updated, boolean deleted) {
1026
1027    methodCalls.add(MethodCall.of("externalModification")
1028        .with("tableName", tableName)
1029        .with("inserted", inserted, "updated", updated, "deleted", deleted));
1030
1031    if (persistBulkUpdates) {
1032      bulkUpdate.externalModification(tableName, inserted, updated, deleted);
1033    }
1034  }
1035
1036}