Table of contents
- Java Spring Code Example
- Explanation
- Important Notes
- Simple Application Caching Example with Spring
Application caching in Spring allows you to store frequently accessed data in memory, reducing the need to fetch it from the source every time. This can significantly improve the performance of your application.
Java Spring Code Example
// Import necessary libraries
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class DataService {
// Define a cache named "userData"
@Cacheable(value = "userData", key = "#userId")
public UserData getUserData(int userId) {
// Simulate fetching data from a database
// Replace this with your actual data retrieval logic
return fetchDataFromDatabase(userId);
}
private UserData fetchDataFromDatabase(int userId) {
// Simulate database query
// Replace this with your actual database query
System.out.println("Fetching data from database for user ID: " + userId);
// Assuming this method returns UserData object
return new UserData(userId, "John Doe");
}
}
In this example, we're creating a DataService
class that contains a method getUserData(int userId)
. This method is annotated with @Cacheable
which indicates that the result of this method should be cached.
@Cacheable(value = "userData", key = "#userId")
: This annotation specifies that the method's result should be cached. Thevalue
attribute represents the name of the cache (in this case, "userData"). Thekey
attribute specifies how to generate the cache key, in this case, it's based on theuserId
.fetchDataFromDatabase(int userId)
: This is a simulated method that fetches data from a database. In a real-world scenario, this is where you would perform your actual data retrieval logic.
Explanation
When
getUserData(int userId)
is called for the first time with a specificuserId
, Spring checks if there's already a cached result for the givenuserId
.If there's a cache hit (i.e., the result is already cached), Spring returns the cached result without executing the method.
If it's a cache miss (i.e., the result is not cached), Spring executes the method
fetchDataFromDatabase(int userId)
.The result is returned and cached using the specified cache name ("userData") and cache key (based on the
userId
).Subsequent calls with the same
userId
will retrieve the result from the cache without executing the method again.
Important Notes
Ensure you have a caching provider configured in your Spring application (e.g., using Redis or Ehcache).
Make sure to handle cache eviction and cache invalidation based on your application's requirements.
This example demonstrates how Spring's caching mechanism can be employed to efficiently handle frequently accessed data. Replace the simulated database access with your actual data retrieval logic.
Simple Application Caching Example with Spring
For this example, let's imagine we have a service that fetches user details by their ID from a database. We want to use caching to improve the performance of this operation.
Step 1: Set Up a Spring Boot Project
Create a new Spring Boot project using Spring Initializer (https://start.spring.io/).
Add the dependencies for "Spring Web" and "Spring Cache" to your project.
Step 2: Enable Caching in Your Application
Open your main application class (usually Application.java
) and add @EnableCaching
annotation to enable caching in your Spring application:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Step 3: Create a Service Class
Next, create a service class that will contain the method we want to cache. In this case, let's create a class named UserService
.
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable(value = "userData", key = "#userId")
public UserData getUserData(int userId) {
// Simulate fetching data from a database
return fetchDataFromDatabase(userId);
}
private UserData fetchDataFromDatabase(int userId) {
// Simulate database query
System.out.println("Fetching data from database for user ID: " + userId);
return new UserData(userId, "John Doe");
}
}
Step 4: Create a Model Class
Create a simple UserData
class to represent user data.
public class UserData {
private int id;
private String name;
// Constructor, getters, and setters
// ...
}
Step 5: Create a Controller
Create a controller class to handle HTTP requests.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{userId}")
public UserData getUserData(@PathVariable int userId) {
return userService.getUserData(userId);
}
}
Step 6: Run and Test
Start your Spring Boot application and make GET requests to http://localhost:8080/user/{userId}
(replace {userId}
with an actual user ID).
How It Works
When you make a request to get user data, Spring will first check the cache named "userData" to see if the data for the given user ID is already available.
If found in the cache, Spring will return the cached data directly, without executing the method.
If not found in the cache, the method
getUserData
will be executed, which will simulate fetching data from a database.The retrieved data will be cached using the specified cache name ("userData") and cache key (based on the user ID).
Subsequent requests with the same user ID will retrieve the result from the cache without executing the method again.
To add a caching provider like Redis to your Spring Boot application, you'll need to follow specific steps for each provider.(likely you can add Ehcache or Hazelcast to spring)
Adding Redis as the Caching Provider
Step 1: Add Redis Dependencies
Open your pom.xml
file and add the following dependencies for Redis:
<dependencies>
<!-- Other dependencies -->
<!-- Add Redis Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
Step 2: Configure Redis in application.properties
or application.yml
Open your application.properties
or application.yml
file and add the following properties to configure Redis:
propertiesCopy code# Redis Configuration
spring.redis.host=localhost
spring.redis.port=6379
For application.yml
:
yamlCopy code# Redis Configuration
spring:
redis:
host: localhost
port: 6379
These configurations assume that Redis is running on the local machine with the default port (6379). Adjust the properties as needed for your specific Redis setup.
Step 3: Enable Caching
If you've already added @EnableCaching
to your main-application class (as in the previous example), you're all set.
Step 4: Test with Redis
Now, when you run your Spring Boot application, it will use Redis as the caching provider.
Caching Annotations:
-
Indicates that a method's result should be cached for a specific cache name.
If the same method is called again with the same arguments, the cached result is returned instead of re-executing the method.
Example:
@Cacheable(value = "userData", key = "#userId")
public UserData getUserData(int userId) {
// ...
}
-
Forces a method to execute and update the cache with the result.
It's used for scenarios where you want to ensure that a method always updates the cache.
Example:
@CachePut(value = "userData", key = "#userId")
public UserData updateUser(int userId, UserData newData) {
// ...
}
-
Removes entries from the cache.
It's used when you want to explicitly evict (invalidate) cached entries.
Example:
@CacheEvict(value = "userData", key = "#userId")
public void deleteUser(int userId) {
// ...
}
-
- Allows multiple caching annotations to be applied to the same method.
Example:
@Caching(
cacheable = @Cacheable(value = "userData", key = "#userId"),
put = @CachePut(value = "userData", key = "#userId")
)
public UserData getUserData(int userId) {
// ...
}
Cache Manager Methods:
getCache(String name):
- Retrieves the named cache.
Example:
Cache userDataCache = cacheManager.getCache("userData");
getCacheNames():
- Returns the names of all caches.
Example:
Set<String> cacheNames = cacheManager.getCacheNames();
When to Use These Annotations and Methods:
-
Use when you want to cache the result of a method that has expensive computation or database access.
It's suitable for read-heavy operations where the data doesn't change frequently.
-
- Use when you want to ensure a method always updates the cache, regardless of whether the result was previously cached.
-
- Use when you want to explicitly evict (invalidate) cached entries, such as after a data update or deletion.
-
- Use when you want to apply multiple caching annotations to the same method.
Cache Manager Methods:
- Use when you need to interact with the cache manager programmatically, for example, to retrieve a specific cache or get a list of cache names.
Example Usage:
@Cacheable(value = "userData", key = "#userId")
public UserData getUserData(int userId) {
// Simulate fetching data from a database
return fetchDataFromDatabase(userId);
}
@CachePut(value = "userData", key = "#userId")
public UserData updateUser(int userId, UserData newData) {
// Simulate updating data in a database
return updateDataInDatabase(userId, newData);
}
@CacheEvict(value = "userData", key = "#userId")
public void deleteUser(int userId) {
// Simulate deleting data from a database
deleteDataFromDatabase(userId);
}
In this example, getUserData
fetches user data from a database and caches the result. updateUser
updates the user data in the database and ensures that the cache is updated. deleteUser
deletes the user data from the database and evicts it from the cache.
More
In the @Cacheable
annotation you provided:
javaCopy code@Cacheable(key = "#id", value = "Product", unless = "#result.price < 1000")
public Product findProduct(@PathVariable int id) {
return productService.findProductById(id);
}
key = "#id"
: This specifies that theid
parameter of the method will be used as the cache key. This means that if the method is called with the sameid
, it will return the cached result if available.value = "Product"
: This indicates the name of the cache where the result will be stored. In this case, it's set to "Product".unless = "#result.price < 1000"
: This is a condition that, if evaluated totrue
, will prevent the result from being cached. In this case, it means that if theprice
of the returnedProduct
is less than 1000, the result won't be cached.#result
: This is a SpEL (Spring Expression Language) expression referring to the return value of the method. In this case, it refers to theProduct
object returned byfindProductById(id)
.#result.price < 1000
: This is a condition in SpEL. It checks if theprice
of the returnedProduct
is less than 1000.
Putting it all together, this unless
condition means that if the Product
returned has a price less than 1000, the caching won't be applied, and the method will be executed every time it's called with that id
.
For example, if you have a product with a price of 800, it won't be cached because it meets the condition #result.price < 1000
. However, if you have a product with a price of 1200, it will be cached and subsequent calls with the same id
will return the cached result without executing the method.
For demo GitHub. Have fun learning🚀!!