Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2013_1 / КСТ / Разработка веб-приложений.pdf
Скачиваний:
160
Добавлен:
23.02.2015
Размер:
2.74 Mб
Скачать

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

Соседние файлы в папке КСТ