Introduce evictIfPresent/invalidate operations on Cache abstraction

@CacheEvict.beforeInvocation suggests immediate execution even in case of transactional caches. The cache interceptor delegates to the new evictIfPresent/invalidate operations now which imply immediate execution semantics (and also provide an indication for whether any corresponding entries where present when programmatically called).

Closes gh-23192
This commit is contained in:
Juergen Hoeller
2019-07-17 22:37:55 +02:00
parent 2c33c11d4c
commit ffc1f242ca
15 changed files with 285 additions and 86 deletions

View File

@@ -21,25 +21,23 @@ import org.junit.Test;
import org.springframework.cache.Cache;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.tests.transaction.CallCountingTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.support.TransactionTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* @author Stephane Nicoll
* @author Juergen Hoeller
*/
public class TransactionAwareCacheDecoratorTests {
private final PlatformTransactionManager txManager = new CallCountingTransactionManager();
private final TransactionTemplate txTemplate = new TransactionTemplate(new CallCountingTransactionManager());
@Test
public void createWithNullTarget() {
assertThatIllegalArgumentException().isThrownBy(() ->
new TransactionAwareCacheDecorator(null));
assertThatIllegalArgumentException().isThrownBy(() -> new TransactionAwareCacheDecorator(null));
}
@Test
@@ -79,20 +77,18 @@ public class TransactionAwareCacheDecoratorTests {
public void putTransactional() {
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
TransactionStatus status = this.txManager.getTransaction(
new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED));
Object key = new Object();
cache.put(key, "123");
assertThat(target.get(key)).isNull();
this.txManager.commit(status);
txTemplate.execute(() -> {
cache.put(key, "123");
assertThat(target.get(key)).isNull();
});
assertThat(target.get(key, String.class)).isEqualTo("123");
}
@Test
public void putIfAbsent() { // no transactional support for putIfAbsent
public void putIfAbsentNonTransactional() {
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
@@ -104,6 +100,23 @@ public class TransactionAwareCacheDecoratorTests {
assertThat(target.get(key, String.class)).isEqualTo("123");
}
@Test
public void putIfAbsentTransactional() { // no transactional support for putIfAbsent
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
Object key = new Object();
txTemplate.execute(() -> {
assertThat(cache.putIfAbsent(key, "123")).isNull();
assertThat(target.get(key, String.class)).isEqualTo("123");
assertThat(cache.putIfAbsent(key, "456").get()).isEqualTo("123");
// unchanged
assertThat(target.get(key, String.class)).isEqualTo("123");
});
assertThat(target.get(key, String.class)).isEqualTo("123");
}
@Test
public void evictNonTransactional() {
Cache target = new ConcurrentMapCache("testCache");
@@ -122,12 +135,36 @@ public class TransactionAwareCacheDecoratorTests {
Object key = new Object();
cache.put(key, "123");
txTemplate.execute(() -> {
cache.evict(key);
assertThat(target.get(key, String.class)).isEqualTo("123");
});
TransactionStatus status = this.txManager.getTransaction(
new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED));
cache.evict(key);
assertThat(target.get(key, String.class)).isEqualTo("123");
this.txManager.commit(status);
assertThat(target.get(key)).isNull();
}
@Test
public void evictIfPresentNonTransactional() {
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
Object key = new Object();
cache.put(key, "123");
cache.evictIfPresent(key);
assertThat(target.get(key)).isNull();
}
@Test
public void evictIfPresentTransactional() { // no transactional support for evictIfPresent
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
Object key = new Object();
cache.put(key, "123");
txTemplate.execute(() -> {
cache.evictIfPresent(key);
assertThat(target.get(key)).isNull();
});
assertThat(target.get(key)).isNull();
}
@@ -150,13 +187,38 @@ public class TransactionAwareCacheDecoratorTests {
Object key = new Object();
cache.put(key, "123");
TransactionStatus status = this.txManager.getTransaction(
new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED));
cache.clear();
assertThat(target.get(key, String.class)).isEqualTo("123");
this.txManager.commit(status);
txTemplate.execute(() -> {
cache.clear();
assertThat(target.get(key, String.class)).isEqualTo("123");
});
assertThat(target.get(key)).isNull();
}
@Test
public void invalidateNonTransactional() {
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
Object key = new Object();
cache.put(key, "123");
cache.invalidate();
assertThat(target.get(key)).isNull();
}
@Test
public void invalidateTransactional() { // no transactional support for invalidate
Cache target = new ConcurrentMapCache("testCache");
Cache cache = new TransactionAwareCacheDecorator(target);
Object key = new Object();
cache.put(key, "123");
txTemplate.execute(() -> {
cache.invalidate();
assertThat(target.get(key)).isNull();
});
assertThat(target.get(key)).isNull();
}
}