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

Create Spring Boot Application

Create a Spring Boot application with required dependencies. We will add spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-freemarker, Lombok(To reduce boilerplate code) and 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>

Configure the H2 database and Entity class

Add the mentioned 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.java JPA entity class. This class maps the Student table of the H2 database with the entity class.

The entity class 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;
}

Create Repository, Service, and Controller layers

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

Create JPA Repository

Create an interface with the name StudentRepository.java

Spring boot’s JPA starter provides built-in support for basic operations on the entity class. 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> {

}

Create the service class

Create a StudentService.java 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);
	}
}

Create controller class

Create a spring controller class by creating a StudentController.java class.

This controller class contains 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:/";
	}
}

Few of the important points are:

  • The URL path “/” is will render the home.ftlh FreeMarker template file and displays all the available students. The “students” model attribute contains all available students.
  • The GET URL mapping with path “/create” returns the create student page. This path will add a new Student entity object to the model attribute and renders the create-update.ftlh 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.
  • 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 home.ftlh template file under the /src/main/resources/templates directory. The application renders this template file on the application startup.

The default file path for the FreeMarker template is the /src/main/resources/templates directory. The spring-boot-starter-freemarker dependency auto-configures the FreeMarker 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>

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. Each iteration repeats the HTML block inside the FreeMarker directive.
  • We are 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 FreeMarker 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>

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 operation on the Student entity, as given below.

Spring boot freemarker crud example

Conclusion

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

We learned how spring-boot-starter-freemarker 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.

You may also be interested in