Over the past year we’ve been refactoring a monolithic application into one with a number of microservices for common functionality. This code refactor has allowed us to take a step back and assess what we like about our code and more importantly what we think can be done better. One area of focus has been the persistence layer, for which we were previously using Mybatis.
Mybatis can be quite useful if you need fine grained control over your CRUD operations and search queries. However there is a lot of boilerplate code and making even slight changes to our data model would require a significant number of changes to our queries. We knew we could rely on Hibernate to map our classes to our database and relieve some of this pain. When we discovered the advantages of using hibernate in conjunction with Spring Data though, we were able to simplify our persistence layer even further. Spring Data’s stated objective seemed to coincide with what we were trying to achieve.
The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.
But how does it accomplish this noble task? I’ve created a sample application by following Spring Data’s Getting Started Guide to demonstrate how Spring Data streamlines data management. The sample application has a data access layer to the classicmodels database.
After setting up all my entities, the next step was implementing one of Spring Data’s repository interfaces. The CrudRepository for example provides CRUD functionality for the class being managed. Fantastic! No need to write generic save, delete, and find methods for my managed entity. Spring Data takes things one step further though and can generate queries based solely on the method signature. Suppose for example that I wanted to find an employee by name. Perhaps I want to retrieve all employees that work internationally. Previously this would require explicitly writing queries to retrieve this data. No more with Spring Data!
Let’s take a closer look at the findByFirstNameAndLastName query. firstName and lastName are both properties on our Employee entity and the generated query will match the passed in parameters. In the method signature they are separated by And which specifies that both of these criteria must be met. If on the other hand we only need one of these criteria to be met we could replace And with Or in the method name. Here is a truncated version of the automatically generated SQL:
Our other method, findByOfficeCountryNot, is a bit more complex. This is because we are attempting to filter data by using a property on the linked Office entity. Spring Data constructs a query that first joins to the Office entity and then creates the criteria for the country property on that entity. The Not specification at the end indicates we want to retrieve data that doesn’t match the passed in parameter. Here is a truncated version of the automatically generated SQL:
Now that we’re getting the hang of things let’s try something even more ambitious. We’ve already created queries that filter results based on a criteria that either matches or doesn’t match. Let’s now return results based on whether a property is greater than the passed in parameter. And on top of that, we’ll also sort the returned results on that property.
Spring Data looks for keywords in the method name like GreaterThan and OrderBy when constructing this query. There are many different keywords available and I encourage you to check out Spring Data’s documentation to get an idea of just how powerful a tool it is. Here is a truncated version of the automatically generated SQL:
Now we’ve got a couple of Spring Data repositories in place, capable of performing CRUD operations and various data retrieval queries. And we’ve done all of this without manually writing any SQL or HQL. Let’s put everything together and see these repositories in action.
I hope this tutorial has highlighted some of the benefits of using Spring Data for data management. It certainly helped us achieve our objective of simplifying the application data access layer.