Apache Ignite

 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:

  1. Ignite Configuration: Configuring Ignite caches with Read-Through and Write-Through enabled.
  2. CommandLineRunner: Loading data from the database into the Ignite cache at startup.
  3. Spring Data JPA Repository: Interfacing with the database.
  4. CacheStore Implementation: Managing persistence logic for the cache.
  5. 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:

  1. Set up a Spring Boot application.
  2. Configure Apache Ignite for multiple entities.
  3. Implement CRUD operations with caching.
  4. 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 UserOrderProduct, 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 OrderServiceProductService, 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 OrderControllerProductController, 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

Previous Post Next Post