Programming
Java
9 December 2019

Пишем простое веб-приложение используя Spring MVC, Spring Data JPA и Hibernate

From Sandbox
Привет, Хабр! Представляю Вашему вниманию перевод руководства «Spring MVC + Spring Data JPA + Hibernate — CRUD Example» автора Nam Ha Minh.

В этом руководстве по Java Spring вы узнаете, как настроить Spring MVC приложение для работы с Spring Data JPA, разработав простое веб-приложение, которое позволяет управлять информацией о клиентах.

По завершению этого руководства, вы сможете создать веб-приложение Java основанное на технологиях Spring MVC и Spring Data JPA, которое выглядит следующим образом:

image

Программы и технологии используемые в этом руководстве: 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 и Servlet 3.1.

Начнём с создания базы данных.

1. Создание базы данных


Мы будем использовать MySQL. В нашем пример мы будем работать с данными в таблице customer, которая находится в схеме с именем sales. Таблица customer имеет 4 поля: id, name, email и address:

image

Вы можете запустить следующий MySQL скрипт для создания схемы и таблицы:

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. Создание проекта в Eclipse


Создайте Dynamic Web Project в Eclipse, и конвертируйте его в проект Maven: для этого нажмите ПКМ по проекту, выберете Configure > Convert to Maven Project. В открывшемся диалоговом окне Create new POM введите следующую информацию:

— Group Id: net.codejava
— Artifact Id: CustomerManager

Также убедитесь, что версия JRE для проекта Java 8 или новее.

Затем, откройте pom.xml (файл Maven), чтобы настроить зависимости для этого проекта. Объявите свойства версий для Spring и Hibernate Frameworks:

<properties>
    <spring.version>5.1.5.RELEASE</spring.version>
    <hibernate.version>5.4.1.Final</hibernate.version>
</properties>

Укажите зависимость для Spring Framework:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
</dependency>

Для создания веб-приложений Spring MVC:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>

Для использования 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>

Мы используем Hibernate в качестве реализации JPA, поэтому добавим следующую зависимость:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>${hibernate.version}</version>
</dependency>

Для того, чтобы приложение работало с MySQL, нам нужна зависимость для драйвера MySQL JDBC:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.14</version>
    <scope>runtime</scope>
</dependency>

И теперь зависимости для Java Servlet, JSP и 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>

Создайте два Java пакета в корне проекта:
net.codejava.config: для классов конфигурации.
net.codejava.customer: для классов приложения.

3. Создание файла настройки JPA


Поскольку мы используем JPA, нам нужно определить свойства для подключения к базе данных в файле persistence.xml, а не в hibernate.cfg.xml. Создайте новый каталог с именем META-INF в исходной папке проекта, чтобы поместить в него файл persistence.xml:

image

И впишите в этот файл код представленный ниже:

<?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>

Как вы можете видеть, мы указываем свойства для соединения с базой данных, такие как URL, user, password и класс JDBC драйвера. Так же обратите внимание, что имя SalesDB будет использоваться нами в коде конфигурации.

4. Создание Model Class


Создайте класс Customer, который сопоставляется с таблицей customer в базе данных следующим образом:

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, или вручную.
 
}

Как вы могли увидеть, мы используем аннотацию @Entity для сопоставления этого класса с таблицей customer (имя класса совпадает с именем таблицы). Все имена полей класса идентичны именам полей в таблице. Поле id имеет аннотации @Id и @GeneratedValue, чтобы указать, что это поле является первичным ключом и его значение генерируется автоматически.

5. Конфигурация Spring MVC и Spring Data JPA


Далее, напишем Java код для настройки Spring MVC и Spring Data JPA. Мы будем использовать конфигурацию на основе Java, так как она проще, чем XML.

Настройка Spring Dispatcher Servlet


Для использования Spring MVC в нашем приложении, нам надо зарегистрировать Spring Dispatcher Servlet при запуске приложения, написав следующий класс:

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("/");
          
    }
}

Метод onStartup() этого класса будет автоматически вызываться сервлетом при загрузке приложения. Spring Dispatcher Servlet обарабатывает все запросы cопоставляя URL "/" и ищет конфигурацию в классе WebMvcConfig, которй описан ниже.

Настройка Spring MVC


Создайте класс WebMvcConfig в пакете net.codejava.config, содержащий следующий код:

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;
    }
}

Этот класс помечен аннотацией @Configuration, сообщающей Spring, что это файл конфигурации. Аннотация @ComponentScan говорит Spring искать классы конфигурации в пакете net.codejava.

В этом классе мы создаём bean-компонент, который распознаёт представления (View), с помощью указания префикса и суффикса для этих представлений. Поэтому создайте каталог views внутри каталога WebContent/WEB-INF для хранения JSP файлов.

Здесь вы можете добавить и другие конфигурации Spring MVC.

Настройка Spring Data JPA


Для работы с Spring Data JPA нам надо создать два beans-компонента: EntityManagerFactory и JpaTransactionManager. Поэтому создадим другой конфигурационный класс 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;
    } 
}

Здесь мы используем две важные аннотации:

  • @EnableJpaRepositories: сообщает Spring Data JPA, что нужно искать классы репозитория в указанном пакете (net.codejava) для внедрения соответсвующего кода во время выполнения.
  • @EnableTransactionManagement: сообщает Spring Data JPA, чтобы тот генерировал код для управления транзакциями во время выполнения.

В этом классе первый метод создаёт экземпляр EntityManagerFactory для управления Persistence Unit нашей SalesDB (это имя указано выше в persistence.xml).

