Главная > J2EE > JSTL: Шаблоны для разработки веб-приложений в java. Часть 3

Тема Зацепин
268

Java-разработчик 🧩
422
3 минуты

JSTL: Шаблоны для разработки веб-приложений в java. Часть 3

Теперь перейдем к рассмотрению методик работы с SQL-запросами в jstl. Скажу честно и прямо, этот подход ужасен (полагаю, вы все в курсе, что смешивать логику и визуализацию глупо). Хотя этими тегами я иногда пользуюсь для маленьких, секретненьких (никогда ни кому не показываемых) проектиков.

Добавлено : 4 Nov 2008, 15:52

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

<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

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

CREATETABLE `tm_user` ( `UserID` int(11)NOTNULLAUTO_INCREMENT, `UserName` varchar(100)DEFAULTNULL, `BirthDate` datetimeDEFAULTNULL, `Sex` enum('male','female')DEFAULTNULL, `Comment` text,PRIMARYKEY(`UserID`))ENGINE=InnoDBDEFAULTCHARSET=utf8

Теперь заполним ее:

INSERTINTO `tm_user` (username,birthdate,sex,comment)VALUES('Jim Tapkin','2006.1.1','male','bla-bla-bla'),('Ron Baskerville','2002.7.5','male','be-be-be'),('Lenka Slonova','2009.4.1','female','wow-wow-wow')

Доступ к подключению может быть выполнен либо путем использования через JNDI соединения либо внутри самого jstl-файла нужно объявить объект соединения. Сначала первая методика, создаю для tomcat файл Context.xml и кладу его в папку META-INF/context.xml:

xmlversion="1.0"encoding="UTF-8"?>path="/">name="jdbc/VeloDemoDS"auth="Container"type="javax.sql.DataSource"username="jim"password=""driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://center/obmachine_t?autoReconnect=true&useUnicode=true&characterEncoding=UTF8"/>

Теперь создаю jstl-код. В нем мне нужно будет первым шагом обратиться к JNDI и взять из нее подключение к описанному выше ресурсу. Затем я должен положить этот объект в какую-нибудь область видимости (пусть это будет pageContext). Теперь можно попробовать выполнить запрос, для этого используется два тега: либо query либо update. Полагаю, что вы догадались, когда нужно использовать первый из них, а когда – второй. Что касается текста запроса, то его можно указать (как сделал я) непосредственно как тело тега query, либо значением атрибута sql – без разницы.

<%InitialContextco = newInitialContext(); pageContext.setAttribute("ds_jndi", co.lookup("java:comp/env/jdbc/VeloDemoDS")); %>var="users"dataSource="${ds_jndi}"> select * from tm_user items="${users.rows}"var="row">value="${row.UserName}"/>

Можно сделать и еще короче: здесь я вообще не обращался сам к JNDI а внутри атрибута dataSource тега непосредственно посылающего запросы к базе данных указал ее JNDI-адрес:

var="users"dataSource="jdbc/VeloDemoDS"> select * from tm_user 

Если писать в каждом из query JNDI-адрес не слишком удобно (также не хочется вставлять в начале страницы код извлекающий из Context-а JNDI подключение), то можно сделать так:

dataSource="jdbc/VeloDemoDS"var="velo_simple"/>var="users"dataSource="${velo_simple}"> select * from tm_user 

В результате выполнения запроса у меня появилась переменная users. Однако с ней не все так просто: эта переменная играет роль контейнера для еще множества переменных (точнее полей) содержащих детальные сведения о том, что же там было отобрано с сервера.

rows – представляет собой список вида HashMap. В котором роль ключа играет имя поля, а значением является … значение поля.

rowsByIndex – список массивов. Т.е. каждый элемент списка – массив – представляет отдельную запись в таблице. Однако для доступа к полям нужно указать не имя поля, а его порядковый номер (отсчет начинается с 1).

columnNames – список имен полей. Например, так я распечатаю все имена отобранных полей:

items="${users.columnNames}"var="col">value="${col}"/>

rowCount – число равное количеству отобранных строк.

limitedByMaxRows – логическая переменная, признак того, что количество отобранных записей было ограничено, правда, я так и не понял чем ограничено и зачем … (ни явные команды в тексте запроса вида LIMIT “что-там-надо-ограничить”, ни дополнительные атрибуты для тега query не повлияли на эту переменную).

Последнее действия приведенного выше примера тривиально – нужно организовать цикл по списку rows, затем вывести с помощью c:out значения полей.

У тега query еще есть несколько атрибутов: scope – контекст, куда будет помещена переменная с результатом отбора данных; атрибуты maxRows и startRow служат для ограничения выборки начиная с некоторой записи и числом не более чем.

