This project template is used throughout a two-hour training session for Java developers and architects who want to explore the best practices and nuances of using Spring Boot and Spring Data with Apache Ignite. During that instructor-led training, you build a RESTful web service that uses Apache Ignite as an in-memory database. The service is a Spring Boot application that interacts with the Ignite cluster via Spring Data repository abstractions.
Check the schedule a join one of our upcoming sessions. All the sessions are delivered by seasoned Ignite experts and committers.
- Java Developer Kit, version 8 or later
- Apache Maven 3.0 or later
- Your favorite IDE, such as IntelliJ IDEA, or Eclipse, or a simple text editor.
Open a terminal window and clone the project to your dev environment:
git clone https://github.com/GridGain-Demos/spring-data-training.git
-
Enable Ignite Spring Boot and Spring Data extensions by adding the following artifacts to the
pom.xml
file<dependency> <groupId>org.apache.ignite</groupId> <artifactId>ignite-spring-data-2.2-ext</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.apache.ignite</groupId> <artifactId>ignite-spring-boot-autoconfigure-ext</artifactId> <version>1.0.0</version> </dependency>
-
Add the following property to the pom.xml to select a version of H2 supported by Ignite:
<properties> <h2.version>1.4.197</h2.version> </properties>
-
Add the
IgniteConfig
class that returns an instance of Ignite started by Spring Boot:@Configuration public class IgniteConfig { @Bean(name = "igniteInstance") public Ignite igniteInstance(Ignite ignite) { return ignite; } }
-
Update the
Application
class by tagging it with@EnableIgniteRepositories
annotation. -
Start the application and confirm Spring Boot started an Ignite server node instance.
-
Update the
IgniteConfig
by adding anIgniteConfigurer
that requires Spring Boot to start an Ignite client node:@Bean public IgniteConfigurer configurer() { return igniteConfiguration -> { igniteConfiguration.setClientMode(true); }; }
-
Add an
ServerNodeStartup
class that will be a separate application/process for an Ignite server node.public class ServerNodeStartup { public static void main(String[] args) { Ignition.start(); } }
-
Start the Spring Boot application and the
ServerNodeStartupClass
application, and confirm the client node can connect to the server.
-
Open the
world.sql
script and add theVALUE_TYPE
property to theCREATE TABLE Country
statement:VALUE_TYPE=com.gridgain.training.spring.model.Country
-
Add the following
VALUE_TYPE
property to theCREATE TABLE City
statementVALUE_TYPE=com.gridgain.training.spring.model.City
-
Add the following
KEY_TYPE
property to theCREATE TABLE City
statementKEY_TYPE=com.gridgain.training.spring.model.CityKey
-
Build a shaded package for the app:
mvn clean package -DskipTests=true
-
Start an SQLLine process:
java -cp libs/app.jar sqlline.SqlLine
-
Connect to the cluster:
!connect jdbc:ignite:thin://127.0.0.1/ ignite ignite
-
Load the database:
!run config/world.sql
-
Create the
CountryRepository
class:@RepositoryConfig (cacheName = "Country") @Repository public interface CountryRepository extends IgniteRepository<Country, String> { }
-
Add a method that returns countries with a population bigger than provided one:
public List<Country> findByPopulationGreaterThanOrderByPopulationDesc(int population);
-
Add a test that validates that the method returns a non-empty result:
@Test void countryRepositoryWorks() { System.out.println("count=" + countryRepository.findByPopulationGreaterThanOrderByPopulationDesc(100_000_000).size()); }
-
Create the
CityRepository
class:@RepositoryConfig(cacheName = "City") @Repository public interface CityRepository extends IgniteRepository<City, CityKey> { }
-
Add a query that returns a complete key-value pair:
public Cache.Entry<CityKey, City> findById(int id);
-
Add a direct SQL query that joins two tables:
@Query("SELECT city.name, MAX(city.population), country.name FROM country " + "JOIN city ON city.countrycode = country.code " + "GROUP BY city.name, country.name, city.population " + "ORDER BY city.population DESC LIMIT ?") public List<List<?>> findTopXMostPopulatedCities(int limit);
-
Create a test to validate the methods respond properly:
@Test void cityRepositoryWorks() { System.out.println("city = " + cityRepository.findById(34)); System.out.println("top 5 = " + cityRepository.findTopXMostPopulatedCities(5)); }
-
Create a REST Controller for the application:
@RestController public class WorldDatabaseController { @Autowired CityRepository cityRepository; }
-
Add a method that returns top X most populated cities:
@GetMapping("/api/mostPopulated") public List<List<?>> getMostPopulatedCities(@RequestParam(value = "limit", required = false) Integer limit) { return cityRepository.findTopXMostPopulatedCities(limit); }
-
Test the method in your browser:
http://localhost:8080/api/mostPopulated?limit=5