Последний метод создаёт экземпляр JpaTransactionManager для EntityManagerFactory, созданный методом ранее.

Это минимальная необходимая конфигурация для использования Spring Data JPA.

Создание Repository Interface


Создайте интерфейс CustomerRepository, который расширяет определенный в Spring Data JPA интерфейс CrudRepository:

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> {
     
}

Это почти весь код, который нам нужен для доступа к данным. Просто, согласитесь? С Spring Data JPA нам не нужно писать DAO (Java Data Acces Object) код. Просто объявите интерфейс, расширяющий интерфейс CrudRepository, в котором определены такие методы CRUD как: save(), findAll(), findById(), deleteById() и т.д. Во время выполнения Spring Data JPA автоматически сгенерирует код.

Обратите внимание, что мы должны указать тип класса модели и тип поля первичного ключа при расширении CrudRepository: CrudRepository<Customer, Long>.

7. Создание Service Class


Затем, создайте класс CustomerService:

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);
    }
     
}

Обратите внимание на аннотацию @Transactional, которой помечен наш класс. Это означает, что все методы этого класса будут перехвачены Spring Data JPA для управления транзакциями. И экземпляр интерфейса CustomerRepository будет внедрён в этот класс:

@Autowired CustomerRepository repo;

Это похоже на магию, поскольку мы не пишем DAO код, но Spring Data JPA автоматически создаст реализацию во время выполнения.

Как вы можете видеть, все методы в данном классе предназначены для операций CRUD. Он просто делегирует весь вызов объекту CustomerRepository. Вам может показаться этот класс избыточным, но он необходим, для отделения уровня business/service от уровня repository/DAO.

8. Создание контроллера Spring MVC


Создайте класс CustomerContoroller для обработки всех запросов от клиентов:

package net.codejava.customer;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
 
 
@Controller
public class CustomerController {
 
    @Autowired
    private CustomerService customerService;
 
    // здесь будут методы обработки
}

Это типичный класс Spring MVC Controller, который аннотирован с помощью @Controller. Вы можете увидеть, что экземпляр CustomerService внедряется в этот объект с помощью аннотации @Autowired.

Мы напишем методы обработки в следующих секциях.

9. Добавление списка клиентов


На домашней странице нашего приложения будут отображаться все клиенты, для этого добавьте соответствующий обрабатывающий метод в наш CustomerController класс:

@RequestMapping("/")
public ModelAndView home() {
    List<Customer> listCustomer = customerService.listAll();
    ModelAndView mav = new ModelAndView("index");
    mav.addObject("listCustomer", listCustomer);
    return mav;
}

Домашняя страница просмотра (index.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>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>

Теперь вы можете запустить веб-приложение. Добавьте несколько строк в таблицу customer и перейдите по адресу http://localhost:8080/CustomerManager/, и вы увидите что-то похожее:

image

10. Добавление нового пользователя


Для того, чтобы реализовать функцию создания нового клиента, нам нужно написать два метода-обработчика. И первый из них будет отображать новую форму для добавления клиента:

@RequestMapping("/new")
public String newCustomerForm(Map<String, Object> model) {
    Customer customer = new Customer();
    model.put("customer", customer);
    return "new_customer";
}

Напишем саму JSP форму с именем 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>

Теперь на главной странице у вас появится ссылка New Customer, при нажатии на которую вы увидите новую форму:

image

Второй метод-обработчик будет обрабатывать кнопку Save в этой форме:

@RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveCustomer(@ModelAttribute("customer") Customer customer) {
    customerService.save(customer);
    return "redirect:/";
}

Как вы можете видеть, она перенаправляет клиента на домашнюю страницу, после успешного сохранения пользователя.

11. Изменение данных пользователя


Чтобы реализовать функцию редактирования клиента, добавим следующий метод-обработчик в класс CustomerController:

@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;
}

Напишем форму edit_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>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>

Нажмите на гиперссылку Edit рядом с клиентом на домашней странице, вызовется форма редактирования клиента, которая будет выглядеть примерно вот так:



Метод-обработчик по-прежнему обрабатывает кнопку Save.

12. Удаление клиента


Для реализации функции удаления, напишите следующий метод-обработчик в классе CustomerController:

@RequestMapping("/delete")
public String deleteCustomerForm(@RequestParam long id) {
    customerService.delete(id);
    return "redirect:/";       
}

Нажмите на гиперссылку Delete рядом с клиентом на главной странице. Клиент удалится, а список обновится.

13. Поиск по клиентам


Наконец, давайте реализуем функцию поиска, которая позволяет пользователю искать клиентов, вводя ключевое слово. Функция поиска ищет ключевые слова в трёх полях: имя, email и адрес, для чего нам потребуется написать собственный метод в интерфейсе CustomerRepository:

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);
}

Метод search() — это просто абстрактный метод, аннотированный с помощью @Query. Поисковый запрос является запросом JPA.

Затем добавьте метод в класс CustomerService:

public List<Customer> search(String keyword) {
    return repo.search(keyword);
}

Теперь добавьте метод-обработчик в класс CustomerController:

@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;    
}

И создайте страницу результата поиска 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>

Для тестирования функции поиска, введите ключевое слово в поле поиска на домашней странице, и нажмите Enter. Вы увидите страницу результата поиска:


Выводы


В данном руководстве вы узнали как можно разработать веб-приложение Spring MVC, используя Spring Data JPA для доступа к данным. Как вы могли видеть, Spring Data JPA значительно уменьшает и упрощает код, который нам нужно писать.

Для сравнения, вот структура проекта в Eclipse IDE:


Благодарю за прочтение!

+4
4.9k 65
Comments 5
Top of the day