Tutorial describing how to create the simple CRUD (Create Read Update Delete) application using Spring Boot, JSP template engine and H2 as embedded database.
Technologies used:
- Spring Boot 2.1.7.RELEASE
- JDK 1.8
- Maven 3
- JSP
- Spring Data JPA
- H2 database
Project setup
Project dependencies managed by the Maven’s pom.xml configuration file:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> </parent> <groupId>com.devcases.springboot</groupId> <artifactId>springboot-crud</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</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>
The explanation of the above dependencies:
spring-boot-starter-web
– packages all the necessary dependencies together with the auto configuration for running simple web applicationspring-boot-starter-tomcat
– provides the embedded tomcat application serverspring-boot-starter-data-jpa
– provides auto configuration for using Spring Data JPA with Hibernatetomcat-embed-jasper
– enables support for JSPjstl
– provides JSP Standard Tag Library (JSTL) – collection of useful JSP tags. We will use theform
tag.h2
– provides auto configured embedded H2 databasespring-boot-maven-plugin
– plugin prividing commands to work with a Spring Boot application (we will be usine thespring-boot:run
command)
Application Initializer
@SpringBootApplication @EnableAutoConfiguration @ComponentScan(basePackages={"com.devcases.springboot.crud.library"}) @EnableJpaRepositories(basePackages="com.devcases.springboot.crud.library.repository") public class Application extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
The only non standard annotation is @EnableJpaRepositories
, which instantiates Spring Data repositories defined in the specified directory.
Controller
@Controller public class LibraryController { private BookService service; @Autowired public LibraryController(BookService service) { this.service = service; } @GetMapping public String showAllBooks(Model model) { model.addAttribute("books", service.findAll()); return "books"; } @GetMapping("/new-book") public String showBookCreationForm(Model model) { model.addAttribute("book", new Book()); return "new-book"; } @PostMapping(value = "/add", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public String addNewBook(@Valid @ModelAttribute Book book, BindingResult result, Model model) { if (result.hasErrors()) { return "new-book"; } service.save(book); model.addAttribute("books", service.findAll()); return "books"; } @GetMapping("/{id}") public String showBookdById(@PathVariable Long id, Model model) { Book book = service.findById(id) .orElseThrow(() -> new IllegalArgumentException("Invalid book Id:" + id)); model.addAttribute("book", book); return "edit-book"; } @PostMapping("/{id}/update") public String updateBook(@PathVariable Long id, @Valid @ModelAttribute Book book, BindingResult result, Model model) { if (result.hasErrors()) { return "edit-book"; } service.findById(id) .orElseThrow(() -> new IllegalArgumentException("Invalid book Id:" + id)); service.save(book); model.addAttribute("books", service.findAll()); return "books"; } @PostMapping("/{id}/delete") public String deleteBook(@PathVariable Long id, Model model) { service.findById(id) .orElseThrow(() -> new IllegalArgumentException("Invalid book Id:" + id)); service.deleteById(id); model.addAttribute("books", service.findAll()); return "books"; } }
The controller contains set of methods repsonsible for CRUD operations:
showAllBooks
– finds and shows all the persisted books in the “books” viewshowBookCreationForm
– creates empty book and allows user to fill the book in the “new-book” viewaddNewBook
– fired when data of new book are requested to persist. Thanks to the @Valid annotation, the Spring validates (according to the validation annotations in theBook
class) passed book and provides errors in the BindingResult object. Based on the result we can return to the same view with the errors or save the book in the database. After successull operation method shows the “books” viewshowBookdById
– finds the book with the specified id and shows its data in the “edit-book” view.updateBook
– saves existing book (with the id passed in the url – we use PathVariable annotation to resolve this id), validates data in the same way as the showBookCreationForm method.deleteBook
– deletes book with specified id.
In our case the controller contains only GET and POST methods. But a real RESTful controller, should have a PUT updateBook and DELETE deleteBook methods. This is because the view (described later) is simple form without any additional scripts. Html Form can’t perform any PUT or DELETE methods – it can only make GET or POST requests.
View
books.jsp template:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<%@ page isELIgnored="false" %>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Book Library</title>
</head>
<body>
<div>
<div>
<h2>Library</h2>
<hr/>
<a href="/new-book">
<button type="submit">Add new book</button>
</a>
<br/><br/>
<div>
<div>
<div>Book list</div>
</div>
<div>
<table>
<tr>
<th>Id</th>
<th>Author</th>
<th>Name</th>
</tr>
<c:forEach var="book" items="${books}">
<tr>
<td>${book.id}</td>
<td>${book.author}</td>
<td>${book.name}</td>
<td>
<a href="/${book.id}">Edit</a>
<form action="/${book.id}/delete" method="post">
<input type="submit" value="Delete" />
</form>
</td>
</tr>
</c:forEach>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
new-book.jsp template:
<!DOCTYPE html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html lang="en">
<head>
<%@ page isELIgnored="false" %>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Book Library</title>
</head>
<body>
<div>
<h2>New User</h2>
<div>
<div>
<form:form action="/add" modelAttribute="book" method="post">
<div>
<div>
<form:label path="author">Author</form:label>
<form:input type="text" id="author" path="author"/>
<form:errors path="author" />
</div>
<div>
<form:label path="name">Name</form:label>
<form:input type="text" id="name" path="name"/>
<form:errors path="name" />
</div>
</div>
<div>
<div>
<input type="submit" value="Add User">
</div>
</div>
</form:form>
</div>
</div>
</div>
</body>
</html>
edit-book.jsp template:
<!DOCTYPE html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html lang="en">
<head>
<%@ page isELIgnored="false" %>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Book Library</title>
</head>
<body>
<div>
<h2>New User</h2>
<div>
<div>
<form:form action="${book.id}/update" modelAttribute="book" method="post">
<div>
<div>
Id: ${book.id}
</div>
<div>
<form:label path="author">Author</form:label>
<form:input type="text" id="author" path="author"/>
<form:errors path="author" />
</div>
<div>
<form:label path="name">Name</form:label>
<form:input type="text" id="name" path="name"/>
<form:errors path="name" />
</div>
</div>
<div>
<div>
<input type="submit" value="Update User">
</div>
</div>
</form:form>
</div>
</div>
</div>
</body>
</html>
The JSP templates contain plain html without any additional scripts. If we used script, we could use the PUT and DELETE http requests.
In our templates we use the form
tag (privided in the jstl
library) – thanks to it we can easily define label, input tag and erros for book fields.
Configuration
The configuration in application.properties
file contains only information how to resolve JSP files.
spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
Running
The project can be started by the running the following command:
mvn clean spring-boot:run
The command deploys the apllication on the embedded tomcat server.
By visiting the localhost:8080
url, we should see the main page containing empty list of books with the ability to add new ones. Because we used the in memory H2 database, restart of the application resets the database – all our data is lost.
The example code you can download from here