Apache Ignite is primarily an in-memory computing platform, but it also supports integration with external storage for persistence. This functionality is achieved through the Persistent Store feature or by integrating with third-party databases and storage systems.
In this article, I will walk you through building a Spring Boot application that leverages Apache Ignite for efficient in-memory caching with the use of Ignite’s Read-Through and Write-Through capabilities to optimize database interactions.
Below is the implementation details in which I will cover:
- Ignite Configuration: Configuring Ignite caches with Read-Through and Write-Through enabled.
- CommandLineRunner: Loading data from the database into the Ignite cache at startup.
- Spring Data JPA Repository: Interfacing with the database.
- CacheStore Implementation: Managing persistence logic for the cache.
- User, Order, Customer and Product Entity: A basic representation of a database table.
Feel free to customize the entities and repositories according to your application’s needs. As you are already aware, Apache Ignite is a distributed database, caching, and processing platform designed for high-performance computing. Below section will demonstrate how to:
- Set up a Spring Boot application.
- Configure Apache Ignite for multiple entities.
- Implement CRUD operations with caching.
- Test the application with sample JSON requests.
Prerequisites
To follow along, you should have:
- A working knowledge of Java and Spring Boot.
- Familiarity with REST APIs and basic caching concepts.
- An IDE (e.g., IntelliJ IDEA, Eclipse) and Maven installed.
Step 1: Setting Up the Project
Start by creating a new Spring Boot project with the following dependencies:
- Spring Web
- Spring Data JPA
- Apache Ignite Spring Boot Starter
- H2 Database (for demonstration purposes)
Your pom.xml
should include:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-spring-boot-autoconfigure-ext</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Step 2: Configuring Apache Ignite
Enable Apache Ignite in your Spring Boot application by configuring it as follows:
@Configuration
public class IgniteConfig {
@Bean
public Ignite igniteInstance(UserCacheStore userCacheStore, OrderCacheStore orderCacheStore,
ProductCacheStore productCacheStore, CustomerCacheStore customerCacheStore) {
Ignite ignite = Ignition.start();
CacheConfiguration<Long, User> userCacheConfig = new CacheConfiguration<>(USER_CACHE);
userCacheConfig.setCacheMode(CacheMode.PARTITIONED);
userCacheConfig.setReadThrough(true);
userCacheConfig.setWriteThrough(true);
userCacheConfig.setCacheStoreFactory(FactoryBuilder.factoryOf(UserCacheStore.class));
userCacheConfig.setInterceptor(userCacheInterceptor);
ignite.getOrCreateCache(userCacheConfig);
CacheConfiguration<Long, Order> orderCacheConfig = new CacheConfiguration<>(ORDER_CACHE);
orderCacheConfig.setCacheMode(CacheMode.PARTITIONED);
orderCacheConfig.setReadThrough(true);
orderCacheConfig.setWriteThrough(true);
orderCacheConfig.setCacheStoreFactory(FactoryBuilder.factoryOf(OrderCacheStore.class));
ignite.getOrCreateCache(orderCacheConfig);
CacheConfiguration<Long, Product> productCacheConfig = new CacheConfiguration<>(PRODUCT_CACHE);
productCacheConfig.setCacheMode(CacheMode.PARTITIONED);
productCacheConfig.setReadThrough(true);
productCacheConfig.setWriteThrough(true);
productCacheConfig.setCacheStoreFactory(FactoryBuilder.factoryOf(ProductCacheStore.class));
ignite.getOrCreateCache(productCacheConfig);
CacheConfiguration<Long, Customer> customerCacheConfig = new CacheConfiguration<>(CUSTOMER_CACHE);
customerCacheConfig.setCacheMode(CacheMode.PARTITIONED);
customerCacheConfig.setReadThrough(true);
customerCacheConfig.setWriteThrough(true);
customerCacheConfig.setCacheStoreFactory(FactoryBuilder.factoryOf(CustomerCacheStore.class));
ignite.getOrCreateCache(customerCacheConfig);
return ignite;
}
}
Step 3: Implementing the Entities
Create entities for User
, Order
, Product
, and Customer
:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and Setters
}
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String product;
private Double price;
// Getters and Setters
}
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
// Getters and Setters
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and Setters
}
Step 4: Creating the Repositories
Define Spring Data JPA repositories for each entity:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {}
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {}
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {}
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {}
Step 5: Implementing the Services
Service classes handle business logic and caching operations. Below is an example for UserService
:
/**
* Service to handle business logic for User entities.
*/
@Service
public class UserService {
@Autowired
Ignite ignite;
public List<User> getAllUsers() {
IgniteCache<Long, User> cache = ignite.getOrCreateCache(USER_CACHE);
// Use ScanQuery to retrieve all entries and collect values into a list
return StreamSupport.stream(
cache.query(new ScanQuery<Integer, User>()).spliterator(), false
).map(Cache.Entry::getValue)
.collect(Collectors.toList());
}
public User getUserById(Long id) {
IgniteCache<Long, User> cache = ignite.getOrCreateCache(USER_CACHE);
return cache.get(id);
}
public User saveUser(User user) {
IgniteCache<Long, User> cache = ignite.getOrCreateCache(USER_CACHE);
cache.put(user.getId(),user);
return user;
}
public User updateUser(Long id, User updatedUser) {
IgniteCache<Long, User> cache = ignite.getOrCreateCache(USER_CACHE);
if (cache.get(id) !=null) {
cache.put(id,updatedUser);
return updatedUser;
}
return null;
}
public void deleteUser(Long id) {
ignite.getOrCreateCache(USER_CACHE).remove(id);
}
}
Similar code is written for OrderService
, ProductService
, and CustomerService
services.
Step 6: Creating the Controllers
Create REST controllers for managing the entities via API endpoints:
/**
* REST Controller to handle requests.
*/
@RestController
@RequestMapping("/api")
public class UserApiController {
@Autowired
private UserService userService;
@GetMapping("/user")
public ResponseEntity<User> getUsers(@RequestParam Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
// User Endpoints
@PostMapping("/user")
public ResponseEntity<User> createUser(@RequestBody UserDTO userDTO) {
User user = new User();
user.setId(userDTO.getId());
user.setName(userDTO.getName());
user.setEmail(userDTO.getEmail());
return ResponseEntity.ok(userService.saveUser(user));
}
@GetMapping("/users")
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userService.getAllUsers());
}
}
You can replicate similar patterns for OrderController
, ProductController
, and CustomerController
.
Step 7: Testing the Application
Start the application and use tools like Postman or cURL to interact with the API endpoints.
Create a User:
{
"name": "John Doe",
"email": "john.doe@example.com"
}
Create an Order:
{
"product": "Laptop",
"price": 1200.00
}
Post a Comment