Spring Data NoSQL

Spring Data JPA and Spring Data JDBC are both excellent solutions for connecting to relational databases that at least provide JDBC driver, but most NoSQL databases do not do this. In such cases, the Spring Data project has a couple of separate modules that target popular NoSQL databases one by one. The Spring team actively develops modules for MongoDB, Redis, Apache Cassandra, Apache Solr, Gemfire, Geode, and LDAP. At the same time, the community develops modules for the following databases and storage—Aerospike, ArangoDB, Couchbase, Azure Cosmos DB, DynamoDB, Elasticsearch, Neo4j, Google Cloud Spanner, Hazelcast, and Vault.

It is worth mentioning that both EclipseLink and Hibernate support NoSQL databases. EclipseLink supports MongoDB, Oracle NoSQL, Cassandra, Google BigTable, and Couch DB. The following article describes NoSQL support in EclipseLink: https://wiki.eclipse.org/EclipseLink/Examples/JPA/NoSQL. Furthermore, Hibernate has a sub-project called Hibernate OGM (http://hibernate.org/ogm), which targets NoSQL support, namely Infinispan, MongoDB, Neo4j, and so on. However, since JPA is an inherently relational API, such solutions, in contrast to specialized Spring Data modules, lack NoSQL-related features. Additionally, JPA with its relational assumptions may lead application design in the wrong direction when applied over a NoSQL data store.

The code to use MongoDB would be almost the same as in the Spring Data JDBC example. To use a MongoDB repository, we have to add the following dependency:

compile('org.springframework.boot:spring-boot-starter-data-mongodb')

Let's imagine that we have to implement an online book catalog. The solution should be based on MongoDB and the Spring Framework. For that purpose, we may define the Book entity with the following Java class:

@Document(collection = "book")                                       // (1)
public class Book {
   @Id                                                               // (2)
   private ObjectId id;                                              // (3)

   @Indexed                                                          // (4)
   private String title;

   @Indexed
   private List<String> authors;                                     // (5)

   @Field("pubYear")                                                 // (6)
   private int publishingYear;

   // constructors, getters and setters
   // ...
}

Here, instead of a JPA @Entity, we use the @Document annotation (1) from the org.springframework.data.mongodb.core.mapping package. This annotation is specific to MongoDB and makes it possible to reference the correct database collection. Furthermore, to define an internal ID for the entity, we use a MongoDB specific type, org.bson.types.ObjectId (3)in conjunction with the Spring Data annotation org.springframework.data.annotation.Id (2). Our entity and consequently our database document would contain a title field, which would also be indexed by MongoDB. To do that, we decorate the field with the @Indexed annotation (4). This annotation provides a few configuration options regarding indexing details. In addition, a book may have one or more authors, and we represent this by declaring a type of the authors field to be List<String> (5). The authors field is also indexed. Note, here we do not create a reference to a separate author table with many-to-many relations, as it would most likely be implemented with a relational database, but instead we embed author names as a sub-document into the book entity. Finally, we define the publishingYear field. The field names in the entity and the database are different. The @Field annotation allows custom mappings for such cases (6).

In the database, such a book entity would be represented by the following JSON document:

{
    "_id" : ObjectId("5b1c0908eb696eddfadc0b1b"),                  /*(1)*/
    "title" : "The Expanse: Leviathan Wakes",
    "pubYear" : 2011,                                              /*(2)*/
    "authors" : [                                                  /*(3)*/
        "Daniel Abraham",                                          /*   */
        "Ty Franck"                                                /*   */
    ],
    "_class" : "org.rpis5.chapters.chapter_07.mongo_repo.Book"     /*(4)*/
}

As we can see, MongoDB uses the specially designed data type to represent a document's ID (1). In this case, the publishingYear is mapped to the pubYear field (2), and authors are represented by an array (3). Also, Spring Data MongoDB adds the supporting _class field, which describes a Java class used for Object-Document Mapping.

With MongoDB, a repository interface should extend the org.springframework.data.mongodb.repository.MongoRepository interface (1), which in turn extends the CrudRepositorywhich we already used in the previous examples:

@Repository
public interface BookSpringDataMongoRepository
   extends MongoRepository<Book, Integer> {                          // (1)

   Iterable<Book> findByAuthorsOrderByPublishingYearDesc(            // (2)
       String... authors
   );

   @Query("{ 'authors.1': { $exists: true } }")                      // (3)
   Iterable<Book> booksWithFewAuthors();
}

Of course, a MongoDB repository supports query generation based on naming conventions, so the findByAuthorsOrderByPublishingYearDesc method searches books by their authors and returns a result sorted by the publishing year beginning with the most recent publications. Also, the org.springframework.data.mongodb.repository.Query annotation allows us to write MongoDB-specific queries. For example, the preceding query (3) cleverly searches for books with more than one author.

The rest of the application should work the same way as in cases with Spring Data JDBC or Spring Data JPA.

Even though we have touched on the main approaches for data persistence with Spring, we have barely scratched the surface of this area. We have entirely omitted transaction management, database initialization, and migration (Liquibase, Flyway), which are the best practices of entity mapping, caching, and performance tuning. All of these areas could fill more than one book, but we have to move forward and investigate how to do persistence reactively.

Achieving reactive support for a NoSQL database with the Spring Framework requires the whole underlying infrastructure to provide a reactive or asynchronous API. In general, NoSQL databases have appeared relatively recently and evolved fast, so not a lot of infrastructure is heavily bounded by the synchronous blocking API. Consequently, it should be easier to achieve reactive persistence with NoSQL databases than with relational databases with JDBC drivers. So far, Spring Data has a few reactive data connectors, and MongoDB is among them. This is covered later in the Reactive data access with Spring Data section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.93.175