- •Обозначения и сокращения
- •введение
- •1. Установка и настройка инструментальных средств
- •1.1. Установка и подготовка к работе операционной системы
- •1.2. Установка программного обеспечения
- •1.3. Создание таблиц в базе данных
- •2. Основы Java EE 6
- •2.1. Распределенные многоуровневые приложения
- •2.2. Контейнеры Java EE
- •2.3. Сервер GlassFish v3
- •2.4. Структура приложения
- •2.5. Конфигурирование приложений
- •2.6. Задание ссылок на ресурсы
- •4. Введение в компоненты Facelets
- •4.1. Веб-страницы
- •4.2. Разработка простого приложения Facelets
- •4.3. Использование шаблонов
- •5. Унифицированный язык записи выражений
- •6.1. Добавление компонент библиотеки HTML на страницу
- •6.2. Использование компонент для таблиц баз данных
- •6.3. Использование тегов библиотеки Core
- •7. Использование конвертеров, слушателей и проверок
- •7.1. Использование стандартных преобразователей
- •7.2. Регистрация слушателей для компонентов
- •8. Внешние объекты (JavaBeans)
- •8.1. Создание класса внешних объектов
- •8.2. Описание свойств бинов
- •8.3. Написание методов внешних бинов
- •8.4. Проверка бинами
- •9.1. Файл конфигурации ресурсов приложения
- •9.2. Упорядочение ресурсов конфигурации приложения
- •9.3. Конфигурирование состояния проекта
- •9.4. Выбор конфигурации бина
- •9.5. Регистрация сообщений об ошибках как пакет ресурса
- •9.7. Конфигурирование правил навигации (Navigation Rules)
- •9.8. Основные требования приложения JavaServer Faces
- •10. Технология Java Servlet
- •11. Введение в Java Persistence API
- •11.1. Требования к классам сущностей
- •11.3. Внедряемые классы в сущностях
- •11.4. Наследование сущностей
- •11.5. Стратегии наследования сущностей с отображением
- •11.6. Управление сущностями
- •11.7. Запросы сущностей
- •12. Примеры хранимых сущностей
- •12.1. Приложение order
- •12.2. Пример получения схемы отношений на основе таблиц БД
- •13.1. Терминология языка запросов
- •13.3. Упрощенный синтаксис языка запросов
- •13.4. Примеры запросов
- •13.5. Запросы с навигацией связанных сущностей
- •13.6. Запросы с другими условными выражениями
- •13.7. Изменение и удаление группы сущностей
- •13.8. Полный синтаксис языка запросов
- •14. Язык запросов Criteria API
- •14.3. Корни запроса
- •14.4. Использование объединения в запросе
- •14.5. Навигация путей в запросах
- •14.6. Ограничения на результаты запроса
- •14.7. Управление результатами запросов
- •14.8. Исполнение запросов
- •15. Связывание ресурсов
- •15.1. Ресурсы и служба имен JNDI
- •15.2. Объекты DataSource и пулы соединений (Connection Pools)
- •15.3. Внедрение ресурсов
- •15.4. Адаптеры ресурсов
- •15.5. Аннотации метаданных
- •16. Безопасность веб-приложений
- •16.1. Краткий обзор
- •16.2. Механизмы обеспечения безопасности
- •16.3. Безопасность сервера предприятия
- •16.4. Использование защищенного соединения SSL
- •18. Пример приложения
- •18.1. Создание проекта веб-приложения
- •18.3.Структура приложения JavaEE 6
- •18.4. Программирование вида для объектов
- •18.5. Дизайн главной страницы
- •18.6. Страница просмотра записей таблицы городов
- •18.7. Страница добавления записей о городах
- •18.8. Страница редактирования записей о городах
- •18.9. Страница удаления записей о городах
- •19. Обработка связей внешних ключей
- •19.1. Разработка класса для вида сущности
- •19.2. Доработка вида для городов
- •19.3. Разработка обзорной страницы
- •19.5. Страница для редактирования записей с внешними ключами
- •20. Дополнительные функции
- •20.1. Сортировка записей таблицы
- •20.2. Контроль за удалением связанных записей
- •20.3. Контроль ввода наименований
- •20.4. Запросы к БД на языке Java Persistence Query Language
- •20.5. Управление страницами при просмотре таблицы
- •20.6. Создание и просмотр отчетов
- •20.7. Использование шаблонов и стилей
- •20.8. Защита приложения паролем
- •Заключение
- •Библиографический список
20. Дополнительные функции
20.1. Сортировка записей таблицы
Правила формирования запросов на языке Critera API позволяют задать сорти-
ровку извлекаемых объектов в желаемом порядке [2, п. 14.7]. Так будет выглядеть
метод findAll фасада городов для сортировки объектов по названию городов:
public List<Cities> findAll() {
CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cq = cb.createQuery(); Root<Cities> cit = cq.from(Cities.class); cq.select(cit); cq.orderBy(cb.asc(cit.get("name")));
return em.createQuery(cq).getResultList();
}
Упражнение
Объясните алгоритм формирования запроса на языке Critera API.
20.2.Контроль за удалением связанных записей
При удалении записей о городе, на который есть ссылки внешних ключей из записей персон, СУБД PostgreSQL должна обнаружить нарушение целостности и заблокировать такую операцию, так как это противоречит описанию ограничения внешнего ключа при создании таблицы персон (см. упражнение 1 к п. 1.3 данной работы:
CONSTRAINT "FK_city" FOREIGN KEY (city_id) REFERENCES cities (id_city) MATCH
SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION). Для программного кон-
троля попытки удалить город можно воспользоваться свойством personsList объекта city, получаемого параметром метода-обработчика командной ссылки «Удалить» в окне просмотра городов. Это свойство содержит список персон, ссылающихся на уда-
ляемый город. Удалять можно только город с пустым списком ссылающихся персон. Обработчик может выглядеть так:
public String delCity(Cities city) { this.city = city;
if (city.getPersonsList().size() > 0) { // Нельзя удалять FacesContext context = FacesContext.getCurrentInstance(); context.addMessage("",new FacesMessage("***Ошибка
удаления*** На город ссылаются записи персон!")); return "citiesPage";
}
else { // Можно удалять return "city_del";}
}
244
Упражнения
1.Найти информацию о классе FacesContext и объяснить получаемое клиентом сообщение.
2.Добавить в сообщение об ошибке список фамилий персон, ссылающихся на город.
3.Доработать соответствующий обработчик для удаления персон.
20.3. Контроль ввода наименований
Для контроля ввода текстов можно использовать регулярные выражения, за-
дающие правильный шаблон. Пусть необходимо проверить ввод названия города.
Правильными названиями считать начинающиеся с заглавной русской или латин-
ской буквы и не имеющие ничего, кроме букв. Шаблон такого условия будет "[А-ЯA-Z] [а-яa-z]*", а откорректированный обработчик кнопки «Добавить» будет выглядеть так:
public String saveCity() {
Pattern pattern = Pattern.compile("[А-ЯA-Z][а-яa-z]*"); Matcher matcher = pattern.matcher(city.getName());
if (matcher.matches()) { this.citiesFacade.create(city); return "citiesPage";
}else {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage("",new FacesMessage("***Ошибка в названии города***"));
return "cityAddPage";
}
}
Поскольку подобный контроль надо выполнять для всех собственных имен, целесообразно написать отдельный метод для верификации данных, подключаемый дизайнером в теги ввода на своих страницах. Добавим метод validateName в класс вида для персон:
public void validateName(FacesContext context, UIComponent toValidate, Object value) {
String message = “***Ошибка в названии***";
String nameObj = (String) value;
Pattern pattern = Pattern.compile("[А-ЯA-Z][а-яa-z]*"); Matcher matcher = pattern.matcher(nameObj);
if (!matcher.matches()) { ((UIInput)toValidate).setValid(false); message = message +
((UIInput)toValidate).getSubmittedValue(); context = FacesContext.getCurrentInstance(); context.addMessage(toValidate.getClientId(context),
new FacesMessage(message));
}
}
245
На странице добавления персоны изменим теги ввода фамилии, имени и отче-
ства, как в следующем примере:
<tr>
<td><h:outputLabel value="Фамилия:"/> </td> <td><h:inputText value="#{personsView.person.family}"
validator="#{personsView.validateName}" /> </td>
</tr>
Упражнение
Доработайте шаблон для использования символа «-» (дефис) внутри названия.
20.4.Запросы к БД на языке Java Persistence Query Language
Для получения данных из БД можно использовать два способа: Java Persistence Query Language (JPQL) API и Criteria API. По умолчанию в методах фасада для доступа к БД используется Criteria API. Вместе с тем язык JPQL может быть более удобным для владеющих языком SQL. Например, метод findAll в фасаде городов для поиска всех записей, вид которого в Criteria API:
public List<Cities> findAll() {
CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cq = cb.createQuery(); Root<Cities> cit = cq.from(Cities.class); cq.select(cit); cq.orderBy(cb.asc(cit.get("name")));
return em.createQuery(cq).getResultList();
}
будет выглядеть следующим образом:
public List<Cities> findAll() {
return em.createQuery("SELECT cit FROM Cities cit ORDER BY cit.name") .getResultList();
}
Метод findRange будет выглядеть так:
public List<Cities> findRange(int[] range) {
Query q = em.createQuery(“SELECT cit FROM Cities cit”); q.setMaxResults(range[1] - range[0]); q.setFirstResult(range[0]);
return q.getResultList();
}
Метод count:
public int count() {
Query q = em.createQuery(“SELECT count(cit) FROM Cities cit”); return ((Long) q.getSingleResult()).intValue();
}
246
Упражнение
Проанализируйте тексты методов доступа к БД с использованием материала учебного пособия The Java EE 5 Tutorial [2, разделы 13 и 14].
20.5.Управление страницами при просмотре таблицы
20.5.1. Сегментация страниц
При просмотре больших объемов данных целесообразно организовать постраничную сегментацию материала с возможностью выбора нужной клиенту страницы.
Поскольку такая потребность возникает для всех таблиц, то предварительно создадим
описание универсального абстрактного класса PaginationHelper, поместив его в от-
дельный пакет utils (создав новый пакет utils в узле Source Packages).
package utils;
import javax.faces.model.DataModel;
public abstract class PaginationHelper {
private int pageSize; private int page;
public PaginationHelper(int pageSize) { this.pageSize = pageSize; this.page = 0;
}
public abstract int getItemsCount();
public abstract DataModel createPageDataModel();
public int getPageFirstItem() { return page*pageSize;
}
public int getPageLastItem() {
int i = getPageFirstItem() + pageSize -1; int count = getItemsCount() - 1;
if (i > count) { i = count;
}
if (i < 0) { i = 0;
}
return i;
}
public boolean isHasNextPage() {
247
return (page+1)*pageSize+1 <= getItemsCount();
}
public void nextPage() { if (isHasNextPage()) {
page++;
}
}
public boolean isHasPreviousPage() { return page > 0;
}
public void previousPage() { if (isHasPreviousPage()) {
page--;
}
}
public int getPageSize() { return pageSize;
}
}
В классе предусмотрена необходимость в перекрытии двух методов:
public abstract int getItemsCount();
public abstract DataModel createPageDataModel();
соответственно для вычисления числа строк в БД и запроса необходимой порции записей.
Упражнения
1.Объясните назначение полей pageSize и page, где и как они используются.
2.Почему класс можно использовать с любыми таблицами БД?
20.5.2.Изменения в классе вида городов
Всвязи с появлением новой функциональности коренной переделке подлежит
файл CitiesView.java вида для городов. Изменения вызваны необходимостью просле-
живания текущего объекта города в порции на просматриваемой клиентом странице.
Дело в том, что протокол http позволяет возвращать не ссылку на объект (город) в командах модификации и удаления, а его порядковый номер в таблице. Поэтому в
технологии JSF предусмотрен класс модели данных, выполняющий сопоставление
номера выбранного на экране объекта и самого объекта в текущей порции.
1. Удалим описание поля city (заменив его полем current для ссылки на текущий город, что будет точнее отображать смысл происходящего) и добавим описание полей
248
для ссылок на менеджер страниц, на требуемую в этой ситуации модель данных для получения порции записей, а также на размер порции и текущий город:
private PaginationHelper pagination; private DataModel items = null; private int pageSize = 15;
private Cities current;
2. Добавим метод getPagination, перекрывающий абстрактные методы getItemsCount и createPageDataModel абстрактного класса PaginationHelper.
public PaginationHelper getPagination() { if (pagination == null) {
pagination = new PaginationHelper(pageSize) {
@Override
public int getItemsCount() { return citiesFacade.count();
}
@Override
public DataModel createPageDataModel() {
return new ListDataModel(citiesFacade.findRange(new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()}));
}
};
}
return pagination;
}
Метод создает и возвращает экземпляр менеджера страниц pagination с раз-
мером страницы pageSize строк таблицы БД и перекрывает абстрактные мето-
ды getItemsCount и createPageDataModel базового класса PaginationHelper. Метод getItemsCount с использованием фасада городов запрашивает число городов в БД, а метод createPageDataModel создает модель данных для нужного диапазона записей из БД.
3. Добавим описание метода getItems получения записей модели данных (свойство items объекта вида) для отображения на затребованной странице, метода recreateModel для сброса состава записей и его последующего обновления при
следующем запросе, методов next для организации просмотра следующей страницы
данных и previous для предыдущей.
public DataModel getItems() { if (items == null) {
items = getPagination().createPageDataModel();
}
return items;
}
private void recreateModel() { items = null;
}
249
public String next() { getPagination().nextPage(); recreateModel();
return "citiesPage";
}
public String previous() { getPagination().previousPage(); recreateModel();
return "citiesPage";
}
4. Добавим метод для получения свойства selected со значением текущего выбранного клиентом города в таблице просмотра:
public Cities getSelected() { if (current == null) { current = new Cities();
}
return current;
}
Задание значения переменной current выполняется методом getRowData моде-
ли данных items. Следующий фрагмент используется во всех методах обработчиках
команд замены и удаления записей (смотрите полный текст класса): current = (Cities) getItems().getRowData();
Полный текст модифицированного класса для вида городов приведён ниже.
Новый файл CitiesView.java
package Views;
import Entities.Cities; import Facades.CitiesFacade; import java.util.ArrayList; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ejb.EJB;
import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped; import javax.faces.context.FacesContext; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import javax.faces.model.SelectItem; import utils.PaginationHelper;
/**
*
* @author cyx */
250
@ManagedBean(name = “citiesView”) @SessionScoped
public class CitiesView {
@EJB
private CitiesFacade citiesFacade; private ArrayList citiesSelector; private PaginationHelper pagination; private DataModel items = null; private int pageSize = 15;
private Cities current;
/** Creates a new instance of CitiesView */ public CitiesView() {
}
public Cities getCity() { return this.current;
}
public Cities getSelected() { if (current == null) { current = new Cities();
}
return current;
}
public int getNumberOfCities() { return citiesFacade.findAll().size();
}
public List<Cities> getAllOfCities() { return citiesFacade.findAll();
}
public String saveCity() {
Pattern pattern = Pattern.compile(“[А-ЯA-Z][а-яa-z]*”); Matcher matcher = pattern.matcher(current.getName()); if (matcher.matches()) {
this.citiesFacade.create(current);
recreateModel(); return “citiesPage”;
} else {
FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(“”, new FacesMessage(“***Ошибка в назва-
нии города***”));
return “cityAddPage”;
}
}
public String modCity() {
current = (Cities) getItems().getRowData();
251
return “city_mod”;
}
public String mod_yes_City() { this.citiesFacade.edit(current); recreateModel();
return “citiesPage”;
}
public String delCity() {
current = (Cities) getItems().getRowData(); if (current.getPersonsList().size() > 0) {
FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(“”, new FacesMessage(“***Ошибка
удаления*** На город ссылаются записи персон!”)); return “citiesPage”;
} else {
return “city_del”;
}
}
public String del_yes_City() { this.citiesFacade.remove(current); recreateModel();
return “citiesPage”;
}
public ArrayList getCitiesSelector() { citiesSelector = new ArrayList();
List<Cities> allCities = this.citiesFacade.findAll(); for (int i = 0; i < (allCities.size()); i++) {
Cities cit = allCities.get(i); citiesSelector.add(new SelectItem(cit.getIdCity(),
cit.getName(), cit.getName()));
}
return citiesSelector;
}
public PaginationHelper getPagination() { if (pagination == null) {
pagination = new PaginationHelper(pageSize) {
@Override
public int getItemsCount() { return citiesFacade.count();
}
@Override
public DataModel createPageDataModel() {
return new ListDataModel(citiesFacade.findRange(new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()}));
}
252
};
}
return pagination;
}
public DataModel getItems() { if (items == null) {
items = getPagination().createPageDataModel();
}
return items;
}
private void recreateModel() { items = null;
}
public String next() { getPagination().nextPage(); recreateModel();
return “citiesPage”;
}
public String previous() { getPagination().previousPage(); recreateModel();
return “citiesPage”;
}
}
Упражнения
1.Объясните назначение методов getItemsCount и createPageDataModel,
где и как они используются.
2.Что за класс ListDataModel используется в методе создания модели?
3.Объясните назначение метода getItems, где и как он используется.
4.КакобработчикузнаетовыбраннойклиентомзаписиБДнастраницепросмотра?
20.5.3. Сегментация записей при просмотре
Так как ранее используемый источник данных для просмотра формировал все
записи из таблицы городов, то заменим в файле citiesPage.xhtml источник данных в
теге dataTable в атрибуте value на «#{citiesView.items}», выбирающий нужную порцию:
<h:dataTable value="#{citiesView.items}" var="city" border="1" >
Удалим ссылку city из параметров вызова обработчиков замены и удаления за-
писей. Для перехода между страницами поместим две командные ссылки при помощи
обработчиков citiesView.previous и citiesView.next. Модифицированный текст файла citiesPage.xhtml выглядит следующим образом.
253
Новый файл citiesPage.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!--
To change this template, choose Tools | Templates and open the template in the editor.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Просмотр таблицы городов</title> </head>
<body>
<f:view>
<table border="1" width="100%"> <tr>
<td><h:outputLabel value="Города. Всего записей "/> <h:outputText value="#{citiesView.numberOfCities}"/>
</td>
</tr>
<tr>
<td>
<h:form>
<!-- h:dataTable value="#
{ citiesView.allOfCities }" var="city" border="1" --> <h:dataTable value="#{citiesView.items}" var="city"
border="1" >
<h:column>
<f:facet name="header"> <h:outputText value="Ключ"/>
</f:facet>
<h:outputText value="#{city.idCity}"/> </h:column>
<h:column>
<f:facet name="header"> <h:outputText value="Название"/>
</f:facet>
<h:outputText value="#{city.name}"/> </h:column>
<h:column>
<f:facet name="header"> <h:outputText value="Население"/>
</f:facet>
<h:outputText value="#{city.population}"/> </h:column>
<h:column>
<f:facet name="header">
254
<h:outputText value="Используется"/> </f:facet>
<h:outputText
value="#{city.personsList.size()}"/>
</h:column>
<h:column>
<f:facet name="header"> <h:outputText value="Операции"/>
</f:facet>
<h:commandLink action="#{citiesView.modCity()}"> <h:outputText value="Заменить" />
</h:commandLink>
<h:commandLink action="#{citiesView.delCity()}"> <h:outputText value="Удалить" />
</h:commandLink>
</h:column>
</h:dataTable>
</h:form>
</td>
</tr>
<tr>
<td>
<h:form>
<h:commandLink action="#{citiesView.previous}"> <h:outputText value="Назад" />
</h:commandLink>
&tab;
<h:commandLink action="#{citiesView.next}"> <h:outputText value="Вперед" />
</h:commandLink>
</h:form>
</td>
</tr>
<tr>
<td>
<h:form>
<h:commandLink action="cityAddPage"> <h:outputText value="Добавить" />
</h:commandLink>
&tab;
<h:commandLink action="index"> <h:outputText value="На главную" />
</h:commandLink>
</h:form>
</td>
</tr>
</table>
</f:view>
</body>
</html>
255
Упражнение
Объясните назначение свойства city, где и как оно используется.
20.5.4. Изменение и удаление записей
Последние изменения необходимо проделать с файлами страниц внесения из-
менений и удаления записи. Изменения вызваны появлением нового свойства в клас-
се вида городов — citiesView.selected, содержащего город, выбранный для редактиро-
вания или удаления на странице просмотра citiesPage.xhtml.
Файл city_mod.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!--
To change this template, choose Tools | Templates and open the template in the editor.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Изменение атрибутов города</title> </head>
<body>
<f:view>
<table border="1" width="100%"> <tr>
<td>
<h:form>
<table>
<tr>
<td> <h:outputLabel value="Ключ:#{citiesView.selected.idCity}"/> </td>
<td> <h:inputHidden value="#{citiesView.selected.idCity}"
/> </td>
</tr>
<tr>
<td><h:outputLabel value="Название:"/> </td> <td><h:inputText
value="#{citiesView.selected.name}"/> </td> </tr>
<tr>
<td><h:outputLabel value="Население:"/></td>
256
<td><h:inputText
value="#{citiesView.selected.population}"/></td>
</tr>
<tr>
<td>
<h:commandButton action="#{citiesView.mod_yes_City}"
value="Заменить" /> <h:commandButton action="citiesPage" value="Отменить" />
</td>
</tr>
</table>
</h:form>
</td>
</tr>
</table>
</f:view>
</body>
</html>
Файл city_del.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!--
To change this template, choose Tools | Templates and open the template in the editor.
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Удаление записи</title> </head>
<body>
<f:view>
<table border="1" width="100%"> <tr>
<td>
<h:form>
<table>
<tr>
<td> <h:outputLabel value="Ключ: #{citiesView.selected.idCity}"/>
257