Spring Boot FreeMarker CRUD Example

FreeMarker is a template engine that processes the server-side data and generates dynamic HTML content.

In this article, we will learn how to create a Spring MVC web application that performs CRUD operation using Spring Boot, JPA, H2 database, and FreeMarker template.

Technologies used in this article are:

  • Spring Boot version : 2.3.0.RELEASE
  • Java version 1.8

Creating the Spring Boot Application

Create a Spring Boot application with the required dependencies.

We will add spring boot web, spring boot data jpa, spring boot freemarker starter dependencies, Lombok(To reduce boilerplate code), and also the H2(Embedded H2 database) maven dependencies.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

Configuring the H2 database and Entity class

Add the below configuration property to the Spring Boot application’s “application.properties” file. 

This configuration property configures the data source URL of the application.

spring.datasource.url=jdbc:h2:~/asbdb;

Create a Student JPA entity class. This class maps the Student table of the H2 database with the entity class.

The entity class also contains the auto-generated idname, and grade fields.

package com.asbnotebook.student;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Data;
@Entity
@Data
public class Student {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Integer id;
	
	private String name;
	private String grade;
}

Creating the Repository, Service, and Controller layers

For the Student entity class, let us create a Repository, service, and Controller layers.

Creating the JPA Repository

Create an interface with the name StudentRepository

Spring boot’s JPA starter provides built-in support for basic operations on the entity class.

Also, we need to extend the JpaRepository interface to get these functionalities.

package com.asbnotebook.student;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StudentRepository extends JpaRepository<Student, Integer> {
}

Creating the service class

Create a StudentService class and add the below content.

package com.asbnotebook.student;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
	@Autowired
	private StudentRepository studentRepository;
	public List<Student> getAllStudents() {
		return studentRepository.findAll();
	}
	public void createStudent(Student student) {
		studentRepository.save(student);
	}
	public void updateStudent(Student student, Integer id) {
		Student std = studentRepository.getOne(id);
		std.setName(student.getName());
		studentRepository.save(std);
	}
	public void deleteStudent(Integer id) {
		studentRepository.deleteById(id);
	}
	public Student getStudent(Integer id) {
		return studentRepository.getOne(id);
	}
}

Creating the controller class

Create a spring controller class by creating a StudentController class.

This controller class also contains the required URL mappings for the CRUD operation of the student entity.

package com.asbnotebook.student;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class StudentController {
	@Autowired
	private StudentService studentService;
	@GetMapping("/")
	public String getAllStudents(Model model) {
		List<Student> students = studentService.getAllStudents();
		model.addAttribute("students", students);
		return "home";
	}
	@GetMapping("/create")
	public String createStudentPage(Model model) {
		Student student = new Student();
		model.addAttribute("student", student);
		model.addAttribute("isUpdate", false);
		return "create-update";
	}
	@GetMapping("/update/{id}")
	public String updateStudentPage(Model model, @PathVariable("id") Integer id) {
		Student student = studentService.getStudent(id);
		model.addAttribute("student", student);
		model.addAttribute("isUpdate", true);
		return "create-update";
	}
	@PostMapping("/update/{id}")
	public String createStudent(@ModelAttribute("student") Student student, @PathVariable("id") Integer id) {
		studentService.updateStudent(student, id);
		return "redirect:/";
	}
	@PostMapping("/create")
	public String createStudent(@ModelAttribute("student") Student student) {
		studentService.createStudent(student);
		return "redirect:/";
	}
	@GetMapping("/delete/{id}")
	public String deleteStudent(@PathVariable("id") Integer id) {
		studentService.deleteStudent(id);
		return "redirect:/";
	}
}

A few of the important points are:

  • The URL path “/” is will render the home Free Marker template file and displays all the available students. Also, the students model attribute contains all available students.
  • The GET URL mapping with path “/create” returns the create student page. This path will also add a new Student entity object to the model attribute and renders the create-update template file.
  • The URL path “/update/{id}” GET mapping will render existing student details and enables updating the student name and grade.
  • The model attribute “isUpdate” determines the type of operation.
  • The POST URL mappings with path “/create” and “/update/{id}” are used to create and update the student details, respectively.
  • The GET URL path “/delete/{id}” deletes a particular student entity.
  • Also, after create, update, and delete operations, the application displays the updated list of students by redirecting to the home page URL path.

