Chapter 4. Spring Data GemFire

If you’ve been working with enterprise Java recently, you likely have heard of, and almost equally likely have used, Spring Framework (commonly referred to as just “Spring”). Spring is used for building modern enterprise applications. At its core, Spring is a lightweight dependency-injection architecture that allows for loose coupling of components. Its features bring all manner of benefits to the Spring developer. Spring also brings benefits to the application, including modularity and scalability (which plays right into the design of today’s cutting-edge microservices architectures), testing and mocking, metrics, and a single configuration platform (Spring).

But Spring Framework extends well beyond its core. One of the most used and most long-lived projects under Spring Framework is Spring Data.

What Is Spring Data?

The Spring Data team explains it this way on its home page:

Spring Data’s mission is to provide a familiar and consistent, Spring-based programming model for data access while still retaining the special traits of the underlying data store.

Spring Data is all about accessing your data, and doing so in a consistent way, irrespective of how you store that data, be it in a relational database like MariaDB, a NoSQL database like MongoDB, or an in-memory data grid like GemFire.

This chapter is going to look at Spring Data, and the GemFire implementation of Spring Data, called, unsurprisingly, Spring Data GemFire. Spring Data GemFire lets Spring developers easily get up and running with GemFire by using constructs, patterns, and configurations that are familiar to them. Spring Data is an agnostic way to access backend data stores, but Spring Data GemFire provides hooks to GemFire-specific capabilities

Getting Started

So, let’s revisit the Hello example from Chapter 3, but, to get you introduced to Spring with GemFire, we’ll rewrite it to use Spring’s configuration and annotation features. This isn’t, strictly speaking, Spring Data Gemfire, but it’s a good start in showing how Spring and GemFire work together. After this quick example, which uses Spring to create a local cache on the fly, we dive into Spring Data GemFire, and show a much more robust and Spring Data–oriented example.

For our example, we are going to use Java configuration and annotations to configure our Spring ApplicationContext; also, for simplicity’s sake we repurpose our main class as the configuration class, as well. The code that follows, complete with a Maven pom.xml file,1 is available on our GitHub site.

@Configuration
public class HelloWorld {
    @Bean
    CacheFactoryBean gemfireCache() {
        CacheFactoryBean gemfireCache =
            new CacheFactoryBean();
        return gemfireCache;
    }

    @Bean(name="hello")
    LocalRegionFactoryBean<String, String>
      getEmployee(GemFireCache cache) {
        LocalRegionFactoryBean<String, String> region =
              new LocalRegionFactoryBean<String, String>();
        region.setCache(cache);
        region.setName("hello");
        return region;
    }

    public static void main(String[] args) {
      ApplicationContext context =
         new AnnotationConfigApplicationContext(
             HelloWorld.class);
      Region<String, String> region =
           (Region<String, String>) context.getBean("hello");
      region.put("1", "Hello");
      region.put("2", "World");
      for (Map.Entry<String, String>  entry :
            region.entrySet()) {
              System.out.format("key = %s, value = %s\n",
              entry.getKey(), entry.getValue());
      }
      region.close();
      region.getCache().close();
       System.exit(0);
   }
}

If you’re a Spring developer, this programming style should look very familiar to you. But what you’ve seen here is really just some Spring-ification of standard GemFire access patterns, and that is not Spring Data GemFire. Spring Data GemFire helps you access your (GemFire) data in the way that you would access any persistent data source. This starts with the idea that all data is contained in repositories; access to those disparate repositories has some commonalities that can be reflected in the way you access them in Spring. So, Spring Data GemFire is Spring Data with a GemFire repository.

Spring Data GemFire Features

What does Spring Data GemFire offer above the standard GemFire Java API? First, Spring Data GemFire lets you configure your GemFire cache in the same way that you configure the rest of your Spring application. Every configuration in GemFire’s Java API is also available in Spring Data GemFire. Spring Data GemFire also lets you use all the configuration features and shortcuts built into Spring, including the following:

Profile support

Spring profiles make it easy to set up different runtime profiles that can all be held in a common location.

Property placeholders

Spring property placeholders allow properties to be stored externally and pulled in at configuration time. Properties are usually kept in XML, YAML, or standard .properties formats. Managing properties externally is a tenet of the 12-factor methodology used in microservices architectures.

