From abd775e3f38009d3da1bf70b07b8e6f40be68690 Mon Sep 17 00:00:00 2001 From: mikereiche Date: Sat, 15 Jan 2022 10:51:38 -0800 Subject: [PATCH] Allow caching null objects. Closes #939. --- .../data/couchbase/cache/CouchbaseCache.java | 4 +- .../data/couchbase/cache/CacheUser.java | 71 ++++++++++++ .../cache/CouchbaseCacheIntegrationTests.java | 101 ++++++++++++++++++ 3 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/springframework/data/couchbase/cache/CacheUser.java create mode 100644 src/test/java/org/springframework/data/couchbase/cache/CouchbaseCacheIntegrationTests.java diff --git a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCache.java b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCache.java index cfcd22b9d..8df20f754 100644 --- a/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCache.java +++ b/src/main/java/org/springframework/data/couchbase/cache/CouchbaseCache.java @@ -106,7 +106,7 @@ public void put(final Object key, final Object value) { name)); } - cacheWriter.put(cacheConfig.getCollectionName(), createCacheKey(key), value, cacheConfig.getExpiry(), + cacheWriter.put(cacheConfig.getCollectionName(), createCacheKey(key), toStoreValue(value), cacheConfig.getExpiry(), cacheConfig.getValueTranscoder()); } @@ -116,7 +116,7 @@ public ValueWrapper putIfAbsent(final Object key, final Object value) { return get(key); } - Object result = cacheWriter.putIfAbsent(cacheConfig.getCollectionName(), createCacheKey(key), value, + Object result = cacheWriter.putIfAbsent(cacheConfig.getCollectionName(), createCacheKey(key), toStoreValue(value), cacheConfig.getExpiry(), cacheConfig.getValueTranscoder()); if (result == null) { diff --git a/src/test/java/org/springframework/data/couchbase/cache/CacheUser.java b/src/test/java/org/springframework/data/couchbase/cache/CacheUser.java new file mode 100644 index 000000000..3bcad08d8 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/cache/CacheUser.java @@ -0,0 +1,71 @@ +/* + * Copyright 2022 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.cache; + +import java.io.Serializable; + +/** + * This is a standalone class (vs. inner) to allow Serialization of all fields to work. + * If it was an inner class of CouchbaseCacheIntegrationTests, then it would have a + * this$0 field = CouchbaseCacheIntegrationTests and would not serialize. + * + * @author Michael Reiche + */ +class CacheUser implements Serializable { + // private static final long serialVersionUID = 8817717605659870262L; + String firstname; + String lastname; + String id; + + public CacheUser(String id, String firstname, String lastname) { + this.id = id; + this.firstname = firstname; + this.lastname = lastname; + } + + public String getId() { + return id; + } + + // define equals for assertEquals() + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (!(o instanceof CacheUser)) { + return false; + } + + CacheUser other = (CacheUser) o; + if (id == null && other.id != null) { + return false; + } + if (firstname == null && other.firstname != null) { + return false; + } + if (lastname == null && other.lastname != null) { + return false; + } + return id.equals(other.id) && firstname.equals(other.firstname) && lastname.equals(other.lastname); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("CacheUser: { id=" + id + ", firstname=" + firstname + ", lastname=" + lastname + "}"); + return sb.toString(); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/cache/CouchbaseCacheIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/cache/CouchbaseCacheIntegrationTests.java new file mode 100644 index 000000000..8dadb39c3 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/cache/CouchbaseCacheIntegrationTests.java @@ -0,0 +1,101 @@ +/* + * Copyright 2022 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.cache; + +import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS; +import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.couchbase.util.ClusterType; +import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.data.couchbase.util.JavaIntegrationTests; + +import com.couchbase.client.java.query.QueryOptions; + +/** + * CouchbaseCache tests Theses tests rely on a cb server running. + * + * @author Michael Reiche + */ +@IgnoreWhen(clusterTypes = ClusterType.MOCKED) +class CouchbaseCacheIntegrationTests extends JavaIntegrationTests { + + volatile CouchbaseCache cache; + + @BeforeEach + @Override + public void beforeEach() { + super.beforeEach(); + cache = CouchbaseCacheManager.create(couchbaseTemplate.getCouchbaseClientFactory()).createCouchbaseCache("myCache", + CouchbaseCacheConfiguration.defaultCacheConfig()); + clear(cache); + } + + private void clear(CouchbaseCache c) { + couchbaseTemplate.getCouchbaseClientFactory().getCluster().query("SELECT count(*) from `" + bucketName() + "`", + QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)); + c.clear(); + couchbaseTemplate.getCouchbaseClientFactory().getCluster().query("SELECT count(*) from `" + bucketName() + "`", + QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)); + } + + @Test + void cachePutGet() { + CacheUser user1 = new CacheUser(UUID.randomUUID().toString(), "first1", "last1"); + CacheUser user2 = new CacheUser(UUID.randomUUID().toString(), "first2", "last2"); + assertNull(cache.get(user1.getId())); // was not put -> cacheMiss + cache.put(user1.getId(), user1); // put user1 + cache.put(user2.getId(), user2); // put user2 + assertEquals(user1, cache.get(user1.getId()).get()); // get user1 + assertEquals(user2, cache.get(user2.getId()).get()); // get user2 + } + + @Test + void cacheEvict() { + CacheUser user1 = new CacheUser(UUID.randomUUID().toString(), "first1", "last1"); + CacheUser user2 = new CacheUser(UUID.randomUUID().toString(), "first2", "last2"); + cache.put(user1.getId(), user1); // put user1 + cache.put(user2.getId(), user2); // put user2 + cache.evict(user1.getId()); // evict user1 + assertEquals(user2, cache.get(user2.getId()).get()); // get user2 -> present + } + + @Test + void cacheHitMiss() { + CacheUser user1 = new CacheUser(UUID.randomUUID().toString(), "first1", "last1"); + CacheUser user2 = new CacheUser(UUID.randomUUID().toString(), "first2", "last2"); + assertNull(cache.get(user2.getId())); // get user2 -> cacheMiss + cache.put(user1.getId(), null); // cache a null + assertNotNull(cache.get(user1.getId())); // cacheHit null + assertNull(cache.get(user1.getId()).get()); // fetch cached null + } + + @Test + void cachePutIfAbsent() { + CacheUser user1 = new CacheUser(UUID.randomUUID().toString(), "first1", "last1"); + CacheUser user2 = new CacheUser(UUID.randomUUID().toString(), "first2", "last2"); + assertNull(cache.putIfAbsent(user1.getId(), user1)); // should put user1, return null + assertEquals(user1, cache.putIfAbsent(user1.getId(), user2).get()); // should not put user2, should return user1 + assertEquals(user1, cache.get(user1.getId()).get()); // user1.getId() is still user1 + } + +}