[Из песочницы] Spring MVC 3, Аннтоции Hibernate, MySQL. Туториал по интеграции

Не так давно я начал изучать фреймворк Spring и понял, что количество материала на русском языке ограниченно буквально парой стоящих статей. По быстрому пробежав, я захотел сделать что-то более интересное, но с наскоку взять не удалось. Пришлось погуглить по поводу нескольких вопросов касательно взаимодействия Spring и Hibernate. Неожиданно я наткнулся на блог достаточно интересного разработчика Mark Serrano aka krams. Теперь вместе с вами я хотел бы начать цикл статей-переводов, а так же свое обучение в мире Spring.Приступим…В этом уроке мы создадим простое приложение для управления списком лиц с использованием Spring MVC 3. Мы построим простую CRUD (CReate Update Delete) систему для просмотра, добавления, правки и удаления персон. В качестве слоя для работы с базой данных мы будем использовать Hibernate 3 и базу MySQL, хотя вы можете использовать другую базу данных. В этом уроке предполагается, что вы знакомы с MVC, ORM и SQL (от переводчика: не пугайтесь этих сложных слов, смело читаем, все будет довольно просто)

Что такое Hibernate? Hibernate — библиотека для языка программирования Java, предназначенная для решения задач объектно-реляционного отображения (object-relational mapping — ORM). Данная библиотека предоставляет лёгкий в использовании каркас (фреймворк) для отображения объектно-ориентированной модели данных в традиционные реляционные базы данных.Hibernate решает проблему между хранением объекта в базе данных и его объектно-ориентированном представлении на уровне языка.

Что такое MySQL? База данных MySQL обеспечивает работу наиболее требовательных сетей, электронной комерции и обработки транзакций приложений. Она гарантирует безопасность транзакций, ACID совместимость дает возможность комита, отката, восстановления в случае сбоя, а также возможностей блокировки строк. MySQL обеспечивает простоту использования, масштабируемость и производительность, эти качества сделали MySQL наиболее популярной базой данных с открытым исходным кодом в мире. Некоторые из наиболее посещаемых ресурсов сети использует MySQL, такие как Facebook, Google, Ticketmaster и Ebay.В начале давайте посмотрим на конечную структуру нашего проекта:

image

И посмотрим, как будет выглядеть наше приложение:

image

Мы начнем с определения нашего доменного объекта Person (сущности Person).

Person

package org.krams.tutorial.domain; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; /** * For a complete reference see * * Hibernate Annotations Communit Documentations */ @Entity @Table (name = «PERSON») public class Person implements Serializable { private static final long serialVersionUID = -5527566248002296042L; @Id @Column (name = «ID») @GeneratedValue private Integer id; @Column (name = «FIRST_NAME») private String firstName; @Column (name = «LAST_NAME») private String lastName; @Column (name = «MONEY») private Double money; public Integer getId () { return id; } public void setId (Integer id) { this.id = id; } public String getFirstName () { return firstName; } public void setFirstName (String firstName) { this.firstName = firstName; } public String getLastName () { return lastName; } public void setLastName (String lastName) { this.lastName = lastName; } public Double getMoney () { return money; } public void setMoney (Double money) { this.money = money; } } Person простой POJO содержащий четыре приватных переменных: idfirstNamelastNamemoney

Дополнение от переводчика: POJO (англ. Plain Old Java Object) — «простой Java-объект в старом стиле», простой Java-объект, не унаследованный от какого-то специфического объекта и не реализующий никаких служебных интерфейсов сверх тех, которые нужны для бизнес-модели (Wiki)

Каждой из этих переменных с аннотацией @Column соответствует колонка в базе данных:

IDFIRST_NAMELAST_NAMEMONEY

Вам нет необходимости иметь с ними дело, Hibernate возьмет это на себя. Тем не менее вы должны правильно объявить эти имена (например @Column (name = ``ID``)). Вы не объявляете их в базе данных. Помните — вашей базы данных еще не существует.

POJO ссылается на таблицу вашей базы данных, обратите внимание на аннотацию Table, где указано имя таблицы соответствующей данному объекту.

@Entity @Table (name = «PERSON») public class Person implements Serializable Обратите внимание, аннотация Entity стоит перед аннотацие Table, это говорит Hibernate, что POJO является отображением таблицы базы данных.Мы будем манипулирувать списком лиц, поэтому создадим для этого сервис:

PersonService

