Build a Markdown-based Blog with Spring Boot - Part 2
If you haven't read the first part, please do so.
At the end of the first part, we had defined the POJO classes for the two entities (Author and Post) in our project.
We begin the second part by defining the repository interfaces for those entities.
Defining repositories
Spring Data provides several Repository abstractions that aim at reducing the amount of human-generated code for data-access layers. Two of such abstractions that we need for our blog are CrudRepository and PagingAndSortingRepository.
The CrudRepository interface provides various methods for performing CRUD operations on an entity. On the other hand, the PagingAndSortingRepository is an extension of the CrudRepository that provides additional methods for retrieving entities using the paging and sorting abstraction.
The repositories for our entities are defined as follows.
Since we don't need to implement paging or sorting for the Author entity, we can extend CrudRepository for AuthorRepository. On the other hand, since we will be retrieving posts using the paging and sorting abstraction, we need to extend PagingAndSortingRepository for PostRepository.
Note how both CrudRepository and PagingAndSortingRepository take as type arguments the domain class to manage and the type of ID of the domain class.
Another thing to note is that we don't need to annotate either repository interfaces with @Repository or @Component. This is because CrudRepository and PagingAndSorting interfaces are annotated with @NoRepositoryBean, making them intermediate interfaces. Intermediate interfaces are not picked up by the Spring container and as such are not instantiated as beans.
However, any derivatives of such interfaces that are not annotated with @NoRepositoryBean -- in our case, AuthorRepository and PostRepository -- are automatically instantiated by the Spring container.
Defining controllers
Now onto defining controllers for the blog. These will handle requests to our web application and are responsible for delivering the appropriate view to the client.
We need to define two classes -- RootController and PostController -- each of which will handle respective requests.
RootController
RootController will be mapped to handle GET / requests.
As we see, all GET requests to the / route will be redirected to the /posts route.
PostController
PostController will be mapped to handle GET /posts/* requests.
At this point, there are no handler methods defined in the class. We define an instance variable named PAGINATIONSIZE that indicates the size of pagination -- i.e., the number of posts to render at once.
Then, we inject an implementation of PostRepository.
We will define two additional methods that handle requests made to GET /posts and GET /posts/{id}.
The getPaginatedPosts method takes three parameters -- the requested page, size of each page, and the model to add attributes to.
We obtain an instance of PageRequest using three arguments -- page, size, and the order of posts.
The order of posts should be in decreasing order of their ID since larger the ID, more recent the post was authored. This is because when we retrieve our posts, we need them in the order of their decreasing recency.
Then, since PostRepository extends the PagingAndSortingRepository, its findAll method can take the said instance of PageRequest, which in turn returns a Page of posts. This instance of type Page can be converted to a List type, and that is exactly what we did.
Then, using the total number of posts available, we compute the total number of pages to be made available to the client.
Finally, we add these values as attributes to our Model parameter.
Next, we define a getPostById method.
The getPostById method takes two parameters -- ID of the post to retrieve and the model to add attributes to.
Using the findById method of PostRepository, we retrieve an Optional instance of type Post. This provides a sense of null-safety as we reduce our chances of running into Null Pointer Exceptions.
Only if the Optional instance contains a non-null value (i.e., if a post of that ID exists), do we add the post as a model attribute.
Code
This is it for part 2. Stay tuned for more parts where we continue building our Markdown blog.
As always, all of the code up to this point has been pushed to the GitHub repository.