package org.molgenis.data.transaction; import org.molgenis.data.*; import org.molgenis.data.aggregation.AggregateQuery; import org.molgenis.data.aggregation.AggregateResult; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; import static java.util.Objects.requireNonNull; /** * Repository decorator that wraps CRUD operations in a (read-only) transaction. Classes that extend from * {@link AbstractRepositoryDecorator} might not be managed by Spring, so {@link TransactionTemplate} is used instead * of the {@link Transactional} annotation. * * @param <E> entity type */ public class TransactionalRepositoryDecorator<E extends Entity> extends AbstractRepositoryDecorator<E> { private final Repository<E> decoratedRepo; private final PlatformTransactionManager transactionManager; public TransactionalRepositoryDecorator(Repository<E> decoratedRepo, PlatformTransactionManager transactionManager) { this.decoratedRepo = requireNonNull(decoratedRepo); this.transactionManager = requireNonNull(transactionManager); } @Override protected Repository<E> delegate() { return decoratedRepo; } @Override public void forEachBatched(Consumer<List<E>> consumer, int batchSize) { createReadonlyTransactionTemplate().execute((status) -> { decoratedRepo.forEachBatched(consumer, batchSize); return null; }); } @Override public void forEachBatched(Fetch fetch, Consumer<List<E>> consumer, int batchSize) { createReadonlyTransactionTemplate().execute((status) -> { decoratedRepo.forEachBatched(fetch, consumer, batchSize); return null; }); } @Override public long count() { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.count()); } @Override public long count(Query<E> q) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.count(q)); } @Override public Stream<E> findAll(Query<E> q) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.findAll(q)); } @Override public E findOne(Query<E> q) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.findOne(q)); } @Override public E findOneById(Object id) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.findOneById(id)); } @Override public E findOneById(Object id, Fetch fetch) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.findOneById(id, fetch)); } @Override public Stream<E> findAll(Stream<Object> ids) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.findAll(ids)); } @Override public Stream<E> findAll(Stream<Object> ids, Fetch fetch) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.findAll(ids, fetch)); } @Override public AggregateResult aggregate(AggregateQuery aggregateQuery) { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.aggregate(aggregateQuery)); } @Override public void update(E entity) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.update(entity); return null; }); } @Override public void update(Stream<E> entities) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.update(entities); return null; }); } @Override public void delete(E entity) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.delete(entity); return null; }); } @Override public void delete(Stream<E> entities) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.delete(entities); return null; }); } @Override public void deleteById(Object id) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.deleteById(id); return null; }); } @Override public void deleteAll(Stream<Object> ids) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.deleteAll(ids); return null; }); } @Override public void deleteAll() { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.deleteAll(); return null; }); } @Override public void add(E entity) { createWriteTransactionTemplate().execute((status) -> { decoratedRepo.add(entity); return null; }); } @Override public Integer add(Stream<E> entities) { return createWriteTransactionTemplate().execute((status) -> decoratedRepo.add(entities)); } @Override public Iterator<E> iterator() { return createReadonlyTransactionTemplate().execute((status) -> decoratedRepo.iterator()); } private TransactionTemplate createWriteTransactionTemplate() { return new TransactionTemplate(transactionManager); } private TransactionTemplate createReadonlyTransactionTemplate() { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.setReadOnly(true); return transactionTemplate; } }