Запросы без переменных (параметров) слишком редки, чтобы разработчики jstl не предусмотрели несколько механизмов позволяющих вам использовать функциональность PreparedStatement-s. Для этого внутрь тега query кроме текста запроса необходимо поместить еще и элементы param. Каждый из них имеет атрибут value со значением, которое будет подставлено в текст запроса вместо вопросика (будьте внимательны с порядком следования параметров).

var="users"dataSource="${ds_jndi}"> select * from tm_user where UserID between ? and ? value="${param.minID}"/>value="${param.maxID}"/>

Есть подвид тега param – dateParam. Его рекомендуют применять в случаях, когда поле, которое подставляется в текст запроса, имеет тип дата/время:

var="users"dataSource="${ds_jndi}"> select * from tm_user where BirthDate = ? value="${date}"/>

Второй вид запросов – на обновление данных - выполняется с помощью тега update, правила его использования полностью идентичны приведенным выше для query (нужно указать источник данных, текст запроса, можно использовать параметры). Вот только в переменную “var” помещается не выборка записей, а количество записей, которые были подвергнуты модификации.

var="qtyInsterted"dataSource="${ds_jndi}"> insert into `tm_user` (username, birthdate, sex, comment) values ('Jim Tapkin', '2006.1.1', 'male', 'bla-bla-bla'), ('Ron Baskerville', '2002.7.5', 'male', 'be-be-be'), ('Lenka Slonova', '2009.4.1', 'female', 'wow-wow-wow')  count affected records = value="${qtyInsterted}"/>

Есть в jstl и поддержка работы с транзакциями (вот только управления транзакциями не хватает нашему гипотетическому дизайнеру, но раз есть, значит, будем разбираться). Транзакция представлена тегом “transaction”. Ее атрибутами является источник данных, для которого начата транзакция и уровень ее изоляции (isolation).

dataSource="${ds_jndi}"> delete from tm_user  delee adfs adasd 

Обратите внимание на то, что внутри транзакции ни один из тегов query или update не содержит атрибута dataSource (что как раз таки очень логично).

Вторая команда внутри транзакции, очевидно, должна привести к ошибке – будет выброшено исключение. Визуально на экране браузера я вижу текст сообщения об ошибке, и выполнение дальнейшего кода jstl было прервано. Надо бы это поправить. Методы обработки исключений в jstl тривиальны. Если исключение произошло не внутри блока catch, то генерация страницы прерывается и с этим поделать ничего нельзя. В случае если потенциально опасные действия были внутри catch, то при генерации исключения объект, описывающий его, будет помещен внутрь переменной заданной с помощью атрибута var. Затем нужно с помощью if-а проверить чему равна эта переменная, и если она не равна null, то ошибка случилась.

var="e_sql">dataSource="${ds_jndi}"> delete from tm_user  delee adfs adasd test="${e_sql != null}"> Произошла страшная ошибка: value="${e_sql}"/>

Завершу рассказ о работе с базами данных, показав второй способ конфигурирования подключения к СУБД – явно, в коде jstl указать драйвер, учетные данные для доступа:

driver="com.mysql.jdbc.Driver"user="jim"password=""url="jdbc:mysql://center/obmachine_t?autoReconnect=true&useUnicode=true&characterEncoding=UTF8"var="velo_local"scope="application"/>

Теперь у нас есть объект подключения и его можно использовать точь-в-точь как полученный из jndi (в предыдущих примерах) для выполнения запросов на отбор данных или их модификацию.

Работа с форматирование, локализацией

В jstl входит набор тегов, специально предназначенный для создания локализованных приложений: представление дат, чисел в соответствии с определенными национальными настройками, также есть средства для загрузки текстовых ресурсов из Resource

Boundle.

Начнем мы с того, что подключим библиотеку тегов fmt:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

Самый часто используемый в этой библиотеке тег requestEncoding – установить значение кодировки входных данных, чтобы веб-сервер корректно произвел их декодирование:

value="utf-8"/>

Для работы с датами потребуются два тега: formatDate (выполняет преобразование даты в строку) и parseDate (на основании строки конструируется объект Date).

Единственным необходимым атрибутом является value – значение даты, которую необходимо форматировать. Если не указать значение атрибута var, то результат форматирования будет выведен на экран, а не в промежуточную переменную. Атрибут pattern задает строку форматирования.

id="beanNow"class="java.util.Date"/>value="${beanNow}"var="s_now"pattern="yyyy.MMM.dd hh:mm:ss"/>value="${s_now}"/>

А вот пример обратного преобразования: из строки в дату:

pattern="dd.MM.yyyy"value="12.04.2001"/>

