Skip to content

Commit c8ec0ac

Browse files
committed
Reinstate CouchbaseCache documentation.
Closes #1356.
1 parent fedd4c4 commit c8ec0ac

File tree

7 files changed

+139
-6
lines changed

7 files changed

+139
-6
lines changed

src/main/asciidoc/caching.adoc

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
[[couchbase.caching]]
2+
= Caching
3+
4+
This chapter describes additional support for caching and `@Cacheable`.
5+
6+
[[caching.usage]]
7+
== Configuration & Usage
8+
9+
Technically, caching is not part of spring-data, but is implemented directly in the spring core. Most database implementations in the spring-data package can't support `@Cacheable`, because it is not possible to store arbitrary data.
10+
11+
Couchbase supports both binary and JSON data, so you can get both out of the same database.
12+
13+
To make it work, you need to add the `@EnableCaching` annotation and configure the `cacheManager` bean:
14+
15+
.`AbstractCouchbaseConfiguration` for Caching
16+
====
17+
[source,java]
18+
----
19+
20+
@Configuration
21+
@EnableCaching
22+
public class Config extends AbstractCouchbaseConfiguration {
23+
// general methods
24+
25+
@Bean
26+
public CouchbaseCacheManager cacheManager(CouchbaseTemplate couchbaseTemplate) throws Exception {
27+
CouchbaseCacheManager.CouchbaseCacheManagerBuilder builder = CouchbaseCacheManager.CouchbaseCacheManagerBuilder
28+
.fromConnectionFactory(couchbaseTemplate.getCouchbaseClientFactory());
29+
builder.withCacheConfiguration("mySpringCache", CouchbaseCacheConfiguration.defaultCacheConfig());
30+
return builder.build();
31+
}
32+
----
33+
====
34+
35+
The `persistent` identifier can then be used on the `@Cacheable` annotation to identify the cache manager to use (you can have more than one configured).
36+
37+
Once it is set up, you can annotate every method with the `@Cacheable` annotation to transparently cache it in your couchbase bucket. You can also customize how the key is generated.
38+
39+
.Caching example
40+
====
41+
[source,java]
42+
----
43+
@Cacheable(value="persistent", key="'longrunsim-'+#time")
44+
public String simulateLongRun(long time) {
45+
try {
46+
Thread.sleep(time);
47+
} catch(Exception ex) {
48+
System.out.println("This shouldnt happen...");
49+
}
50+
return "I've slept " + time + " miliseconds.;
51+
}
52+
----
53+
====
54+
55+
If you run the method multiple times, you'll see a set operation happening first, followed by multiple get operations and no sleep time (which fakes the expensive execution). You can store whatever you want, if it is JSON of course you can access it through views and look at it in the Web UI.
56+
57+
Note that to use cache.clear() or catch.invalidate(), the bucket must have a primary key.

src/main/asciidoc/index.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ include::template.adoc[]
2626
include::transactions.adoc[]
2727
include::collections.adoc[]
2828
include::ansijoins.adoc[]
29+
include::caching.adoc[]
2930
:leveloffset: -1
3031

3132
[[appendix]]

src/test/java/org/springframework/data/couchbase/cache/CouchbaseCacheCollectionIntegrationTests.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
*
3737
* @author Michael Reiche
3838
*/
39-
@IgnoreWhen(clusterTypes = ClusterType.MOCKED, missesCapabilities = { Capabilities.COLLECTIONS })
39+
@IgnoreWhen(clusterTypes = ClusterType.MOCKED, missesCapabilities = { Capabilities.COLLECTIONS })
4040
class CouchbaseCacheCollectionIntegrationTests extends CollectionAwareIntegrationTests {
4141

4242
volatile CouchbaseCache cache;
@@ -58,7 +58,6 @@ private void clear(CouchbaseCache c) {
5858
QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS));
5959
}
6060

