Spring Data GemFire Tutorial
Welcome to the Spring Data GemFire tutorial. GemFire is a product from Pivotal. Pivotal Gemfire is an in-memory data grid powered by Apache Geode. Here is a quote from the Pivotal GemFire documentation:
Pivotal GemFire is a data management platform that provides real-time, consistent access to data-intensive applications throughout widely distributed cloud architectures.
GemFire pools memory, CPU, network resources, and optionally local disk across multiple processes to manage application objects and behavior. It uses dynamic replication and data partitioning techniques to implement high availability, improved performance, scalability, and fault tolerance. In addition to being a distributed data container, GemFire is an in-memory data management system that provides reliable asynchronous event notifications and guaranteed message delivery.
Spring Data GemFire makes it easier to build highly scalable, Spring-powered applications using Pivotal GemFire for distributed data management.
1. Suggested Reading
Here are some articles that will help you better understand this tutorial.
2. Tools
3. Assumptions
This article assumes that you know your way around Eclipse. You are familiar with Maven. Familiarity with Spring Data is handy but not required. Basically, you have done some coding. This project has been created using Eclipse Oxygen so all instructions are based on this IDE.
4. Project Setup
To start, we create our project. This can be done by going to File -> New -> Maven Project and fill up what is required. Alternatively, we can import the Maven project by going to File -> Import… and picking the project.
5. Project Object Model
Our pom.xml
should look like the one below:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.example</groupId> <artifactId>spring-data-gemfire</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-gemfire</artifactId> <version>2.0.8.RELEASE</version> </dependency> </dependencies> <repositories> <repository> <id>spring-libs-release</id> <name>Spring Releases</name> <url>https://repo.spring.io/libs-release</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
As shown above, our project has 2 dependencies. We are using spring-boot-starter
which means that we can use all the Spring modules included in it. For example, the Spring Core and Spring Boot modules will be available for us to use plus many more. We’ll use Spring Boot so that we won’t have to do dabble with XML configuration and we can create a stand-alone Spring application. The next dependency is spring-data-gemfire
which contains all the libraries we need to create our Spring powered GemFire application. In Eclipse, we can see the dependency hierarchy by opening the pom.xml
and clicking on the Dependency Hierarchy tab.
6. Define an Entity
Our example is about blogs. The first thing we need to do is create a Blog
object and annotate it as Region. GemFire is an IMDG that maps data to Regions. This data will be mapped to the Blog Region.
Blog.java
package com.javacodegeeks.example; import java.io.Serializable; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.gemfire.mapping.annotation.Region; @Region(value = "Blog") public class Blog implements Serializable { private static final long serialVersionUID = 1L; @Id private final String url; private String title; @PersistenceConstructor public Blog(String url, String title) { this.url = url; this.title = title; } public String getUrl() { return url; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } @Override public String toString() { return "Blog [url=" + url + ", title=" + title + "]"; } }
This class is fairly easy to understand. It has getters, setters, a constructor, a toString
method, and some fields. The @PersistenceConstructor
populates the entity when creating a new instance. This new entity will be created in the “Blog” Region. The url
field is the key of the Blog
instance because of the @Id
annotation. It’s similar to the java.util.Map
key-value concept.
Below, we will configure the Region as ClientRegionShortcut.LOCAL
so that we don’t need to set up multiple nodes in a cluster.
7. Create a Repository Interface
We extend the CrudRepository
interface to take advantage of Spring Data’s features. Spring Data has the ability to derive queries so we don’t have to learn the query language of GemFire. The methods we write are converted by Spring Data into GemFire queries.
BlogRepository.java
package com.javacodegeeks.example; import org.springframework.data.gemfire.repository.query.annotation.Trace; import org.springframework.data.repository.CrudRepository; public interface BlogRepository extends CrudRepository<Blog, String> { @Trace Blog findByTitle(String title); }
The generic parameters that CrudRepository
works with are of type Blog
and String
. Because we extended CrudRepository
, we inherit several methods for working with Blog
persistence. We are able to create, read, update, and delete Blog
entities.
We have also defined our own query method by simply declaring the method signature (findByTitle
). The @Trace
annotation enables GemFire OQL query debugging.
8. Create the Main Class
Our code below performs the four basic CRUD operations. Skim through the code below but peruse the explanation after it.
Main.java
package com.javacodegeeks.example; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.gemfire.config.annotation.ClientCacheApplication; import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions; import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; import org.apache.geode.cache.client.ClientRegionShortcut; @SpringBootApplication @ClientCacheApplication(name="CrudGemFireExample", logLevel = "fatal") // the pivotal gemfire cache @EnableEntityDefinedRegions(basePackageClasses = Blog.class, clientRegionShortcut = ClientRegionShortcut.LOCAL) // enable the creation of Pivotal GemFire/Apache Geode Regions based on the application persistent entities @EnableGemfireRepositories public class Main { public static void main(String args[]) { SpringApplication.run(Main.class, args); } @Bean ApplicationRunner run(BlogRepository blogRepo) { System.out.println("Spring Data GemFire example"); Blog example = new Blog("http://example.com", "Example"); Blog jcg = new Blog("http://javacodegeeks.com", "JCG"); Blog dzone = new Blog("https://dzone.com", "Dzone"); // create blogRepo.save(example); blogRepo.save(jcg); blogRepo.save(dzone); // read blogRepo.findAll().forEach(blog -> System.out.println(blog)); // find by title System.out.println("Finding JCG..."); Blog temp = blogRepo.findByTitle("JCG"); System.out.println(temp); // update temp.setTitle("new JCG"); blogRepo.save(temp); System.out.println("JCG updated..."); blogRepo.findAll().forEach(blog -> System.out.println(blog)); // delete System.out.println("Deleting Example"); temp = blogRepo.findByTitle("Example"); blogRepo.delete(temp); blogRepo.findAll().forEach(blog -> System.out.println(blog)); return null; } }
The @ClientCacheApplication
is one of GemFire’s convenient configuration-based annotation. This tells us that this is a GemFire cache client and its name in the cluster is CrudGemFireExample
. It will only output fatal
log messages.
The @EnableEntityDefinedRegions
enables the creation of Pivotal GemFire/Apache Geode Regions based on the application persistent entities. We have defined the Region as ClientRegionShortcut.LOCAL
so that we won’t need to run any servers and the client will just store the data locally. The basePackageClasses
specifies the packages to scan for @Region
annotated persistent entities. The package of each class specified will be scanned.
The @EnableGemfireRepositories
annotation enables GemFire repositories. This means that Spring will scan the current package for any interfaces that extend one of Spring Data’s Repository
interfaces.
The public static void main
method uses Spring Boot’s SpringApplication.run()
to run the application and invoke the ApplicationRunner
that performs our CRUD operations on GemFire. The BlogRepository
has been autowired by Spring.
9. Spring Data GemFire Output
After running the code above (Run As -> Java Application), we should have an output that looks like the one below.
Console Output
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.3.RELEASE) [info 2018/07/29 11:27:56.284 BST tid=0x1] No active profile set, falling back to default profiles: default [info 2018/07/29 11:27:56.482 BST tid=0x1] Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@43f02ef2: startup date [Sun Jul 29 11:27:56 BST 2018]; root of context hierarchy Spring Data GemFire example Blog [url=https://dzone.com, title=Dzone] Blog [url=http://javacodegeeks.com, title=JCG] Blog [url=http://example.com, title=Example] Finding JCG... Blog [url=http://javacodegeeks.com, title=JCG] JCG updated... Blog [url=https://dzone.com, title=Dzone] Blog [url=http://javacodegeeks.com, title=new JCG] Blog [url=http://example.com, title=Example] Deleting Example Blog [url=https://dzone.com, title=Dzone] Blog [url=http://javacodegeeks.com, title=new JCG]
The output shows the CRUD operations being performed. We created 3 blog objects. We read the blog objects from the in-memory GemFire cache. Then we searched the cache for a title. After that, we updated the “JCG” title, changing it from “JCG” to “new JCG”. Then we deleted the “Example” blog object. Easy peasy lemon squeezy.
10. Spring Data GemFire Summary
In summary, we include the spring-data-gemfire
dependency to make available all the Spring modules we need to make GemFire operations. We define an entity, in this case a Blog
class. Next, we created a repository interface by extending Spring’s CrudRepository
. Finally, we wire everything up in the main application class.
11. Download the Source Code
This is an example about Spring Data GemFire.
You can download the source code of this example here: spring-data-gemfire.zip.