Можно выполнить “тонкий тюнинг” процесса форматирования/парсинга с помощью атрибута timeZone и parseLocal (для тега parseDate). Возможно еще поместить операцию форматирования внутрь тега timeZone, тогда форматирование будет выполняться по определенным правилам:

value="Europe/Minsk">value="${beanNow}"pattern="yyyy.MMM.dd hh:mm:ss"/>value="Antarctica/Casey">value="${beanNow}"pattern="yyyy.MMM.dd hh:mm:ss"/>

Изменить локаль можно с помощью тега setLocale, например, так (к сожалению тега применяющего локаль на короткое время, подобно, timeZone нет):

value="ja_JP"/>value="${beanNow}"pattern="yyyy.MMM.dd hh:mm:ss"/>

Для работы с числами используются два тега с очень похожими на своих “братцев”: названиями: formatNumber и parseNumber:

Сначала из числа в строку:

pattern="#,##0.0#"value="234234.23423"/>

А теперь наоборот:

value="234,234.23"pattern="#,##0.0#"/>

Для работы с локализованными сообщениями используется два тега в паре: setBundle и message. Первый из них загружает Resource Bundle, второй – выводит сообщения на основании Resource Bundle (обратите внимание, что я при загрузке ресурсов указал название “упаковки сообщений” и именно на это имя, loc, я ссылался при выводе сообщений). У тега message обязательно должен быть указан атрибут key – это имя свойства хранящего сообщение на нужном языке. Если не указан атрибут var, то сообщение будет выведено на экран тут же, в противном случае сообщение будет помещено в промежуточную переменную.

basename="testi.messages"var="loc"/>bundle="${loc}"key="MSG_1"/>bundle="${loc}"key="MSG_2"var="m_2"/>value="${m_2}"/>

Вот вкратце и все теги, которые входят в состав jstl. Однако я продолжаю свой рассказ, т.к. необходимо рассказать еще и об функциях которые можно использовать при записи jstl-выражений. Во всех примерах выше я использовал синтаксис ${что-то-там}. В основном я обращался к переменным, несколько раз делал простые проверки равна переменная чему-то или не равна, проверял может значение переменной null (хотя в примерах и не было это показано, но возможно использовать в EL-выражении и тернарный оператор). Но это еще не все: если я подключу в заголовке файла jsp еще одну библиотеку, то возможности EL становятся шире (хотел, было написать в разы, но все же заменю нейтральным “становятся больше”).

<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

Начнем разбирать функции по порядку. Первая из них “fn:contains” (имена всех функций должны предваряться префиксом “fn:” – именно его мы подключили в начале jsp-файла). Contains служит для проверки того, что внутри строки содержится другая подстрока.

value="boy goes to school"var="boy"/>test="${fn:contains(boy, 'goes')}">

He goes to School. Realy.

Если проверить такое условие нужно без учета регистра символов, то используйте функцию containsIgnoreCase:

test="${fn:containsIgnoreCase(boy, 'GOES')}">

He goes to School. Realy.

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

test="${fn:startsWith(boy, 'boy')}">

Starts from BOY

test="${fn:endsWith(boy, 'school')}">

Ends with SCHOOL

Для экранирования спец.символов некоторой строки перед выводом можно использовать функцию escapeXml (не забывайте, что в старом-добром c:out есть атрибут обеспечивающий сходную функциональность):

value="boy goes © to school"var="boy"/>value="${boy}"/>value="${fn:escapeXml(boy)}"/>value="${boy}"escapeXml="true"/>

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

value="boy goes to school"var="boy"/>value="${fn:indexOf(boy, 'to' )}"/>

Теперь вырежем из исходной строки ее часть, начиная с той позиции где началось слово “to”:

value="boy goes to school"var="boy"/>value="${fn:substring(boy, fn:indexOf(boy, 'to' ), 100)}"/>

Как видите, мы указываем, откуда вырезать фрагмент, третьим параметром функции выступает число – до какого символа нужно вырезать фрагмент. В следующем примере я вырезаю только два символа из строки:

value="${fn:substring(boy, fn:indexOf(boy, 'to' ), fn:indexOf(boy, 'to' )+2)}"/>

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

value="${fn:substring(boy, fn:indexOf(boy, 'to' ), -1)}"/>

Сразу же пока я не забыл, расскажу, как вычислить длину строки. Для этого приходится функция length. Надо сказать что length – универсальная функция и умеет считать длину не только строки, но произвольной коллекции:

var="users"dataSource="jdbc/VeloDemoDS"> select * from tm_user value="boy goes to school"var="boy"/>

Вот пример длины строки:

value="${fn:length(boy)}"/>

А так считаем длину списка записей (сколько записей было отобрано из таблицы)

