Build a Markdown-based Blog with Spring Boot - Part 2
Tags: java, springboot, thymeleaf, markdown
Published: 2021-05-22 11:53:07

<p>If you haven't read the <a href="https://www.roshanadhikary.com.np/2021/05/build-a-markdown-based-blog-with-spring-boot-part-1.html">first part</a>, please do so.</p><p>At the end of the first part, we had defined the POJO classes for the two entities (<em>Author</em> and <em>Post</em>) in our project.</p><p>We begin the second part by defining the repository interfaces for those entities.</p><h3 style="text-align: left;">Defining repositories</h3><p style="text-align: left;">Spring Data provides several&nbsp;<em>Repository</em> 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&nbsp;<em><a href="https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html" target="_blank">CrudRepository</a></em>&nbsp;and&nbsp;<em><a href="https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/PagingAndSortingRepository.html" target="_blank">PagingAndSortingRepository</a></em>.</p><p style="text-align: left;">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.</p><p style="text-align: left;">The repositories for our entities are defined as follows.</p>
<p>
{% gist https://gist.github.com/roshanadh/f6878a7e370d3bebef438e241e1c2c0a.js %}
</p>
<p>
{% gist https://gist.github.com/roshanadh/077415bf9ecaba0f3db7b2ce26a8436b.js %}
</p>
<p style="text-align: left;">Since we don't need to implement paging or sorting for the Author entity, we can extend CrudRepository for <em>AuthorRepository</em>. On the other hand, since we will be retrieving posts using the paging and sorting abstraction, we need to extend PagingAndSortingRepository for <em>PostRepository</em>.</p><p style="text-align: left;">Note how both CrudRepository and PagingAndSortingRepository take as type arguments the domain class to manage and the type of ID of the domain class.</p><p style="text-align: left;">Another thing to note is that we <a href="https://stackoverflow.com/a/51918761/10218970" rel="nofollow">don't need to annotate</a> either repository interfaces with <em>@Repository</em> or <em>@Component</em>. This is because CrudRepository and PagingAndSorting interfaces are annotated with <em>@NoRepositoryBean</em>, making them <em>intermediate interfaces</em>. Intermediate interfaces are not picked up by the Spring container and as such are not instantiated as beans.</p><p style="text-align: left;">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.</p><h3 style="text-align: left;">Defining controllers</h3><p style="text-align: left;">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.</p><p style="text-align: left;">We need to define two classes -- RootController and PostController -- each of which will handle respective requests.</p><h4 style="text-align: left;">RootController</h4><p style="text-align: left;">RootController will be mapped to handle&nbsp;<em>GET /</em>&nbsp;requests.</p>
<p>
{% gist https://gist.github.com/roshanadh/d57746bf56a798d2b470b6a26423130d.js %}
</p>
<p style="text-align: left;">As we see, all GET requests to the <em>/</em> route will be redirected to the <em>/posts</em> route.</p><h4 style="text-align: left;">PostController</h4><p style="text-align: left;">PostController will be mapped to handle&nbsp;<em>GET /posts/*</em>&nbsp;requests.</p>
<p>
{% gist https://gist.github.com/roshanadh/fa4b667c2934396791544278089394b7.js %}
</p>
<p style="text-align: left;">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.</p><p style="text-align: left;">Then, we inject an implementation of PostRepository.</p><p style="text-align: left;">We will define two additional methods that handle requests made to <em>GET /posts</em> and <em>GET /posts/{id}</em>.</p>
<p>
 {% gist https://gist.github.com/roshanadh/467b038aa5b22607c4af8bd0b259f503.js %}
</p>
<p style="text-align: left;">The <em>getPaginatedPosts</em> method takes three parameters -- the requested page, size of each page, and the model to add attributes to.</p><p style="text-align: left;">We obtain an instance of <em>PageRequest</em> using three arguments -- page, size, and the order of posts.</p><p style="text-align: left;">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.</p><p style="text-align: left;">Then, since PostRepository extends the PagingAndSortingRepository, its <em>findAll</em>&nbsp;method can take the said instance of PageRequest, which in turn returns a <em>Page</em>&nbsp;of posts. This instance of type Page can be converted to a List type, and that is exactly what we did.</p><p style="text-align: left;">Then, using the total number of posts available, we compute the total number of pages to be made available to the client.</p><p style="text-align: left;">Finally, we add these values as attributes to our <em>Model</em> parameter.</p><p style="text-align: left;">Next, we define a <em>getPostById</em> method.</p>
<p>
{% gist https://gist.github.com/roshanadh/3a19d74f75e933031966affd981f10a5.js %}
</p>
<p style="text-align: left;">The getPostById method takes two parameters -- ID of the post to retrieve and the model to add attributes to.</p><p style="text-align: left;">Using the findById method of PostRepository, we retrieve an <em>Optional</em> instance of type Post. This provides a sense of <em>null-safety</em>&nbsp;as we reduce our chances of running into <em>Null Pointer Exception</em>s.</p><p style="text-align: left;">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.</p><h3 style="text-align: left;">Code</h3><p style="text-align: left;">This is it for part 2. Stay tuned for more parts where we continue building our Markdown blog.</p><p style="text-align: left;">As always, all of the code up to this point has been pushed to the GitHub <a href="https://github.com/roshanadh/markdown-blog/tree/part-2" target="_blank">repository</a>.</p>