The focus of NixMash Spring v0.4.4 is Spring Caching where we'll start by adding caching to the blogging module and afterward to other functions. We've added Post Caching to three data retrieval points:
We'll cover the details in this post as well as show you how to add Caching to your Spring Boot application.
We'll begin in our build.gradle file where we are adding the Spring Boot Cache Starter dependency. You see spring-boot-starter-cache about half-way down the list. Javax.cache:cache-api is another addition to the list. This gives us JCache JSR-107 support, or the ability to use @CacheResult and the other JCache annotations. We'll be using EhCache 3 as our Cache Provider which is at the bottom of the dependency list.
Since we're using EhCache 3 we have an ehcache3.xml configuration file in /resources. We have two caches: posts and pagedPosts.
I should add that we added Spring Caching in our JPA Module since JPA is our primary data retrieval module. Below is the JPA Launcher class where we see the addition of the @EnableCaching annotation to our JpaLauncher class.
We'll complete our Spring Caching setup in our JpaLoader class, which, my friend, is all of the Java Code required to enable Caching in Spring Boot!
For completeness we added a single caching property to our application.properties file.
Now let's fire up our app (here from a Gradle bootRun in our MVC module) to confirm we're cooking with Spring Cache and EhCache.
Now we arrive at the fun part, adding caching to our primary Post retrieval points. As I said, we added caching to retrieving posts by ID, by post name, and paged posts used to generate the Post Streams. We will do this in the PostServiceImpl.java class. Since we are primarily working with the posts Cache, we'll add it in our class @CacheConfig.
A helpful way to understand Spring Caches is that a cache is essentially a key-value store, with each invocation of a cached method translated into a suitable key for cache access. Spring Caching uses the method signature to generate the key-value relationship, so in our case we don't need to add the SpEL #postId and #postName keys. Spring does that automatically.
Our third cached data point is the Paged Posts list used to generate the Post Streams. We always call the getPublishedPosts() method with page number and number of posts per page, so we'll use that unique combination for our pagedPosts Cache Key. Our keys will be in the form of “0-20”, “1-20”, etc.
We don't have a post deletion function, so the two methods affecting our Post Caches are add(PostDTO) and update(PostDTO). Hey, wait a minute, what's this @CachePostUpdate? That's not a Spring or JCache Annotation?!
To prevent having to repeat ourselves we created an Annotation Interface.
For adding and updating posts we'll use @CachePut on our posts caches, and clear the pagedPosts cache with @CacheEvict. Notice an interesting key for the posts postId cache key: #result.postId. Spring needs a postId to serve as the Cache Key. Our add(PostDTO) method takes a PostDTO which does NOT have a PostId value, but it returns a new Post object which DOES contain a PostId. Thankfully Spring gives us the flexibility to use the method #result object to create our Cache Key. Pretty cool.
Let's take a look at how Spring Caching affects the retrieval of paged posts and ultimately the generation of our Post Stream. We're going to retrieve two pages of posts, each twice in succession to compare the effect of caching verses no caching. In our first example we are NOT CACHING the getPublishedPosts() method. The second post retrieval call is faster than the first, but that is only because Hibernate has some built-in caching.
Now let's look at getPublishedPosts() retrieval performance WITH CACHING. Wow!
We're going to look at a test for refreshing our three Post Caches on a post update where we replace the cached Post with the updated Post in our posts caches and clear our pagedPosts cache. Here we show the test @Before and @After for context.
Now for the test.