Spring Boot with Thymeleaf tutorial, part 3 – Spring Data JPA
In this tutorial I am going to add a possibility of storing added posts in a database. I extend previous project where post form is already implemented. I use H2 in-memory database to simplify project’s complexity. To implement data access layer in the application I use Spring Data JPA.
1. Set up the project as is described in previous post or checkout the source code.
2. Add Maven dependencies to enable Spring Data JPA and H2 database:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency>
pom.xml should look like this:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jvmhub</groupId> <artifactId>spring-boot-thymeleaf-jpa</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>spring-boot-thymeleaf-jpa</name> <description>Spring Boot with Thymeleaf, part 3 - SPRING DATA JPA</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3. Create business domain object PostEntity. This object will be stored in database by JPA. It is very similar to Post object in form package, but has additional JPA annotations like @Entity, @Id, @GeneratedValue and id field. I have decided to split it into two objects. It would be also possible use only one Post object, but then the file could look unreadable.
package com.jvmhub.springboot.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class PostEntity { public PostEntity() {} public PostEntity(String title, String content) { this.title = title; this.content = content; } @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String title; private String content; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
4. The repository class is needed to manipulate (read, save, update, delete) on domain objects. Using Spring Data it is very easy to create simple repository interface by extending CrudRepository class from Spring Data. By default there are enabled number of common functions, but it is also possible to create own functions, like findByTitle(String title) in example below. The only thing what is needed to do is defining functions according to Spring Data rules. More informations about Spring Data you can find here.
package com.jvmhub.springboot.repository; import java.util.List; import org.springframework.data.repository.CrudRepository; import com.jvmhub.springboot.domain.PostEntity; public interface PostRepository extends CrudRepository<PostEntity, Long> { List<PostEntity> findByTitle(String title); }
5. Right now we can use the repository in our other classes. PostRepository is used in Home controller for storing in database post passed in form and getting all posts from database to show them on results view. Spring can automatically find it out if we use @Autowired annotation. Functions postRepository.findAll() and postRepository.save() are inherited from CrudRepository, so we had not to implement them.
package com.jvmhub.springboot.controller; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.jvmhub.springboot.domain.PostEntity; import com.jvmhub.springboot.form.Post; import com.jvmhub.springboot.repository.PostRepository; @Controller public class Home { @Autowired private PostRepository postRepository; @RequestMapping(value="/", method=RequestMethod.GET) public String index(Post post) { return "index"; } @RequestMapping(value = "/", method = RequestMethod.POST) public String addNewPost(@Valid Post post, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { return "index"; } postRepository.save(new PostEntity(post.getTitle(), post.getContent())); model.addAttribute("posts", postRepository.findAll()); return "redirect:result"; } @RequestMapping(value = "/result", method = RequestMethod.GET) public String showAllPosts(Model model) { model.addAttribute("posts", postRepository.findAll()); return "result"; } }
6. The last thing which has to be realized is a view where all posts from the database can be displayed. We can modify result.html file to let Themeleaf template engine to iterate over collection of posts. It is very simple in Thymeleaf and implementation can look like this:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Spring Boot and Thymeleaf example</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h3>Spring Boot and Thymeleaf, part 3 - SPRING DATA JPA</h3> <p th:each="post : ${posts}"> <h4>ID:</h4> <div th:text="${post.id}"></div> <h4>Title:</h4> <div th:text="${post.title}"></div> <h4>Content:</h4> <div th:text="${post.content}"></div> <div>---------------------------------------------------------</div> </p> </body> </html>
All file structure should look like this:
Compile the application by
mvn package
and run it by
java -jar target/spring-boot-thymeleaf-jpa-0.0.1.jar
Your application is available on http://localhost:8080 and all posts can be displayed on http://localhost:8080
Complete source code: https://github.com/jvmhub/Spring-Boot-with-Thymeleaf-part-3-Spring-Data-JPA