Optimizing Infinispan Cache for Hibernate with Spring Boot

Snippet of programming code in IDE
Published on

Optimizing Infinispan Cache for Hibernate with Spring Boot

Caching is a vital aspect of application performance, especially when dealing with large databases in enterprise applications. One powerful combination is using Infinispan as a caching layer for Hibernate within a Spring Boot application. This blog post will guide you through integrating and optimizing Infinispan with Hibernate using Spring Boot, improving your application’s performance.

Understanding Infinispan and Hibernate Caching

Infinispan is a distributed in-memory data grid solution that provides a caching layer and is highly scalable. Hibernate, on the other hand, is an object-relational mapping (ORM) tool that can be combined with various caching strategies to enhance performance.

When Infinispan is used as a second-level cache for Hibernate, it allows you to cache entity data that is frequently read and less frequently modified. This minimizes database interactions and reduces load times significantly.

Setting Up the Environment

To get started, ensure you have the following installed:

  • JDK 8+
  • Maven 3.6+
  • Integrated Development Environment (IDE) (like IntelliJ IDEA or Eclipse)
  • An SQL database (like MySQL or PostgreSQL)

Start with a Spring Boot Project

Create a Spring Boot application using Spring Initializr with the following dependencies:

  • Spring Web
  • Spring Data JPA
  • Infinispan
  • Hibernate

You can quickly set this up using your terminal:

curl https://start.spring.io/starter.zip -o demo.zip \
  -d dependencies=web,data-jpa,infinispan \
  -d packageName=com.example.demo \
  -d name=demo \
  -d version=1.0.0 \
  -d type=maven-project
unzip demo.zip
cd demo

Maven Dependencies

Add the Infinispan and Spring Boot starter dependencies to your pom.xml:

<dependency>
    <groupId>org.infinispan</groupId>
    <artifactId>infinispan-spring4</artifactId>
    <version>12.1.7.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.30.Final</version>
</dependency>

Make sure to check for the latest version of these libraries.

Configuring Infinispan as a Cache Provider

Next, configure Infinispan in your application.properties file:

spring.cache.type=infinispan

infinispan.cache.default.name=localCache
infinispan.cache.default.eviction.strategy=LRU
infinispan.cache.default.eviction.maxEntries=5000
infinispan.cache.default.expiration.lifespan=600000

Explanation of Properties

  • spring.cache.type=infinispan: Tells Spring Boot to use Infinispan as the cache provider.
  • eviction.strategy=LRU: Sets a caching strategy to remove the least recently used items when the cache gets full.
  • eviction.maxEntries=5000: The maximum number of entries in the cache.
  • expiration.lifespan=600000: The lifespan of cached entries in milliseconds before they expire.

Creating Entities with Caching

Let’s implement a simple User entity and enable caching at the class level:

import javax.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "users")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;

    // Constructors, getters, and setters
}

Why the Cache Annotations?

  • @Cacheable: Marks the entity as cacheable, enabling caching of instances.
  • @Cache: Specifies the cache strategy. READ_WRITE allows for safe caching with concurrent modifications.

Repository Layer

Next, create a repository interface for accessing User entities:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

Service Layer

Implement a service layer to handle the user business logic:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }

    public User save(User user) {
        return userRepository.save(user);
    }
}

Controller Layer

Finally, set up a REST controller:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
}

Fine-Tuning Cache Performance

To ensure the best performance, consider configuring additional Infinispan settings in an infinispan-config.xml file in the resources directory:

<infinispan>
    <local-cache name="localCache">
        <expiration lifespan="600000" maxIdle="300000" />
        <eviction maxEntries="10000" strategy="LRU" />
    </local-cache>
</infinispan>

Additional Optimization Techniques

  1. Proper Size Configuration: Ensure you set maxEntries correctly based on expected usage to prevent excessive eviction.
  2. Monitor Cache Usage: Use Infinispan’s management operations to monitor cache hits and misses, helping you adjust configurations dynamically.
  3. Cluster Configuration: If your application scales horizontally, consider configuring Infinispan for clustered caches.

Testing the Integration

Run your Spring Boot application and test the caching functionality using Postman or CURL:

  1. Create a new user via POST request to /api/users.
  2. Retrieve the user via GET request to /api/users/{id}.

Monitor your database interactions; subsequent requests for the same user should hit the Infinispan cache rather than the database.

A Final Look

Integrating Infinispan as a caching layer with Hibernate in a Spring Boot application can greatly enhance performance and scalability. By carefully configuring your cache settings and fine-tuning your environment, you can ensure that your application responds quickly even under load.

For further reading on caching in Spring, check the Spring Framework Documentation and for a deeper dive into Infinispan, visit the Infinispan Documentation.

With a well-configured cache, your application will not only be faster but also more efficient in resource utilization. Happy coding!