61-
6261
@Test
6362
void cachePutGet() {
6463
CacheUser user1 = new CacheUser(UUID.randomUUID().toString(), "first1", "last1");

src/test/java/org/springframework/data/couchbase/cache/CouchbaseCacheIntegrationTests.java

+51
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@
2121
import static org.junit.jupiter.api.Assertions.assertEquals;
2222
import static org.junit.jupiter.api.Assertions.assertNull;
2323

24+
import java.util.List;
2425
import java.util.UUID;
2526

27+
import org.junit.jupiter.api.AfterEach;
2628
import org.junit.jupiter.api.BeforeEach;
2729
import org.junit.jupiter.api.Test;
30+
import org.springframework.beans.factory.annotation.Autowired;
31+
import org.springframework.context.ApplicationContext;
32+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
33+
import org.springframework.data.couchbase.domain.Config;
34+
import org.springframework.data.couchbase.domain.User;
35+
import org.springframework.data.couchbase.domain.UserRepository;
2836
import org.springframework.data.couchbase.util.ClusterType;
2937
import org.springframework.data.couchbase.util.IgnoreWhen;
3038
import org.springframework.data.couchbase.util.JavaIntegrationTests;
@@ -40,6 +48,8 @@
4048
class CouchbaseCacheIntegrationTests extends JavaIntegrationTests {
4149

4250
volatile CouchbaseCache cache;
51+
@Autowired CouchbaseCacheManager cacheManager; // autowired not working
52+
@Autowired UserRepository userRepository; // autowired not working
4353

4454
@BeforeEach
4555
@Override
@@ -48,6 +58,16 @@ public void beforeEach() {
4858
cache = CouchbaseCacheManager.create(couchbaseTemplate.getCouchbaseClientFactory()).createCouchbaseCache("myCache",
4959
CouchbaseCacheConfiguration.defaultCacheConfig());
5060
clear(cache);
61+
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
62+
cacheManager = ac.getBean(CouchbaseCacheManager.class);
63+
userRepository = ac.getBean(UserRepository.class);
64+
}
65+
66+
@AfterEach
67+
@Override
68+
public void afterEach() {
69+
clear(cache);
70+
super.afterEach();
5171
}
5272

5373
private void clear(CouchbaseCache c) {
@@ -69,6 +89,19 @@ void cachePutGet() {
6989
assertEquals(user2, cache.get(user2.getId()).get()); // get user2
7090
}
7191

92+
@Test
93+
void cacheable() {
94+
User user = new User("cache_92", "Dave", "Wilson");
95+
cacheManager.getCache("mySpringCache").clear();
96+
userRepository.save(user);
97+
long t0 = System.currentTimeMillis();
98+
List<User> users = userRepository.getByFirstname(user.getFirstname());
99+
assert (System.currentTimeMillis() - t0 > 1000 * 5);
100+
t0 = System.currentTimeMillis();
101+
users = userRepository.getByFirstname(user.getFirstname());
102+
assert (System.currentTimeMillis() - t0 < 100);
103+
}
104+
72105
@Test
73106
void cacheEvict() {
74107
CacheUser user1 = new CacheUser(UUID.randomUUID().toString(), "first1", "last1");
@@ -98,4 +131,22 @@ void cachePutIfAbsent() {
98131
assertEquals(user1, cache.get(user1.getId()).get()); // user1.getId() is still user1
99132
}
100133

134+
@Test // this test FAILS (local empty (i.e. fast) Couchbase installation)
135+
public void clearFail() {
136+
cache.put("KEY", "VALUE"); // no delay between put and clear, entry will not be
137+
cache.clear(); // will not be indexed when clear() executes
138+
assertNotNull(cache.get("KEY")); // will still find entry, clear failed to delete
139+
}
140+
141+
@Test // this WORKS
142+
public void clearWithDelayOk() throws InterruptedException {
143+
cache.put("KEY", "VALUE");
144+
Thread.sleep(50); // give main index time to update
145+
cache.clear();
146+
assertNull(cache.get("KEY"));
147+
}
148+
149+
@Test
150+
public void noOpt() {}
151+
101152
}

src/test/java/org/springframework/data/couchbase/domain/Config.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@
1717
package org.springframework.data.couchbase.domain;
1818

1919
import java.lang.reflect.InvocationTargetException;
20+
import java.util.HashMap;
21+
import java.util.Map;
2022

23+
import org.springframework.cache.annotation.EnableCaching;
2124
import org.springframework.context.annotation.Bean;
2225
import org.springframework.context.annotation.Configuration;
2326
import org.springframework.data.auditing.DateTimeProvider;
2427
import org.springframework.data.couchbase.CouchbaseClientFactory;
2528
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
29+
import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration;
30+
import org.springframework.data.couchbase.cache.CouchbaseCacheManager;
2631
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
2732
import org.springframework.data.couchbase.core.CouchbaseTemplate;
2833
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
@@ -53,7 +58,7 @@
5358
@EnableReactiveCouchbaseRepositories
5459
@EnableCouchbaseAuditing(dateTimeProviderRef = "dateTimeProviderRef")
5560
@EnableReactiveCouchbaseAuditing(dateTimeProviderRef = "dateTimeProviderRef")
56-
61+
@EnableCaching
5762
public class Config extends AbstractCouchbaseConfiguration {
5863
String bucketname = "travel-sample";
5964
String username = "Administrator";
@@ -214,6 +219,14 @@ public TranslationService couchbaseTranslationService() {
214219
return jacksonTranslationService;
215220
}
216221

222+
@Bean
223+
public CouchbaseCacheManager cacheManager(CouchbaseTemplate couchbaseTemplate) throws Exception {
224+
CouchbaseCacheManager.CouchbaseCacheManagerBuilder builder = CouchbaseCacheManager.CouchbaseCacheManagerBuilder
225+
.fromConnectionFactory(couchbaseTemplate.getCouchbaseClientFactory());
226+
//CouchbaseCacheConfiguration cfg = CouchbaseCacheConfiguration.defaultCacheConfig().withCacheConfiguration("mySpringCache", CouchbaseCacheConfiguration.defaultCacheConfig());
227+
return builder.build();
228+
}
229+
217230
@Override
218231
public String typeKey() {
219232
return "t"; // this will override '_class', is passed in to new CustomMappingCouchbaseConverter

src/test/java/org/springframework/data/couchbase/domain/User.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.springframework.data.annotation.PersistenceConstructor;
2020
import org.springframework.data.couchbase.core.mapping.Document;
2121

22+
import java.io.Serializable;
23+
2224
/**
2325
* User entity for tests
2426
*
@@ -27,7 +29,7 @@
2729
*/
2830

2931
@Document
30-
public class User extends AbstractUser {
32+
public class User extends AbstractUser implements Serializable {
3133

3234
@PersistenceConstructor
3335
public User(final String id, final String firstname, final String lastname) {

src/test/java/org/springframework/data/couchbase/domain/UserRepository.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
import java.util.List;
2020
import java.util.stream.Stream;
2121

22-
import com.couchbase.client.java.query.QueryScanConsistency;
22+
import org.springframework.cache.annotation.Cacheable;
2323
import org.springframework.data.couchbase.repository.CouchbaseRepository;
2424
import org.springframework.data.couchbase.repository.Query;
2525
import org.springframework.data.couchbase.repository.ScanConsistency;
2626
import org.springframework.data.repository.query.Param;
2727
import org.springframework.stereotype.Repository;
2828

2929
import com.couchbase.client.java.json.JsonArray;
30+
import com.couchbase.client.java.query.QueryScanConsistency;
3031

3132
/**
3233
* User Repository for tests
@@ -35,7 +36,7 @@
3536
* @author Michael Reiche
3637
*/
3738
@Repository
38-
@ScanConsistency(query=QueryScanConsistency.REQUEST_PLUS)
39+
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
3940
public interface UserRepository extends CouchbaseRepository<User, String> {
4041

4142
List<User> findByFirstname(String firstname);
@@ -57,4 +58,13 @@ public interface UserRepository extends CouchbaseRepository<User, String> {
5758
List<User> findByIdIsNotNullAndFirstnameEquals(String firstname);
5859

5960
List<User> findByVersionEqualsAndFirstnameEquals(Long version, String firstname);
61+
62+
// simulate a slow operation
63+
@Cacheable("mySpringCache")
64+
default List<User> getByFirstname(String firstname) {
65+
try {
66+
Thread.sleep(1000 * 5);
67+
} catch (InterruptedException ie) {}
68+
return findByFirstname(firstname);
69+
}
6070
}

0 commit comments

Comments
 (0)