package org.krams.tutorial.service; import java.util.List; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.krams.tutorial.domain.Person; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * Service for processing Persons * */ @Service («personService») @Transactional public class PersonService { protected static Logger logger = Logger.getLogger («service»); @Resource (name=«sessionFactory») private SessionFactory sessionFactory; /** * Retrieves all persons * * @return a list of persons */ public List getAll () { logger.debug («Retrieving all persons»); // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession (); // Create a Hibernate query (HQL) Query query = session.createQuery («FROM Person»); // Retrieve all return query.list (); } /** * Retrieves a single person */ public Person get (Integer id) { // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession (); // Retrieve existing person first Person person = (Person) session.get (Person.class, id); return person; } /** * Adds a new person */ public void add (Person person) { logger.debug («Adding new person»); // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession (); // Save session.save (person); } /** * Deletes an existing person * @param id the id of the existing person */ public void delete (Integer id) { logger.debug («Deleting existing person»); // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession (); // Retrieve existing person first Person person = (Person) session.get (Person.class, id); // Delete session.delete (person); } /** * Edits an existing person */ public void edit (Person person) { logger.debug («Editing existing person»); // Retrieve session from Hibernate Session session = sessionFactory.getCurrentSession (); // Retrieve existing person via id Person existingPerson = (Person) session.get (Person.class, person.getId ()); // Assign updated values to this person existingPerson.setFirstName (person.getFirstName ()); existingPerson.setLastName (existingPerson.getLastName ()); existingPerson.setMoney (existingPerson.getMoney ()); // Save updates session.save (existingPerson); } } Мы объявили простую CRUD систему со следующими методами:

getAll ()add ()delete ()edit ()

В каждом методе мы получаем сессию:

Session session = sessionFactory.getCurrentSession ();

Это похоже на соединение с базой данных, так что мы можем делать нашу работу. Объект Session предоставляет множество методов для работы объектами-сущностями. Для этого урока мы используем следующие методы класса Session:

session.createQuery ()session.save ()session.delete ()

Мы создали доменный и сервисный слои, давайте создадим Spring контроллер.

MainController

package org.krams.tutorial.controller; import java.util.List; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.krams.tutorial.domain.Person; import org.krams.tutorial.service.PersonService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; /** * Handles and retrieves person request */ @Controller @RequestMapping (»/main») public class MainController { protected static Logger logger = Logger.getLogger («controller»); @Resource (name=«personService») private PersonService personService; /** * Handles and retrieves all persons and show it in a JSP page * * @return the name of the JSP page */ @RequestMapping (value = »/persons», method = RequestMethod.GET) public String getPersons (Model model) { logger.debug («Received request to show all persons»); // Retrieve all persons by delegating the call to PersonService List persons = personService.getAll (); // Attach persons to the Model model.addAttribute («persons», persons); // This will resolve to /WEB-INF/jsp/personspage.jsp return «personspage»; } /** * Retrieves the add page * * @return the name of the JSP page */ @RequestMapping (value = »/persons/add», method = RequestMethod.GET) public String getAdd (Model model) { logger.debug («Received request to show add page»); // Create new Person and add to model // This is the formBackingOBject model.addAttribute («personAttribute», new Person ()); // This will resolve to /WEB-INF/jsp/addpage.jsp return «addpage»; } /** * Adds a new person by delegating the processing to PersonService. * Displays a confirmation JSP page * * @return the name of the JSP page */ @RequestMapping (value = »/persons/add», method = RequestMethod.POST) public String add (@ModelAttribute («personAttribute») Person person) { logger.debug («Received request to add new person»); // The «personAttribute» model has been passed to the controller from the JSP // We use the name «personAttribute» because the JSP uses that name // Call PersonService to do the actual adding personService.add (person); // This will resolve to /WEB-INF/jsp/addedpage.jsp return «addedpage»; } /** * Deletes an existing person by delegating the processing to PersonService. * Displays a confirmation JSP page * * @return the name of the JSP page */ @RequestMapping (value = »/persons/delete», method = RequestMethod.GET) public String delete (@RequestParam (value=«id», required=true) Integer id, Model model) { logger.debug («Received request to delete existing person»); // Call PersonService to do the actual deleting personService.delete (id); // Add id reference to Model model.addAttribute («id», id); // This will resolve to /WEB-INF/jsp/deletedpage.jsp return «deletedpage»; } /** * Retrieves the edit page * * @return the name of the JSP page */ @RequestMapping (value = »/persons/edit», method = RequestMethod.GET) public String getEdit (@RequestParam (value=«id», required=true) Integer id, Model model) { logger.debug («Received request to show edit page»); // Retrieve existing Person and add to model // This is the formBackingOBject model.addAttribute («personAttribute», personService.get (id)); // This will resolve to /WEB-INF/jsp/editpage.jsp return «editpage»; } /** * Edits an existing person by delegating the processing to PersonService. * Displays a confirmation JSP page * * @return the name of the JSP page */ @RequestMapping (value = »/persons/edit», method = RequestMethod.POST) public String saveEdit (@ModelAttribute («personAttribute») Person person, @RequestParam (value=«id», required=true) Integer id, Model model) { logger.debug («Received request to update person»); // The «personAttribute» model has been passed to the controller from the JSP // We use the name «personAttribute» because the JSP uses that name // We manually assign the id because we disabled it in the JSP page // When a field is disabled it will not be included in the ModelAttribute person.setId (id); // Delegate to PersonService for editing personService.edit (person); // Add id reference to Model model.addAttribute («id», id); // This will resolve to /WEB-INF/jsp/editedpage.jsp return «editedpage»; } } Контроллер объявляет следующие представления:/persons — для получения всех лиц/persons/add (GET) — показать форму «Добавить»/persons/add (POST) — сохраняет новую персону/persons/delete — удаляет существующую персону/persons/edit (GET) — показывает форму «Правка»/persons/edit (POST) — сохраняет «исправленную» персону

Каждое представление вызывает PersonService. Когда PersonService завершает обработку, крнтроллер пересылает запрос на JSP-страницу, которая показывает сообщение с подтверждением. Вот страницы JSP:

personspage.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here

Persons

First Name Last Name Money
Edit Delete Add
There are currently no persons in the list. Add a person. editpage.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here

Edit Person

Id:
First Name:
Last Name
Money
addpage.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here

Create New Person

First Name:
Last Name
Money
editedpage.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page import="java.util.Date" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here

Persons

You have edited a person with id ${id} at <%= new java.util.Date() %>

Return to Main List

addedpage.jsp <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page import="java.util.Date" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here

Persons

You have added a new person at <%= new java.util.Date() %>

Return to Main List

deletedpage.jsp <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page import="java.util.Date" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> Insert title here

Persons

You have deleted a person with id ${id} at <%= new java.util.Date() %>

Return to Main List

Давайте сконфигурируем наше приложение.Для работы Spring MVC в web.xml добавим: web.xml

spring org.springframework.web.servlet.DispatcherServlet 1 spring /krams/* org.springframework.web.context.ContextLoaderListener Обратите внимание на шаблон URL. При обращении к страницам нашего приложения к имени хоста должна быть добавка в виде:/krams

В web.xml мы указали в качестве имени сервлета spring. По соглашению мы также должны создать файл spring-servlet.xml.

spring-servlet.xml

Мы также должны создать файл applicationContext.xml.applicationContext.xml

Обратите внимание, что в файле applicationContext.xml мы объявили следующий импорт: Он содержит файлы конфигурации Hibernate.Hibernate-context.xml

В этом файле мы инкапсулировали все связанные конфигурации Hibernate и Spring.Комментарии по конфигурации:

1. Включаем поддержку транзакций через использование аннотаций Spring.

2. Объявили SessionFactory для Hibernate. SessionFactory — фабрика, генерирующая нам объяекты-сессии. Это похоже на Автомобильный завод, работа которого заключается в производстве автомобилей для людей.Что такое сессия (Session)? Основная функция Сессии — создание, чтение, удаление объектов классов-сущностей (классов помеченных аннотацией Entity).Для SessionFactory требуется источник данных, которым в данном уроке является база данных.SessionFactory требует наличие файла со специфической конфигурацией Hibernate.

Hibernate.cfg.xml

org.hibernate.dialect.MySQL5InnoDBDialect false create Здесь мы указали тип нашей базы данных. Мы используем MySQL dialect. Мы используем особенный диалект — MySQL5InnoDBDialect, потому что мы используем потому что мы используем InnoDB движок для управления памятью.Что такое MySQL InnoDB? InnoDB — MySQL движок, обеспечивающий безопасность транзакций, позволяющий комиты, откат и восстановление для защиты пользовательских данных.Так вот, вернемся обратно к бину SessionFactory, ему необходимо знать, где расположены наши объекты-сущности. Поэтому мы указываем в этом уроке, что они расположены в пакете org.krams.tutorial.

3. Укажем в файле hibernate-context.xml источники данных.

Для эффективного доступа к нашей базе данных мы используем C3p0 пул. Зачем нам необходимо оборачивать наши данные в пул? Соединения JDBC чаще управляются через пул, нежели напрямую через драйвер. Примеры пулов соединений BoneCP, C3P0 и DBCP.

Зачем нужен пул? При разработке программного пул соединений является кешем для открытых соединений. Пулы необходимы для повышения производительности. Открытие и проведение соединения с базой, каждым пользователем, требуют много ресурсов. А при использовании пулов, требуется только извлечь нужней пул из кеша, а не снова устанавливать соединение.Особенности подкдючения к базе данных мы указываем в файле spring.properties.

spring.properties

# database properties app.jdbc.driverClassName=com.mysql.jdbc.Driver app.jdbc.url=jdbc: mysql://localhost/mydatabase app.jdbc.username=root app.jdbc.password= #hibernate properties hibernate.config=/WEB-INF/hibernate.cfg.xml Имя моей базы данных — mydatabase.Как альтернатива мы можем указать эти особенности прям в файле hibernate-context.xml.

Преимущество использования раздельных файлов—конфигураций, в инкапсуляции данных касающихся соединения с базой данных.

Мы завершили наше приложение. Нам удалось создать простое Spring MVC приложение с использованием Hibernate, для доступа к MySQL.

Для доступа к главной странице введите следующий URL: localhost:8080/spring-hibernate-mysql/krams/main/persons

Лучший способ изучения — попытаться создать приложение самому.

Для скачивания проекта перейдите по ссылке: spring-mvc-hibernate-annotations-integration-tutorial.googlecode.com/files/spring-hibernate-mysql.zip

© Habrahabr.ru