Writing a simple web application using Spring MVC, Spring Data JPA and Hibernate

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:

image

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 :

image

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:

image

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; } //       .       IDE,  . } 

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:

image

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:

image

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!

Source: https://habr.com/ru/post/479286/


All Articles