Creating FreeMarker templates

The FreeMarker template files have .ftl or .ftlh extension. These are normal HTML files that contain FreeMarker tags and directives.

Create a template file with the name home, under the /src/main/resources/templates folder. The application renders this template file on the application startup.

Also, the default file path for the FreeMarker template is the /src/main/resources/templates folder.

Finally, the spring boot freemarker starter dependency auto-configures the Free Marker template path.

<!doctype html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</head>
<body>
	<div class="container">
		<h1>Student CRUD operation with Freemarker Template</h1>
		<a href="/create">Create New Student</a>
		<table class="table">
			<thead>
				<tr>
					<th scope="col">Id</th>
					<th scope="col">Name</th>
					<th scope="col">Grade</th>
					<th scope="col"></th>
					<th scope="col"></th>
				</tr>
			</thead>
			<tbody>
				<#list students as student>
				<tr>
					<th scope="row">${student.id}</th>
					<td>${student.name}</td>
					<td>${student.grade}</td>
					<td><a href="/update/${student.id}">Update</a></td>
					<td><a href="/delete/${student.id}">Delete</a></td>
				</tr>
				</#list>
			</tbody>
		</table>
	</div>
</body>
</html>

A few of the important points to notice are:

  • We have used bootstrap to give some styling to the application.
  • The FreeMarker’s <#list students as student> and </list> directive iterate through the Spring model attribute students. Also, each iteration repeats the HTML block inside the FreeMarker directive.
  • We are also printing the idname, and grade attributes of each student object in a table.

Create a FreeMarker template file with the name create-update.ftlh.

To create or update a student entity, we use this Free Marker template file.

<!doctype html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</head>
<body>
	<div class="container">
		<h1>
			<#if !isUpdate>Create</#if>
			<#if isUpdate>Update</#if>
			Student
		</h1>
		<div>
			<form action="<#if isUpdate>/update/${student.id}</#if><#if !isUpdate>/create</#if>" name="student" method="post">
				<table class="table">
					<tbody>
					<thead>
						<tr>
							<th>Field</th>
							<th>Value</th>
						</tr>
					</thead>
					<tbody>
						<#if isUpdate>
						<tr>
							<th>ID</th>
							<td><div name="id">${student.id}</div></td>
						</tr>
						</#if>
						<tr>
							<th>Name</th>
							<td><input type="text" name="name" value="<#if student.name??>${student.name}</#if>" /></td>
						</tr>
						<tr>
							<th>Grade</th>
							<td><input type="text" name="grade" value="<#if student.grade??>${student.grade}</#if>" /></td>
						</tr>
					</tbody>
				</table>
				<button type="submit" class="btn btn-primary">Save</button>
			</form>
		</div>
	</div>
</body>
</html>

A few of the important points to notice here are:

  • The <#if isUpdate> checks the boolean model attribute isUpdate and determines whether the operation is a create or update. The <#if !isUpdate> FreeMarker directive is the inverted version.
  • The id field will be auto-generated by JPA during the create student operation.
  • For update operation, existing student id, name, and grade fields will be populated. The id field can not be updated.
  • FreeMarker’s tag ${student.name} maps the input field name‘s value to the student model attribute. Tag ${student.grade} maps the input field grade‘s value to the student model attribute.

Testing the application

Start the Spring Boot application.

We should be able to perform CRUD operations on the Student entity, as given below.

Spring boot freemarker crud example

Conclusion

In this article, we learned how to perform CRUD operations on a JPA entity using Spring boot and FreeMarker template language.

We also learned how spring boot freemarker starter dependency configures the basic configuration automatically to resolve the FreeMarker template files that are available on the application’s classpath.

The complete example code is available on GitHub.