Не так давно я начал изучать фреймворк Spring и понял, что количество материала на русском языке ограниченно буквально парой стоящих статей. По быстрому пробежав, я захотел сделать что-то более интересное, но с наскоку взять не удалось. Пришлось погуглить по поводу нескольких вопросов касательно взаимодействия Spring и Hibernate. Неожиданно я наткнулся на блог достаточно интересного разработчика Mark Serrano aka krams. Теперь вместе с вами я хотел бы начать цикл статей-переводов, а так же свое обучение в мире Spring.
Spring — Hibernate: ассоциация один ко многим
Приступим…
В этом уроке мы создадим простое приложение для управления списком лиц с использованием Spring MVC 3. Мы построим простую CRUD (Create Read Update Delete) систему для просмотра, добавления, правки и удаления персон. В качестве слоя для работы с базой данных мы будем использовать Hibernate 3 и базу MySQL, хотя вы можете использовать другую базу данных. В этом уроке предполагается, что вы знакомы с MVC, ORM и SQL (от переводчика: не пугайтесь этих сложных слов, смело читаем, все будет довольно просто)
Hibernate — библиотека для языка программирования Java, предназначенная для решения задач объектно-реляционного отображения (object-relational mapping — ORM). Данная библиотека предоставляет лёгкий в использовании каркас (фреймворк) для отображения объектно-ориентированной модели данных в традиционные реляционные базы данных.
Hibernate решает проблему между хранением объекта в базе данных и его объектно-ориентированном представлении на уровне языка.
База данных MySQL обеспечивает работу наиболее требовательных сетей, электронной комерции и обработки транзакций приложений. Она гарантирует безопасность транзакций, ACID совместимость дает возможность комита, отката, восстановления в случае сбоя, а также возможностей блокировки строк. MySQL обеспечивает простоту использования, масштабируемость и производительность, эти качества сделали MySQL наиболее популярной базой данных с открытым исходным кодом в мире. Некоторые из наиболее посещаемых ресурсов сети использует MySQL, такие как Facebook, Google, Ticketmaster и Ebay.
В начале давайте посмотрим на конечную структуру нашего проекта:
И посмотрим, как будет выглядеть наше приложение:
Мы начнем с определения нашего доменного объекта Person (сущности Person).
Person
Person простой POJO содержащий четыре приватных переменных:
id
firstName
lastName
money
Дополнение от переводчика: POJO (англ. Plain Old Java Object) — «простой Java-объект в старом стиле», простой Java-объект, не унаследованный от какого-то специфического объекта и не реализующий никаких служебных интерфейсов сверх тех, которые нужны для бизнес-модели (Wiki)
Каждой из этих переменных с аннотацией @Column соответствует колонка в базе данных:
ID
FIRST_NAME
LAST_NAME
MONEY
Вам нет необходимости иметь с ними дело, Hibernate возьмет это на себя. Тем не менее вы должны правильно объявить эти имена (например @Column(name = ``ID``)). Вы не объявляете их в базе данных. Помните — вашей базы данных еще не существует.
POJO ссылается на таблицу вашей базы данных, обратите внимание на аннотацию Table, где указано имя таблицы соответствующей данному объекту.
Обратите внимание, аннотация Entity стоит перед аннотацие Table, это говорит Hibernate, что POJO является отображением таблицы базы данных.
Мы будем манипулирувать списком лиц, поэтому создадим для этого сервис:
PersonService
Мы объявили простую CRUD систему со следующими методами:
getAll()
add()
delete()
edit()
В каждом методе мы получаем сессию:
Session session = sessionFactory.getCurrentSession();
Это похоже на соединение с базой данных, так что мы можем делать нашу работу. Объект Session предоставляет множество методов для работы объектами-сущностями. Для этого урока мы используем следующие методы класса Session:
session.createQuery()
session.save()
session.delete()
Мы создали доменный и сервисный слои, давайте создадим Spring контроллер.
MainController
Контроллер объявляет следующие представления:
/persons — для получения всех лиц
/persons/add (GET) — показать форму «Добавить»
/persons/add (POST) — сохраняет новую персону
/persons/delete — удаляет существующую персону
/persons/edit (GET) — показывает форму «Правка»
/persons/edit (POST) — сохраняет «исправленную» персону
Каждое представление вызывает PersonService. Когда PersonService завершает обработку, крнтроллер пересылает запрос на JSP-страницу, которая показывает сообщение с подтверждением. Вот страницы JSP:
personspage.jsp
editpage.jsp
addpage.jsp
editedpage.jsp
addedpage.jsp
deletedpage.jsp
Давайте сконфигурируем наше приложение.
Для работы Spring MVC в web.xml добавим:
web.xml
Обратите внимание на шаблон 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 — фабрика, генерирующая нам объяекты-сессии. Это похоже на Автомобильный завод, работа которого заключается в производстве автомобилей для людей.
Основная функция Сессии — создание, чтение, удаление объектов классов-сущностей(классов помеченных аннотацией Entity).
Для SessionFactory требуется источник данных, которым в данном уроке является база данных.
SessionFactory требует наличие файла со специфической конфигурацией Hibernate.
Hibernate.cfg.xml
Здесь мы указали тип нашей базы данных. Мы используем MySQL dialect. Мы используем особенный диалект — MySQL5InnoDBDialect, потому что мы используем потому что мы используем InnoDB движок для управления памятью.
InnoDB — MySQL движок, обеспечивающий безопасность транзакций, позволяющий комиты, откат и восстановление для защиты пользовательских данных.
Так вот, вернемся обратно к бину SessionFactory, ему необходимо знать, где расположены наши объекты-сущности. Поэтому мы указываем в этом уроке, что они расположены в пакете org.krams.tutorial.
3. Укажем в файле hibernate-context.xml источники данных.
Для эффективного доступа к нашей базе данных мы используем C3p0 пул. Зачем нам необходимо оборачивать наши данные в пул?
Соединения JDBC чаще управляются через пул, нежели напрямую через драйвер. Примеры пулов соединений BoneCP, C3P0 и DBCP.
При разработке программного пул соединений является кешем для открытых соединений. Пулы необходимы для повышения производительности. Открытие и проведение соединения с базой, каждым пользователем, требуют много ресурсов. А при использовании пулов, требуется только извлечь нужное соединение из кеша, а не снова устанавливать соединение.
Особенности подкдючения к базе данных мы указываем в файле spring.properties.
spring.properties
Имя моей базы данных — 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
UPD: Как и обещал — ссылка на github: github.com/sa4ek/spring-hibernate-mysql/tree/master
Spring — Hibernate: ассоциация один ко многим
Приступим…
В этом уроке мы создадим простое приложение для управления списком лиц с использованием Spring MVC 3. Мы построим простую CRUD (Create Read 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.
В начале давайте посмотрим на конечную структуру нашего проекта:
И посмотрим, как будет выглядеть наше приложение:
Мы начнем с определения нашего доменного объекта 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
* <a href="http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/">
* Hibernate Annotations Communit Documentations</a>
*/
@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 содержащий четыре приватных переменных:
id
firstName
lastName
money
Дополнение от переводчика: POJO (англ. Plain Old Java Object) — «простой Java-объект в старом стиле», простой Java-объект, не унаследованный от какого-то специфического объекта и не реализующий никаких служебных интерфейсов сверх тех, которые нужны для бизнес-модели (Wiki)
Каждой из этих переменных с аннотацией @Column соответствует колонка в базе данных:
ID
FIRST_NAME
LAST_NAME
MONEY
Вам нет необходимости иметь с ними дело, 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<person> 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<person> 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"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Persons</h1>
<c:url var="addUrl" value="/krams/main/persons/add" />
<table style="border: 1px solid; width: 500px; text-align:center">
<thead style="background:#fcf">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Money</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<c:forEach items="${persons}" var="person">
<c:url var="editUrl" value="/krams/main/persons/edit?id=${person.id}" />
<c:url var="deleteUrl" value="/krams/main/persons/delete?id=${person.id}" />
<tr>
<td><c:out value="${person.firstName}" /></td>
<td><c:out value="${person.lastName}" /></td>
<td><c:out value="${person.money}" /></td>
<td><a href="${editUrl}">Edit</a></td>
<td><a href="${deleteUrl}">Delete</a></td>
<td><a href="${addUrl}">Add</a></td>
</tr>
</c:forEach>
</tbody>
</table>
<c:if test="${empty persons}">
There are currently no persons in the list. <a href="${addUrl}">Add</a> a person.
</c:if>
</body>
</html>
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"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Edit Person</h1>
<c:url var="saveUrl" value="/krams/main/persons/edit?id=${personAttribute.id}" />
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
<table>
<tr>
<td><form:label path="id">Id:</form:label></td>
<td><form:input path="id" disabled="true"/></td>
</tr>
<tr>
<td><form:label path="firstName">First Name:</form:label></td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td><form:label path="lastName">Last Name</form:label></td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td><form:label path="money">Money</form:label></td>
<td><form:input path="money"/></td>
</tr>
</table>
<input type="submit" value="Save" />
</form:form>
</body>
</html>
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"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Create New Person</h1>
<c:url var="saveUrl" value="/krams/main/persons/add" />
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
<table>
<tr>
<td><form:label path="firstName">First Name:</form:label></td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td><form:label path="lastName">Last Name</form:label></td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td><form:label path="money">Money</form:label></td>
<td><form:input path="money"/></td>
</tr>
</table>
<input type="submit" value="Save" />
</form:form>
</body>
</html>
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"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Persons</h1>
<p>You have edited a person with id ${id} at <%= new java.util.Date() %></p>
<c:url var="mainUrl" value="/krams/main/persons" />
<p>Return to <a href="${mainUrl}">Main List</a></p>
</body>
</html>
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"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Persons</h1>
<p>You have added a new person at <%= new java.util.Date() %></p>
<c:url var="mainUrl" value="/krams/main/persons" />
<p>Return to <a href="${mainUrl}">Main List</a></p>
</body>
</html>
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"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Persons</h1>
<p>You have deleted a person with id ${id} at <%= new java.util.Date() %></p>
<c:url var="mainUrl" value="/krams/main/persons" />
<p>Return to <a href="${mainUrl}">Main List</a></p>
</body>
</html>
Давайте сконфигурируем наше приложение.
Для работы Spring MVC в web.xml добавим:
web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/krams/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Обратите внимание на шаблон URL. При обращении к страницам нашего приложения к имени хоста должна быть добавка в виде:
/krams
В web.xml мы указали в качестве имени сервлета spring. По соглашению мы также должны создать файл spring-servlet.xml.
spring-servlet.xml
<!-- Declare a view resolver -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
Мы также должны создать файл applicationContext.xml.
applicationContext.xml
<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config />
<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
For example @Controller and @Service. Make sure to set the correct base-package-->
<context:component-scan base-package="org.krams.tutorial" />
<!-- Configures the annotation-driven Spring MVC Controller programming model.
Note that, with Spring 3.0, this tag works in Servlet MVC only! -->
<mvc:annotation-driven />
<!-- Load Hibernate related configuration -->
<import resource="hibernate-context.xml" />
Обратите внимание, что в файле applicationContext.xml мы объявили следующий импорт:
<import resource="hibernate-context.xml" />
Он содержит файлы конфигурации Hibernate.
Hibernate-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<context:property-placeholder location="/WEB-INF/spring.properties" />
<!-- Enable annotation style of managing transactions -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Declare the Hibernate SessionFactory for retrieving Hibernate sessions -->
<!-- See http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.html -->
<!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/SessionFactory.html -->
<!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/Session.html -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="${hibernate.config}"
p:packagesToScan="org.krams.tutorial"/>
<!-- Declare a datasource that has pooling capabilities-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
<!-- Declare a transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
</beans>
В этом файле мы инкапсулировали все связанные конфигурации Hibernate и Spring.
Комментарии по конфигурации:
1. Включаем поддержку транзакций через использование аннотаций Spring.
<tx:annotation-driven transaction-manager="transactionManager" />
2. Объявили SessionFactory для Hibernate.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="${hibernate.config}"
p:packagesToScan="org.krams.tutorial"/>
SessionFactory — фабрика, генерирующая нам объяекты-сессии. Это похоже на Автомобильный завод, работа которого заключается в производстве автомобилей для людей.
Что такое сессия (Session)?
Основная функция Сессии — создание, чтение, удаление объектов классов-сущностей(классов помеченных аннотацией Entity).
Для SessionFactory требуется источник данных, которым в данном уроке является база данных.
SessionFactory требует наличие файла со специфической конфигурацией Hibernate.
Hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- We're using MySQL database so the dialect needs to MySQL as well-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- Enable this to see the SQL statements in the logs-->
<property name="show_sql">false</property>
<!-- This will drop our existing database and re-create a new one.
Existing data will be deleted! -->
<property name="hbm2ddl.auto">create</property>
</session-factory>
</hibernate-configuration>
Здесь мы указали тип нашей базы данных. Мы используем MySQL dialect. Мы используем особенный диалект — MySQL5InnoDBDialect, потому что мы используем потому что мы используем InnoDB движок для управления памятью.
Что такое MySQL InnoDB?
InnoDB — MySQL движок, обеспечивающий безопасность транзакций, позволяющий комиты, откат и восстановление для защиты пользовательских данных.
Так вот, вернемся обратно к бину SessionFactory, ему необходимо знать, где расположены наши объекты-сущности. Поэтому мы указываем в этом уроке, что они расположены в пакете org.krams.tutorial.
3. Укажем в файле hibernate-context.xml источники данных.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${app.jdbc.driverClassName}"
p:jdbcUrl="${app.jdbc.url}"
p:user="${app.jdbc.username}"
p:password="${app.jdbc.password}"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
Для эффективного доступа к нашей базе данных мы используем 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
UPD: Как и обещал — ссылка на github: github.com/sa4ek/spring-hibernate-mysql/tree/master