Hello, Habr! I present to you the translation of the guide
“Spring MVC + Spring Data JPA + Hibernate - CRUD Example” by Nam Ha Minh.
In this Spring Java tutorial, you'll learn how to set up a Spring MVC application to work with Spring Data JPA by developing a simple web application that lets you manage customer information.
Upon completion of this tutorial, you can create a Java web application based on Spring MVC and Spring Data JPA technologies, which looks like this:
Programs and technologies used in this guide: Java 8, Apache Tomcat 9, MySQL Server 5.7, Eclipse IDE 4.7 (Oxygen), Spring Framework 5.1, Hibernate 5.4, Spring Data JPA 2.1.5, and Servlet 3.1.
Let's start by creating a database.
1. Creating a database
We will use MySQL. In our example, we will work with data in the
customer table, which is located in the schema named
sales . The
customer table has 4 fields:
id ,
name ,
email and
address :
You can run the following MySQL script to create the schema and table:
CREATE DATABASE `sales`; CREATE TABLE `customer` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, `email` varchar(45) NOT NULL, `address` varchar(45) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. Creating a project in Eclipse
Create a Dynamic Web Project in Eclipse, and convert it into a Maven project: to do this, right-click on the project, select
Configure> Convert to Maven Project . In the
Create new POM dialog box that opens, enter the following information:
- Group Id:
net.codejava
- Artifact Id:
CustomerManager
Also ensure that the JRE version for the Java project is 8 or later.
Then, open
pom.xml
(the Maven file) to configure the dependencies for this project. Declare version properties for Spring and Hibernate Frameworks:
<properties> <spring.version>5.1.5.RELEASE</spring.version> <hibernate.version>5.4.1.Final</hibernate.version> </properties>
Specify the dependency for Spring Framework:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency>
To create Spring MVC web applications:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency>
To use Spring Data JPA:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.1.5.RELEASE</version> </dependency>
We use Hibernate as the JPA implementation, so add the following dependency:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency>
In order for the application to work with MySQL, we need a dependency for the MySQL JDBC driver:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.14</version> <scope>runtime</scope> </dependency>
And now the dependencies for Java Servlet, JSP and JSTL:
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
Create two Java packages at the root of the project:
-
net.codejava.config
: for configuration classes.
-
net.codejava.customer
: for application classes.
3. Creating a JPA configuration file
Since we use JPA, we need to define properties for connecting to the database in the
persistence.xml
file, and not in
hibernate.cfg.xml
. Create a new directory named
META-INF
in the project's source folder to place the
persistence.xml
file in it:
And enter the code below in this file:
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="SalesDB"> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/sales" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="(password)" /> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> </properties> </persistence-unit> </persistence>
As you can see, we specify properties for connecting to the database, such as URL, user, password, and the JDBC driver class. Also note that the name
SalesDB
will be used by us in the configuration code.
4. Creating a Model Class
Create a
Customer
class that maps to the
customer table in the database as follows:
package net.codejava.customer; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; private String address; protected Customer() { } protected Customer(String name, String email, String address) { this.name = name; this.email = email; this.address = address; }
As you can see, we use the
@Entity
annotation to map this class to the
customer table (the class name is the same as the table name). All class field names are identical to the field names in the table. The
id
field has annotations
@Id
and
@GeneratedValue
to indicate that this field is a primary key and its value is generated automatically.
5. Configuration of Spring MVC and Spring Data JPA
Next, we write Java code to configure Spring MVC and Spring Data JPA. We will use the Java-based configuration, as it is simpler than XML.
Configure Spring Dispatcher Servlet
To use Spring MVC in our application, we need to register the Spring Dispatcher Servlet when starting the application by writing the following class:
package net.codejava.config; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public class WebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(WebMvcConfig.class); ServletRegistration.Dynamic dispatcher = servletContext.addServlet( "SpringDispatcher", new DispatcherServlet(appContext)); dispatcher.setLoadOnStartup(1); dispatcher.addMapping("/"); } }
The
onStartup()
method of this class will be automatically called by the servlet when the application loads. The Spring Dispatcher Servlet processes all requests by matching the URL "/" and looks for the configuration in the
WebMvcConfig
class, which is described below.
Configure Spring MVC
Create the
WebMvcConfig
class in the
net.codejava.config
package containing the following code:
package net.codejava.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @ComponentScan("net.codejava ") public class WebMvcConfig { @Bean(name = "viewResolver") public InternalResourceViewResolver getViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
This class is marked with the
@Configuration
annotation telling Spring that it is a configuration file.
@ComponentScan
tells Spring to look for configuration classes in
net.codejava
package.
In this class, we create a bean that recognizes views by specifying a prefix and suffix for these views. Therefore, create the
views
directory inside the
WebContent/WEB-INF
to store JSP files.
Here you can add other Spring MVC configurations.
Configuring Spring Data JPA
To work with Spring Data JPA, we need to create two beans components:
EntityManagerFactory
and
JpaTransactionManager
. Therefore, we will create another configuration class
JpaConfig
:
package net.codejava.config; import javax.persistence.EntityManagerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration @EnableJpaRepositories(basePackages = {"net.codejava.customer"}) @EnableTransactionManagement public class JpaConfig { @Bean public LocalEntityManagerFactoryBean entityManagerFactory() { LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean(); factoryBean.setPersistenceUnitName("SalesDB"); return factoryBean; } @Bean public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory); return transactionManager; } }
Here we use two important annotations:
@EnableJpaRepositories
: tells Spring Data JPA to look for repository classes in the specified package (net.codejava) to inject the appropriate code at run time.@EnableTransactionManagement
: tells Spring Data JPA to generate code to manage transactions at runtime.
In this class, the first method creates an instance of
EntityManagerFactory
to control the Persistence Unit of our
SalesDB
(this name is specified in
persistence.xml
above).
The last method creates an instance of
JpaTransactionManager
for
EntityManagerFactory
, created by the method earlier.
This is the minimum configuration required to use Spring Data JPA.
Creating a Repository Interface
Create a
CustomerRepository
interface that extends the
CrudRepository
interface defined in Spring Data JPA:
package net.codejava.customer; import java.util.List; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; public interface CustomerRepository extends CrudRepository<Customer, Long> { }
This is almost all the code we need to access the data. Just agree? With Spring Data JPA, we don’t need to write DAO (Java Data Acces Object) code. Just declare an interface that extends the
CrudRepository
interface, which defines CRUD methods such as
save()
,
findAll()
,
findById()
,
deleteById()
, etc. At run time, Spring Data JPA will automatically generate code.
Please note that we must specify the type of the model class and the type of the primary key field when extending
CrudRepository
:
CrudRepository<Customer, Long>
.
7. Creating a Service Class
Next, create the
CustomerService
class:
package net.codejava.customer; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional public class CustomerService { @Autowired CustomerRepository repo; public void save(Customer customer) { repo.save(customer); } public List<Customer> listAll() { return (List<Customer>) repo.findAll(); } public Customer get(Long id) { return repo.findById(id).get(); } public void delete(Long id) { repo.deleteById(id); } }
Notice the
@Transactional
annotation that
@Transactional
our class. This means that all methods of this class will be intercepted by Spring Data JPA for transaction management. And the
CustomerRepository
interface instance will be embedded in this class:
@Autowired CustomerRepository repo;
This seems like magic since we are not writing DAO code, but Spring Data JPA will automatically create an implementation at runtime.
As you can see, all methods in this class are for CRUD operations. It simply delegates the entire call to the
CustomerRepository
object. This class may seem redundant to you, but it is necessary to separate the business / service level from the repository / DAO level.
8. Creating a Spring MVC Controller
Create a class
CustomerContoroller
to handle all requests from customers:
package net.codejava.customer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class CustomerController { @Autowired private CustomerService customerService;
This is a typical Spring MVC Controller class that is annotated using
@Controller
. You can see that the
CustomerService
instance is being injected into this object using the
@Autowired
annotation.
We will write the processing methods in the following sections.
9. Adding a customer list
All customers will be displayed on the home page of our application. To do this, add the appropriate processing method to our
CustomerController
class:
@RequestMapping("/") public ModelAndView home() { List<Customer> listCustomer = customerService.listAll(); ModelAndView mav = new ModelAndView("index"); mav.addObject("listCustomer", listCustomer); return mav; }
The browsing home page (
index.jsp
) should look like this:
<%@ 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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Customer Manager</title> </head> <body> <div align="center"> <h2>Customer Manager</h2> <form method="get" action="search"> <input type="text" name="keyword" /> <input type="submit" value="Search" /> </form> <h3><a href="/new">New Customer</a></h3> <table border="1" cellpadding="5"> <tr> <th>ID</th> <th>Name</th> <th>E-mail</th> <th>Address</th> <th>Action</th> </tr> <c:forEach items="${listCustomer}" var="customer"> <tr> <td>${customer.id}</td> <td>${customer.name}</td> <td>${customer.email}</td> <td>${customer.address}</td> <td> <a href="/edit?id=${customer.id}">Edit</a> <a href="/delete?id=${customer.id}">Delete</a> </td> </tr> </c:forEach> </table> </div> </body> </html>
Now you can start the web application. Add a few lines to the
customer table and go to
http://localhost:8080/CustomerManager/
and you will see something similar:
10. Adding a new user
In order to implement the function of creating a new client, we need to write two handler methods. And the first one will display a new form for adding a client:
@RequestMapping("/new") public String newCustomerForm(Map<String, Object> model) { Customer customer = new Customer(); model.put("customer", customer); return "new_customer"; }
Let's write the JSP form itself with the name
new_customer.jsp
:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>New Customer</title> </head> <body> <div align="center"> <h2>New Customer</h2> <form:form action="save" method="post" modelAttribute="customer"> <table border="0" cellpadding="5"> <tr> <td>Name: </td> <td><form:input path="name" /></td> </tr> <tr> <td>Email: </td> <td><form:input path="email" /></td> </tr> <tr> <td>Address: </td> <td><form:input path="address" /></td> </tr> <tr> <td colspan="2"><input type="submit" value="Save"></td> </tr> </table> </form:form> </div> </body> </html>
Now on the main page you will see the
New Customer link, when you click on it you will see a new form:
The second handler method will process the
Save button in this form:
@RequestMapping(value = "/save", method = RequestMethod.POST) public String saveCustomer(@ModelAttribute("customer") Customer customer) { customerService.save(customer); return "redirect:/"; }
As you can see, it redirects the client to the home page, after successfully saving the user.
11. Change user data
To implement the client editing function, add the following handler method to the
CustomerController
class:
@RequestMapping("/edit") public ModelAndView editCustomerForm(@RequestParam long id) { ModelAndView mav = new ModelAndView("edit_customer"); Customer customer = customerService.get(id); mav.addObject("customer", customer); return mav; }
Let's write the
edit_customer.jsp
form, which is called by this method:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Edit Customer</title> </head> <body> <div align="center"> <h2>Edit Customer</h2> <form:form action="save" method="post" modelAttribute="customer"> <table border="0" cellpadding="5"> <tr> <td>ID: </td> <td>${customer.id} <form:hidden path="id"/> </td> </tr> <tr> <td>Name: </td> <td><form:input path="name" /></td> </tr> <tr> <td>Email: </td> <td><form:input path="email" /></td> </tr> <tr> <td>Address: </td> <td><form:input path="address" /></td> </tr> <tr> <td colspan="2"><input type="submit" value="Save"></td> </tr> </table> </form:form> </div> </body> </html>
Click on the
Edit hyperlink next to the client on the home page, the client editing form will be called, which will look something like this:
The handler method still processes the
Save button.
12. Removing a client
To implement the delete function, write the following handler method in the
CustomerController
class:
@RequestMapping("/delete") public String deleteCustomerForm(@RequestParam long id) { customerService.delete(id); return "redirect:/"; }
Click on the
Delete hyperlink next to the client on the main page. The client will be deleted, and the list will be updated.
13. Search by customers
Finally, let's implement a search function that allows the user to search for customers by entering a keyword. The search function searches for keywords in three fields: name, email and address, for which we need to write our own method in the
CustomerRepository
interface:
package net.codejava.customer; import java.util.List; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; public interface CustomerRepository extends CrudRepository<Customer, Long> { @Query(value = "SELECT c FROM Customer c WHERE c.name LIKE '%' || :keyword || '%'" + " OR c.email LIKE '%' || :keyword || '%'" + " OR c.address LIKE '%' || :keyword || '%'") public List<Customer> search(@Param("keyword") String keyword); }
The
search()
method is just an abstract method annotated with
@Query
. The search query is a JPA query.
Then add the method to the
CustomerService
class:
public List<Customer> search(String keyword) { return repo.search(keyword); }
Now add a handler method to the
CustomerController
class:
@RequestMapping("/search") public ModelAndView search(@RequestParam String keyword) { List<Customer> result = customerService.search(keyword); ModelAndView mav = new ModelAndView("search"); mav.addObject("result", result); return mav; }
And create a
search.jsp
search result
search.jsp
:
<%@ 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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Search Result</title> </head> <body> <div align="center"> <h2>Search Result</h2> <table border="1" cellpadding="5"> <tr> <th>ID</th> <th>Name</th> <th>E-mail</th> <th>Address</th> </tr> <c:forEach items="${result}" var="customer"> <tr> <td>${customer.id}</td> <td>${customer.name}</td> <td>${customer.email}</td> <td>${customer.address}</td> </tr> </c:forEach> </table> </div> </body> </html>
To test the search function, enter a keyword in the search field on the home page, and press Enter. You will see the search result page:
conclusions
In this guide, you learned how to develop a Spring MVC web application using Spring Data JPA to access data. As you can see, Spring Data JPA greatly reduces and simplifies the code we need to write.
For comparison, here is the project structure in the Eclipse IDE:
Thank you for reading!