value="${fn:length(users.rows)}"/>

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

value="boy goes to school"var="boy"/> After:value="${fn:substringAfter(boy, 'to')}"/> Before:value="${fn:substringBefore(boy, 'to')}"/>

Преобразование в верхний и нижний регистр может быть выполнено с помощью функций toLowerCase и toUpperCase:

value="boy goes to school"var="boy"/>

Делаем маленький регистр:

value="${fn:toLowerCase(boy)}"/>

А теперь большой:

value="${fn:toUpperCase(boy)}"/>

Для удаления символов пробелов размещенных по обоим краям строки используйте функцию trim:

value=" boy goes to school "var="boy"/>

value="${fn:trim(boy)}"/>

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

value="boy goes to school"var="boy"/>value="${fn:replace(boy, 'school', 'bar')}"/>

Последние функции, про которые я сегодня расскажу: split и join. Они занимаются сходными задачами – преобразованием строки в массив подстрок (split) и обратной операцией склейкой массива строк в одну большую строку. В первом и втором случае нужно указать параметр “разделитель”:

value="1,2,3,4"var="str_digits"/>value="${fn:split(str_digits, ',')}"var="arr_digits"/>value="${fn:join(arr_digits, '!')}"var="str2_digits"/>

Массив подстрок – он, правда, будет неудобочитаемым:

value="${fn:length(arr_digits)}"/>

А теперь обратное преобразование:

value="${str2_digits}"/>

Источник — http://black-zorro.com/mediawiki/JSTL:_Шаблоны_для_разработки_веб-приложений_в_java._Часть_3

Теги: j2eejspjstlpatternstaglib

Еще от автора

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Совмещение изображений

1 Введение 2 Правила визуализации и пример 3 Совмещение изображений в оперативной памяти 4 Постепенное исчезновение изображения 5 Ссылки и дополнительная информация

Еще по теме

Применение WeakHashmap для списков слушателей

В статье от 11мая 1999 года Reference Objects были описаны основные идеи применения ссылочных объектов, но не приводилось детального описания. Данная статья позволит вам получить больше сведений, касающихся данной темы. В основном ссылочные объекты применяются для косвенных ссылок на память необходимую объектам. Ссылочные объекты хранятся в очереди (класс ReferenceQueue), в которой отслеживается доступность ссылочных объектов. Исходя из типа ссылочного объекта, сборщик мусора может освобождать память даже тогда, когда обычные ссылки не могут быть освобождены.

Заставки в Mustang

Согласно определению, данному в Wikipedia, заставка - это компьютерный термин, обозначающий рисунок, появляющийся во время загрузки программы или операционной системы. Заставка для пользователя является визуальным отображением инициализации программы. До выхода версии Java SE 6 (кодовое название Mustang) единственной возможностью применения заставки было создание окна, во время запуска метода main, и размещение в нем картинки. Хотя данный способ и работал, но он требовал полной инициализации исполняемой Java среды до появления окна заставки. При инициализации загружались библиотеки AWT и Swing, таким образом, появление заставки задерживалось. В Mustang появился новый аргумент командной строки, значительно облегчающий использование заставок. Этот способ позволяет выводить заставку значительно быстрее до запуска исполняемой Java среды. Окончательное добавление данной функциональности находится на рассмотрении в JCP.

Использование потоков

1 Введение 2 Работа с выражениями типа Boolean 3 Класс JoptionPane 4 Приложение-счетчик 5 Ссылки

Перехват необрабатываемых исключений

В статье от 16 марта 2004 года Best Practices in Exception Handling были описаны приемы обработки исключений. В данной статье вы изучите новый способ обработки исключений при помощи класса UncaughtExceptionHandler добавленного в J2SE 5.0.

Использование класса LinkedHashMap

1 Введение 2 Сортировка хэш-таблицы 3 Копирование таблицы 4 Сохранение порядка доступа к элементам 5 Ссылки

Сказ про кодировки и java

С кодировками в java плохо. Т.е., наоборот, все идеально хорошо: внутреннее представление строк – Utf16-BE (и поддержка Unicode была с самых первых дней). Все возможные функции умеют преобразовывать строку из маленького регистра в большой, проверять является ли данный символ буквой или цифрой, выполнять поиск в строке (в том числе с регулярными выражениями) и прочее и прочее. Для этих операций не нужно использовать какие-то посторонние библиотеки вроде привычных для php mbstring или iconv. Как говорится, поддержка многоязычных тестов “есть в коробке”. Так откуда берутся проблемы? Проблемы возникают, как только строки текста пытаются “выбраться” из jvm (операции вывода текста различным потребителям) или наоборот пытаются в эту самую jvm “залезть” (операция чтения данных от некоторого поставщика).