In this Fuse day, Tikal Java group decided to continue its previous Fuse research for NoSQL, but this time from a different point of view – SpringData and Polyglot persistence. We had two goals in this Fuse day: try working with more than one NoSQL in the same application, and also taking advantage of SpringData data access abstractions for NoSQL databases. We decided to take MongoDB and Neo4J as document DB, and Neo4J as graph database and put them behind an existing, classic and well known application – Spring Travel Sample application.

This is a kind of “hello world” application for Spring users, which demonstrates a very simple hotel bookings application, using Spring framework with JPA to access RDBMS. As said, our goal was to replace the RDBMS behind this app, with the two NoSQL above. Obviously this wasn't done for architecture reasons, since RDBMS can serve perfectly for this simple application. We merely wanted to understand, how hard is to migrate a simple Spring/JPA application to SpringData/NoSQL, and how hard is to use more than one NoSQL on the same project (while gaining some hand-on experience with these technologies).

Next, we converted the simple domain objects to Graph model. In graph model you have two constructs : Nodes and Relationships. Both nodes and relationships can have properties, so there is somewhat less impedance mismatch to Object Oriented world , compare to RDBMS-to-OO one.

We converted the Hotel and User entities to graph Nodes and the Booking entity to Relationship typed “BOOKD”. We also changed the JPA @ID annotation to @GraphID and marked some of the properties as @Index. The @Index enables fast search by Neo4J for nodes by these properties - Behind the scenes Neo4J uses Lucene fast full text search index for that matter. Here are some code snippets from the the entities:

Next step, was to define SpringData Repositories. We created two interfaces: HotelRepository and UserRepository. Both interfaces extends SpringData's GraphRepority interface. We were happy for the no-need to implement the interfaces - They are been implemented automatically by SpringData using method naming conventions :)

Next, we had to integrate the new interfaces into a new BookingService implementation of the existing BookingService interface. This new implementation uses the new created repositories above, to implement the expected behavior. Thus , we kept the MVC layer and GUI as is.

Last step, was creating a small Spring configuration file, which defines the GraphDataService bean and our base package for the repositories interfaces above:

That's all for this phase – After running neo4j server, and deploying the application on Tomcat, we had the same Travel application – but this time working against Neo4J Graph DB.

Step 2 – Integrate MongoDB to Travel Application

In this phase we wanted to overload the Travel application with yet another NoSQL – MongoDB. MongoDB is very popular document datastore, which keeps its documents aggregated in one or more collections inside the DB. MongoDB has a flexible query language (much more flexible than other NoSQL). Nevertheless, for complex operations, like grouping results, you may need to write a map-reduce program in MongoDB, and this is exactly what we had to do here.

Again, we started with Maven dependencies artifacts, and we spent some time to understand, we must take the last dependencies in integrate also with Neo4J dependencies without clashes.

Then, we created a new domain object called BookingDoc, containing the booking-id and userId:

As you can see, this domain class is annotated with @Document to denote its been persistent as a document in MongoDB, and @Id is the document id.

Next step, we created a BookingDocRepository interface, which extends SpringData's MongoRepository base interface. Again no implementation here – we have the auto-generated one from the SpringData on application startup :)

Here we have more complicated use case than in Neo4J: In addition to the basic inherited methods from MongoRepository, we wanted to create a custom implementation. This custom implementation method, could NOT be created automatically by naming conventions, as we saw before. The custom method should invoke a map reduce functionality inside the MongoDB. This map-reduce calculates the bookings count for a given user. For that, we had to create yet another interface, BookingDocCustomRepository:

We created a new implementation repository BookingDocRepositoryImpl, which implements the map-reduce method to calculate the bookings count for the user. Thus, the result on runtime is a “mixed-in” implementation – Containing our custom implementation, and the SpringData generated code. Our custom implementation uses SpringData's MongoTemplate to invoke the map-reduce code in the DB:

As you can see, the map method emits tuples of userId and 1, for every booking associated with the input userId. On the other hand, the reduce method accepts the userId and the list of 1, and sum the 1 values. The reduce returns a list of tuples. Nevertheless, since we have only one user as the output of the map method, we have only one tuple in the list. The tuple's “value” is the booking count for given user.

Last, we added Spring configuration for MongoDB. This includes the DB configuration, MongoTemplate and our MongoDB's repositories base package:

That's it – you have a running Spring travel application using two NoSQL storages with SpringData.

Conclusion

We had much pleasure playing with NoSQL and SpringData in this Tikal Fuse day. We faced the nice SpringData features, neglecting the old “GenericDao” pattern. We were also happy to drop DAO implementations classes (typically dozens empty useless in a regular applications), and use SpringData's “query by convention” and annotations.

We have the confidence now,we can really integrate more than one NoSQL in the same project with SpringData, by making sure the right Maven dependencies, making it as a Polyglot Persistence application. You can download the Travel NoSQL application from Tikal GitHub.