« JSTL: Шаблоны для разработки веб-приложений в java. Часть 2 | Hibernate: отображая иерархии классов » |
JSTL: Шаблоны для разработки веб-приложений в java. Часть 3
Теперь перейдем к рассмотрению методик работы с SQL-запросами в jstl. Скажу честно и прямо, этот подход ужасен (полагаю, вы все в курсе, что смешивать логику и визуализацию глупо). Хотя этими тегами я иногда пользуюсь для маленьких, секретненьких (никогда ни кому не показываемых) проектиков.Для работы всех показанных далее тегов необходимо подключить к jsp-файлу следующую библиотеку тегов:
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
CREATE TABLE `tm_user` (
`UserID` int(11) NOT NULL AUTO_INCREMENT,
`UserName` varchar(100) DEFAULT NULL,
`BirthDate` datetime DEFAULT NULL,
`Sex` enum('male','female') DEFAULT NULL,
`Comment` text,
PRIMARY KEY (`UserID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
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')
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/">
<Resource 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"
/>
</Context>
Теперь можно попробовать выполнить запрос, для этого используется два тега: либо query либо update. Полагаю, что вы догадались, когда нужно использовать первый из них, а когда – второй. Что касается текста запроса, то его можно указать (как сделал я) непосредственно как тело тега query, либо значением атрибута sql – без разницы.
<%
InitialContext co = new InitialContext();
pageContext.setAttribute("ds_jndi", co.lookup("java:comp/env/jdbc/VeloDemoDS"));
%>
<body>
<sql:query var="users" dataSource="${ds_jndi}">
select * from tm_user
</sql:query>
<c:forEach items="${users.rows}" var="row">
<c:out value="${row.UserName}"/>
</c:forEach>
<sql:query var="users" dataSource="jdbc/VeloDemoDS">
select * from tm_user
</sql:query>
<sql:setDataSource dataSource="jdbc/VeloDemoDS" var="velo_simple"/>
<sql:query var="users" dataSource="${velo_simple}">
select * from tm_user
</sql:query>
rows – представляет собой список вида HashMap
rowsByIndex – список массивов. Т.е. каждый элемент списка – массив – представляет отдельную запись в таблице. Однако для доступа к полям нужно указать не имя поля, а его порядковый номер (отсчет начинается с 1).
columnNames – список имен полей.
Например, так я распечатаю все имена отобранных полей:
<c:forEach items="${users.columnNames}" var="col">
<c:out value="${col}"/>
</c:forEach>
limitedByMaxRows – логическая переменная, признак того, что количество отобранных записей было ограничено, правда, я так и не понял чем ограничено и зачем … (ни явные команды в тексте запроса вида LIMIT “что-там-надо-ограничить”, ни дополнительные атрибуты для тега query не повлияли на эту переменную).
Последнее действия приведенного выше примера тривиально – нужно организовать цикл по списку rows, затем вывести с помощью c:out значения полей.
У тега query еще есть несколько атрибутов: scope – контекст, куда будет помещена переменная с результатом отбора данных; атрибуты maxRows и startRow служат для ограничения выборки начиная с некоторой записи и числом не более чем.
Запросы без переменных (параметров) слишком редки, чтобы разработчики jstl не предусмотрели несколько механизмов позволяющих вам использовать функциональность PreparedStatement-s. Для этого внутрь тега query кроме текста запроса необходимо поместить еще и элементы param. Каждый из них имеет атрибут value со значением, которое будет подставлено в текст запроса вместо вопросика (будьте внимательны с порядком следования параметров).
<sql:query var="users" dataSource="${ds_jndi}">
select * from tm_user where UserID between ? and ?
<sql:param value="${param.minID}" />
<sql:param value="${param.maxID}" />
</sql:query>
<sql:query var="users" dataSource="${ds_jndi}">
select * from tm_user where BirthDate = ?
<sql:dateParam value="${date}"/>
</sql:query>
<sql:update 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')
</sql:update>
count affected records = <c:out value="${qtyInsterted}" />
<sql:transaction dataSource="${ds_jndi}">
<sql:update>
delete from tm_user
</sql:update>
<sql:update>
delee adfs adasd
</sql:update>
</sql:transaction>
Вторая команда внутри транзакции, очевидно, должна привести к ошибке – будет выброшено исключение. Визуально на экране браузера я вижу текст сообщения об ошибке, и выполнение дальнейшего кода jstl было прервано. Надо бы это поправить. Методы обработки исключений в jstl тривиальны. Если исключение произошло не внутри блока catch, то генерация страницы прерывается и с этим поделать ничего нельзя. В случае если потенциально опасные действия были внутри catch, то при генерации исключения объект, описывающий его, будет помещен внутрь переменной заданной с помощью атрибута var. Затем нужно с помощью if-а проверить чему равна эта переменная, и если она не равна null, то ошибка случилась.
<c:catch var="e_sql">
<sql:transaction dataSource="${ds_jndi}">
<sql:update>
delete from tm_user
</sql:update>
<sql:update>
delee adfs adasd
</sql:update>
</sql:transaction>
</c:catch>
<c:if test="${e_sql != null}">
Произошла страшная ошибка: <c:out value="${e_sql}"/>
</c:if>
<sql:setDataSource 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"
/>
Работа с форматирование, локализацией
В jstl входит набор тегов, специально предназначенный для создания локализованных приложений: представление дат, чисел в соответствии с определенными национальными настройками, также есть средства для загрузки текстовых ресурсов из Resource Boundle.
Начнем мы с того, что подключим библиотеку тегов fmt:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:requestEncoding value="utf-8" />
Единственным необходимым атрибутом является value – значение даты, которую необходимо форматировать. Если не указать значение атрибута var, то результат форматирования будет выведен на экран, а не в промежуточную переменную. Атрибут pattern задает строку форматирования.
<jsp:useBean id="beanNow" class="java.util.Date" />
<fmt:formatDate value="${beanNow}" var="s_now" pattern="yyyy.MMM.dd hh:mm:ss" />
<c:out value="${s_now}" />
<fmt:parseDate pattern="dd.MM.yyyy" value="12.04.2001" />
<fmt:timeZone value="Europe/Minsk">
<fmt:formatDate value="${beanNow}" pattern="yyyy.MMM.dd hh:mm:ss" />
</fmt:timeZone>
<fmt:timeZone value="Antarctica/Casey">
<fmt:formatDate value="${beanNow}" pattern="yyyy.MMM.dd hh:mm:ss" />
</fmt:timeZone>
<fmt:setLocale value="ja_JP"/>
<fmt:formatDate value="${beanNow}" pattern="yyyy.MMM.dd hh:mm:ss" />
Сначала из числа в строку:
<fmt:formatNumber pattern="#,##0.0#" value="234234.23423" />
<fmt:parseNumber value="234,234.23" pattern="#,##0.0#" />
<fmt:setBundle basename="testi.messages" var="loc" />
<fmt:message bundle="${loc}" key="MSG_1" />
<fmt:message bundle="${loc}" key="MSG_2" var="m_2"/>
<c:out value="${m_2}" />
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<c:set value="boy goes to school" var="boy" />
<c:if test="${fn:contains(boy, 'goes')}">
<h1>
He goes to School. Realy.
</h1>
</c:if>
<c:if test="${fn:containsIgnoreCase(boy, 'GOES')}">
<h1>
He goes to School. Realy.
</h1>
</c:if>
<c:if test="${fn:startsWith(boy, 'boy')}">
<h1>
Starts from BOY
</h1>
</c:if>
<c:if test="${fn:endsWith(boy, 'school')}">
<h1>
Ends with SCHOOL
</h1>
</c:if>
<c:set value="boy goes � to school" var="boy" />
<c:out value="${boy}" />
<c:out value="${fn:escapeXml(boy)}" />
<c:out value="${boy}" escapeXml="true" />
<c:set value="boy goes to school" var="boy" />
<c:out value="${fn:indexOf(boy, 'to' )}" />
<c:set value="boy goes to school" var="boy" />
<c:out value="${fn:substring(boy, fn:indexOf(boy, 'to' ), 100)}" />
<c:out value="${fn:substring(boy, fn:indexOf(boy, 'to' ), fn:indexOf(boy, 'to' )+2)}" />
<c:out value="${fn:substring(boy, fn:indexOf(boy, 'to' ), -1)}" />
<sql:query var="users" dataSource="jdbc/VeloDemoDS">
select * from tm_user
</sql:query>
<c:set value="boy goes to school" var="boy" />
<c:out value="${fn:length(boy)}" />
<c:out value="${fn:length(users.rows)}" />
<c:set value="boy goes to school" var="boy" />
After:<c:out value="${fn:substringAfter(boy, 'to')}" />
Before:<c:out value="${fn:substringBefore(boy, 'to')}" />
<c:set value="boy goes to school" var="boy" />
<c:out value="${fn:toLowerCase(boy)}" />
<c:out value="${fn:toUpperCase(boy)}" />
<c:set value=" boy goes to school " var="boy" />
<h1><c:out value="${fn:trim(boy)}"/></h1>
<c:set value="boy goes to school" var="boy" />
<c:out value="${fn:replace(boy, 'school', 'bar')}"/>
<c:set value="1,2,3,4" var="str_digits" />
<c:set value="${fn:split(str_digits, ',')}" var="arr_digits" />
<c:set value="${fn:join(arr_digits, '!')}" var="str2_digits"/>
<c:out value="${fn:length(arr_digits)}" />
<c:out value="${str2_digits}" />
« JSTL: Шаблоны для разработки веб-приложений в java. Часть 2 | Hibernate: отображая иерархии классов » |