Spring Expression Language (SpEL) support

SpEL is an expression language that is evaluated at runtime; it can be used in Spring to configure GemFire beans (and others) based on values returned at runtime from other beans.

Let’s look at an example with Spring Data GemFire. But first, let’s get into a Spring Data frame of mind. Spring Data abstracts the underlying data layer and minimizes the emphasis on the persistence of data. It keeps the focus on the business domain; Spring Data GemFire exists to enhance our ability to maintain, query, and persist objects in that domain.

So, in this simple example, our domain is the art world; we will model the Artist object (note, again, full source code for this example is in our GitHub repository):

@Region("artist")
public class Artist {
    @Id
    public String name;
    public String style;
    public int yearOfBirth;
}

Savvy Java developers might cringe that we’ve made the object’s attributes public, but we’re trying to keep the example small. Notice, though, the @Id annotation. This is a Spring Data annotation used to indicate the ID, key, or primary key, in various data stores. With Spring Data, you can use this annotation; the framework will map that to whatever construct the data store uses.

Next, we will write our Java configuration class. This is a standard Spring methodology, and this class contains a mixture of Spring annotations and GemFire classes; in other words, it builds the underlying infrastructure for Spring Data Gemfire. Note that we’ve omitted the package declaration and imports for conciseness:

@Configuration
public class GemdemoConfiguration {

    @Bean
    Properties gemfireProperties() {
        Properties gemfireProperties = new Properties();
        gemfireProperties.setProperty("mcast-port", "0");
        return gemfireProperties;
    }

    @Bean
    CacheFactoryBean gemfireCache() {
     CacheFactoryBean gemfireCache = new CacheFactoryBean();
     gemfireCache.setClose(true);
        gemfireCache.setProperties(gemfireProperties());
        return gemfireCache;
    }

    @Bean(name="artist")
    LocalRegionFactoryBean<String, Artist>
        getArtist(final GemFireCache cache) {
        LocalRegionFactoryBean<String, Artist> artistRegion
            = new LocalRegionFactoryBean<String, Artist>();
        artistRegion.setCache(cache);
        artistRegion.setName("artist");
        return artistRegion;
    }
}

With our domain class done and our GemFire cache set up, we’re now ready to tap into the elegance of Spring Data. As mentioned earlier, Spring Data abstracts various data stores into a common object called a repository. When you’re creating a Spring Data (GemFire) application, you define the repository for your domain object, and how you want to query it. The magic here is that the definition you provide need only be an interface—Spring Data will infer what you’re trying to accomplish, based upon method names and other introspection techniques, and will then generate the logic for you. So, in our simple example, we are going to define an Artist repository that can query on name (the ID/key), and can also query for artists born after a specified year. The ArtistRepository will look like this:

@Repository
public interface ArtistRepository extends
    GemfireRepository<Artist, String> {
    Artist findByName(String name);
    Artist findByStyle(String style);
    Iterable<Artist> findByStyleIn(
        Collection<String> modernStyles);
    Iterable<Artist> findByYearOfBirthGreaterThan(
        double yearOfBirth);
}

Spring Data looks at each method in the interface and figures out what is needed. The findByXxx methods, where Xxx is a domain object attribute, are obvious, but Spring Data can also grok the more complex method names; for example, findByStyleIn will look for each domain object that has a style attribute equal to one of the ones in the parameter list. The findByYearOfBirthGreaterThan method is examined by Spring Data, which can parse out the “YearOfBirth” part as an attribute of Artist, and the “GreaterThan” as a relationship to be tested; it generates an implementation of the interface that does the right thing, as the right thing is defined by the backing data store (in our case, GemFire).

What you’ve seen here is the tip of the Spring Data GemFire iceberg. Spring Data GemFire also puts a nice Spring-based wrapper around GemFire functions, making their management much easier for Spring developers; Spring Data core also has many more features worth further investigation—the Spring Data home page is a great place to start when you want to find out more.

1 Spring and GemFire have many JAR dependencies. By far, the easiest way to manage these dependencies is with Maven or Gradle.

Get Scaling Data Services with Pivotal GemFire now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.