Performance is a big problem in software development. And Caching is one solution to speed up system. In the past post, We are familiar with Couchbase database by learning how to build Spring JPA Couchbase application. So in the tutorial, JavaSampleApproach will continue to show you how to create Spring Cache Couchbase application with SpringBoot.
Related posts:
– How to work with Spring Cache | Spring Boot
– Couchbase – How to create Spring JPA Couchbase application with SpringBoot
– SpringBoot Caffeine cache with PostgreSQL backend
– SpringBoot Hazelcast cache with PostgreSQL backend
I. Technologies
– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.5.6RELEASE
– Couchbase 4.6.2
II. Spring Cache Couchbase
We can use Couchbase as backing cache:
For work with SpringCache Couchbase, we need a dependency:
com.couchbase.client couchbase-spring-cache
How to configure Couchbase server as Cache?
-> We need to build a CacheManager
with Couchbase cluster:
@EnableCaching @Configuration public class CacheConfig { ... @Bean(destroyMethod = "disconnect") public Cluster cluster() { return CouchbaseCluster.create(couchbaseServer); } @Bean(destroyMethod = "close") public Bucket bucket() { return cluster().openBucket(couchbaseBucket, couchbasePassword); } @Bean public CacheManager cacheManager() { CacheBuilder cacheBuilder = CacheBuilder.newInstance(bucket()).withExpiration(0); return new CouchbaseCacheManager(cacheBuilder, CACHE_NAME); } }
@EnableCaching
is used to enable the caching.
Document for Couchbase cache is built with Serializable
:
public class Customer implements Serializable { ... }
We use SpringCache annotations to define the ways for caching with each operation {@Cacheable
, @CachePut
, @CacheEvict
}:
– @Cacheable
is one of the most important and common annotation for caching the requests. If the application sends multiple requests, this annotation will not execute the method which is Cachable
multiple times, instead it will return the result from the cached storage.
– @CachePut
updates the cache which is stored and execute the method.
– @CacheEvict
is used for removing a single cache or clearing the entire cache from the cache storage. The method evict uses the input parameter id as a key to find the object that has the same id and removes it from the cache.
III. Practice
In the tutorial, we create a SpringBoot project as below:
Step to do:
– Create SpringBoot project
– Create model class
– Configure Couchbase cache
– Implement repository
– Implement cache service
– Implement business service
– Create Restful APIs
– Deployment
1. Create SpringBoot project
Using SpringToolSuite to create a SpringBoot project, then add dependencies {pring-boot-starter-web
, couchbase-spring-cache
}:
org.springframework.boot spring-boot-starter-web com.couchbase.client couchbase-spring-cache
2. Create model class
– Create a Customer model:
package com.javasampleapproach.couchbase.model; import java.io.Serializable; public class Customer implements Serializable { private static final long serialVersionUID = 1L; private String id; private String firstName; private String lastName; public Customer(String id, String firstName, String lastName){ this.id = id; this.firstName = firstName; this.lastName = lastName; } public String getId() { return this.id; } public void setId(String id) { this.id = id; } public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return this.lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return String.format("Customer[ id=%s, firstName=%s, lastName=%s]", this.id, this.firstName, this.lastName); } }
3. Configure Couchbase cache
Open application.properties file, configure Couchbase server:
jsa.couchbase.server=127.0.0.1 jsa.couchbase.bucket=jsabucket jsa.couchbase.password=123456
Then configure CacheManager
:
package com.javasampleapproach.couchbase.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.couchbase.client.java.Bucket; import com.couchbase.client.java.Cluster; import com.couchbase.client.java.CouchbaseCluster; import com.couchbase.client.spring.cache.CacheBuilder; import com.couchbase.client.spring.cache.CouchbaseCacheManager; @EnableCaching @Configuration public class CacheConfig { public static final String CACHE_NAME = "customers"; @Value("${jsa.couchbase.server}") private String couchbaseServer; @Value("${jsa.couchbase.bucket}") private String couchbaseBucket; @Value("${jsa.couchbase.password}") private String couchbasePassword; @Bean(destroyMethod = "disconnect") public Cluster cluster() { // connect to the Couchbase server running on your local machine return CouchbaseCluster.create(couchbaseServer); } @Bean(destroyMethod = "close") public Bucket bucket() { // connect to the bucket named 'jsabucket' (which must exist on your Couchbase server) // every cache related element will use this bucket return cluster().openBucket(couchbaseBucket, couchbasePassword); } @Bean public CacheManager cacheManager() { CacheBuilder cacheBuilder = CacheBuilder.newInstance(bucket()).withExpiration(0); return new CouchbaseCacheManager(cacheBuilder, CACHE_NAME); } }
4. Implement repository
– Create an interface CustomerRepository
:
package com.javasampleapproach.couchbase.repo; import com.javasampleapproach.couchbase.model.Customer; public interface CustomerRepository { public Customer getById(long id); public Customer put(String firstName, long id); }
– Implement above CustomerRepository
:
package com.javasampleapproach.couchbase.repo.impl; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Repository; import com.javasampleapproach.couchbase.model.Customer; import com.javasampleapproach.couchbase.repo.CustomerRepository; @Repository public class CustomerRepositoryImpl implements CustomerRepository{ private static Mapstore = new HashMap (); static{ store.put(1L, new Customer("1", "Jack", "Smith")); store.put(2L, new Customer("2", "Adam", "Johnson")); } @Override public Customer getById(long id) { System.out.println("Service processing..."); // provide a delay time for simulating slowly processing try{ Thread.sleep(3000); }catch(Exception e){ } Customer cust = store.get(id); return cust; } @Override public Customer put(String firstName, long id) { Customer cust = store.get(id); cust.setFirstName(firstName); return cust; } }
5. Implement cache service
Using SpringCache annotations to implement CustomerCache:
package com.javasampleapproach.couchbase.service.cache; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; import com.javasampleapproach.couchbase.config.CacheConfig; import com.javasampleapproach.couchbase.model.Customer; import com.javasampleapproach.couchbase.repo.CustomerRepository; @Component public class CustomerCache { @Autowired CustomerRepository customerRepository; @CachePut(value=CacheConfig.CACHE_NAME, key="#id") public Customer putOnCache(String firstName, long id){ return customerRepository.put(firstName, id); } @Cacheable(value=CacheConfig.CACHE_NAME, key="#id") public Customer getOnCache(long id){ return customerRepository.getById(id); } @CacheEvict(value = CacheConfig.CACHE_NAME, key = "#id") public void evict(long id){ } }
6. Implement business service
Implement CustomerServices on top of above CustomerCache:
package com.javasampleapproach.couchbase.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.javasampleapproach.couchbase.model.Customer; import com.javasampleapproach.couchbase.service.cache.CustomerCache; @Service public class CustomerServices { @Autowired CustomerCache customerCache; public Customer putCustomer(String firstName, long id){ return customerCache.putOnCache(firstName, id); } public Customer get(long id){ return customerCache.getOnCache(id); } public void evict(long id){ customerCache.evict(id); } }
7. Create Restful APIs
Create 3 RestAPIs {/cachable
, /cacheput
, /cacheevict
}:
package com.javasampleapproach.couchbase.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.javasampleapproach.couchbase.model.Customer; import com.javasampleapproach.couchbase.service.CustomerServices; @RestController public class WebController { @Autowired CustomerServices service; @RequestMapping("/cachable") public Customer get(@RequestParam("id")long id){ return service.get(id); } @RequestMapping("/cacheput") public String put(@RequestParam("firstname") String firstName, @RequestParam("id")long id){ service.putCustomer(firstName, id); return "Done"; } @RequestMapping("/cacheevict") public String evict(@RequestParam("id")long id){ service.evict(id); return "Done"; } }
8. Deployment
8.1 Setup Couchbase server
Follow the guide to setup Couchbase server and create a jsabucket
8.2 Run and check results
Check document in jsabucket before run above SpringBoot project.
Build and Run the SpringBoot project as commanlines {mvn clean install
, mvn spring-boot:run
}.
– Request 1: http://localhost:8080/cachable?id=1
-> Results:
Service process slowly and,
Server has displayed a text on console:
Service processing...
Check documents of Couchebase jsabucket:
– Request 2: http://localhost:8080/cachable?id=1
Now the response is faster because Customer
with id = 1 has been cached before, the application just get data from cache storage.
– Request 3: http://localhost:8080/cacheput?id=1&firstname=Peter
Message is returned on Browser:
Done
Now customer with id=1 is modified: firstname=”Peter”, not “Jack”.
– Request 4: http://localhost:8080/cachable?id=1
Response is faster BUT the result is difference from first request:
– Request 5 – Make a cache-evict request: http://localhost:8080/cacheevict?id=1
Browser displays:
Done
Now customer with id=1 was evicted from cache storage.
– Request 6: http://localhost:8080/cachable?id=1
Now the behavior of the browser is the same as the first request because customer with id=1 was evicted to cache, and the method under @Cachable
is executed.
Service process slowly and result:
Server, of course, displays a text on console:
Service processing...