Учебник по сервлету Java EE: добавление создания, обновления и удаления в список книжного магазина.
Это учебное пособие является частью учебного пособия по Java EE, охватывающего JSP_2.2 и сервлеты 3.0.
содержание
|
Учебник по сервлету Java EE: реализация базового списка CRUD
Мы остановились с основным списком. BookListServlet использовал объект BookRepository (DAO) для загрузки списка книг, а затем делегировал его в book-list.jsp для визуализации списка книг.
На этом этапе мы хотим добавить ссылку в список книг, чтобы при нажатии пользователем на нее отображалась форма, чтобы они могли редактировать детали книги. Также мы хотим добавить ссылку, чтобы конечный пользователь мог добавить новую книгу в список.
Добавление ссылки в список книг для редактирования книги
Измените код, который выглядит следующим образом, в book-list.jsp:
... <c:forEach var="book" items="${books}"> <tr> <td>${book.title}</td> ...
Для этого, добавив ссылку на редактирование книги в список book-list.jsp
... <c:forEach var="book" items="${books}"> <tr> <td><a href="${pageContext.request.contextPath}/book?id=${book.id}">${book.title}</a></td> ...
Выражение EL $ {pageContext.request.contextPath} ссылается на URI, с которым сопоставлен файл war в контейнере сервлета. URI по умолчанию для веб-приложения — это имя файла войны, поэтому наше имя файла войны — bookstore, поэтому отображаемый URL-адрес будет выглядеть следующим образом:
... <tr> <td><a href="/bookstore/book?id=0">War and Peace</a></td> ... <tr> <td><a href="/bookstore/book?id=1">Pride and Prejudice</a></td> ...
Ссылка / книжный магазин / книга / (с косой чертой) загружает список книг. Ссылка / книжный магазин / книга? Id = 1 загружает страницу формы для редактирования существующей книги. Чтобы добавить новую книгу, мы будем использовать ссылку / книжный магазин / книгу.
Добавление ссылки в список книг для добавления книги
Добавление ссылки на добавление книги в список book-list.jsp
Перед таблицей добавьте этот HTML-код, чтобы добавить новую книгу:
... <a href="${pageContext.request.contextPath}/book">Add Book</a> ...
Теперь мы должны ссылаться на URI / книгу, нам нужен сервлет, который обрабатывает ссылки. BookEditorServlet обрабатывает функции добавления и редактирования книг.
Servlet doGet для загрузки формы книги
BookEditorServlet.java doGet
package com.bookstore.web; ... @WebServlet("/book") public class BookEditorServlet extends HttpServlet { @Inject private BookRepository bookRepo; private SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy"); ... /** Prepare the book form before we display it. */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String id = request.getParameter("id"); if (id != null && !id.isEmpty()) { Book book = bookRepo.lookupBookById(id); request.setAttribute("book", book); request.setAttribute("bookPubDate", dateFormat.format(book.getPubDate())); } /* Redirect to book-form. */ getServletContext().getRequestDispatcher("/WEB-INF/pages/book-form.jsp").forward( request, response); } }
Обратите внимание, что мы используем @WebServlet («/ book») для сопоставления BookEditorServlet с URI / book. Обычно загружают форму из метода doGet и обрабатывают отправку формы через doPost. Следуя принципам REST и HTTP, операции GET считывают данные, а данные POST изменяют данные. Метод doGet использует пустой идентификатор или нет, чтобы определить, является ли это загрузкой «Добавить форму сотрудника» или операцией загрузки «Обновить форму сотрудника». Метод doGet также преобразует дату в формат, который легко изменить конечному пользователю, и также отображает книгу в область запроса следующим образом:
Делегат BookEditorServlet.java doGet () на странице book-form.jsp
Book book = bookRepo.lookupBookById(id); request.setAttribute("book", book); request.setAttribute("bookPubDate", dateFormat.format(book.getPubDate()));
В архитектуре модели 2 уровень сервлетов подготавливает данные формы до того, как форма загружается для правильного отображения и редактирования.
Чтобы отобразить HTML-форму, сервлет делегирует book-form.jsp следующим образом:
Делегат BookEditorServlet.java doGet () на странице book-form.jsp
/* Redirect to book-form. */ getServletContext().getRequestDispatcher("/WEB-INF/pages/book-form.jsp").forward( request, response);
Контейнер
сервлетов с
открытым исходным кодом
Caucho’s Resin OpenSource Контейнер сервлетов Java EE JavaLobby Article: Учебное пособие по Java EE, посвященное JSP и сервлетам Часть первая Учебное пособие по сервлету Java EE Caucho Wiki: Использование JSP для создания заголовка, области нижнего колонтитула, форматирования и базовых CSS для книжного магазина <% @ page language = «java» contentType = «text / html; charset = UTF-8» pageEncoding = «UTF-8″%> <% @ taglib uri = «http://java.sun.com/jsp/jstl/core» префикс = «C» %>
$ {Param.title}
Визуализация формы книги HTML
Книга-form.jsp имеет HTML-код для отображения формы следующим образом:
book-form.jsp Предоставляет форму для обновления или добавления книги
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML> <html> <head> <title>Book Form</title> </head> <body> <h1>Book Form</h1> <form method="post" action="${pageContext.request.contextPath}/book"> <fieldset> <legend> <c:choose> <c:when test="${not empty book.id }"> Updating Book </c:when> <c:otherwise> Adding Book </c:otherwise> </c:choose> </legend> <div> <label for="title">Title</label> <input type="text" name="title" id="title" value="${book.title}" /> </div> <div> <label for="description">Description</label> <textarea name="description" id="description" rows="2" cols="60">${book.description}</textarea> </div> <div> <label for="price">Price prebuffer_7lt;/label> <input name="price" id="price" value="${book.price}" /> </div> <div> <label for="pubDate">Publication Date</label> <input name="pubDate" id="pubDate" value="${bookPubDate}" /> <label class="after">(MM/DD/YYYY)</label> </div> <c:if test="${not empty book.id}"> <input type="hidden" name="id" value="${book.id}" /> </c:if> </fieldset> <div class="button-row"> <a href="${pageContext.request.contextPath}/book/">Cancel</a> or <input type="submit" value="Submit" /> </div> </form> </body> </html>
Книга-form.jsp использует JSTL c: choose, c: иначе, чтобы показать, добавляем ли мы новую книгу или обновляем книгу следующим образом:
book-form.jsp с использованием JSTL c: выбрать отображение обновления или добавить статус
... <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> ... <legend> <c:choose> <c:when test="${not empty book.id }"> Updating Book </c:when> <c:otherwise> Adding Book </c:otherwise> </c:choose> </legend> ...
Если свойство book.id присутствует, отображается тест «Обновление книги».
Кроме того, форма отображает скрытое свойство id, если выполняет операцию редактирования / обновления следующим образом:
book-form.jsp с использованием JSTL c: если скрыть поле id для операции редактирования / обновления
<c:if test="${not empty book.id}"> <input type="hidden" name="id" value="${book.id}" /> </c:if>
Метод doPost BookEditorServlet обрабатывает отправку формы следующим образом:
Создание метода doPost для обработки отправки формы
BookEditorServlet.java doPost
package com.bookstore.web; ... @WebServlet("/book") public class BookEditorServlet extends HttpServlet { @Inject private BookRepository bookRepo; ... /** * Handles posting an HTML book form. If the id is null then it is an * "add book", if the id is set then it is an "update book" */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String title = request.getParameter("title"); String description = request.getParameter("description"); String price = request.getParameter("price"); String pubDate = request.getParameter("pubDate"); String id = request.getParameter("id"); if (id == null || id.isEmpty()) { bookRepo.addBook(title, description, price, pubDate); } else { bookRepo.updateBook(id, title, description, price, pubDate); } response.sendRedirect(request.getContextPath() + "/book/"); } ... }
Обратите внимание, что если идентификатор равен нулю, то BookEditorServlet.doPost вызывает bookRepo.addBook, в противном случае он вызывает bookRepo.updateBook. Когда обработка формы завершена, doPost перенаправляет в / book /. Перенаправление означает дополнительное попадание на сервер, так как мы в основном говорим браузеру загрузить другую ссылку. Проницательный читатель может удивиться, почему мы не просто делаем форвард, как в doGet. Ответ в закладки. URL / book / (оканчивающийся косой чертой) представляет собой коллекцию книг, а / book (без косой черты) представляет отдельную книгу (используя URL-адрес в стиле REST). Если бы мы сделали пересылку, то после отправки формы браузер отобразил бы список, но под URI / book вместо / book /. Если пользователь ender ссылается на / book, он не вернет их обратно в список, а вернет к форме, что неверно. Вот почему мы делаем sendRedirect вместо forward.
Теперь у нас есть добавление / редактирование / обновление / чтение / и листинг. Это все, что вы могли бы хотеть от листинга CRUD.
На данный момент мы не делаем много проверки. Мы добавим это в следующем уроке.
Контейнер
сервлетов с
открытым исходным кодом
Caucho’s Resin OpenSource Контейнер сервлетов веб-профиля Java EE JavaLobby Article: Учебное пособие по Java EE, посвященное JSP и сервлетам Часть первая Учебное пособие по сервлету Java EE Caucho Wiki: Использование JSP для создания заголовка, области нижнего колонтитула, форматирования и базовых CSS для книжного магазина
Краткий обзор того, что мы имеем до сих пор
Для этого примера книжного магазина мы создали следующие файлы:
$ find . . ./WebContent/WEB-INF/pages/book-form.jsp Book Form ./WebContent/WEB-INF/pages/book-list.jsp Book Listing ./src/META-INF/beans.xml Needed for Java EE dependency injection (CDI) ./src/com/bookstore/Book.java Domain/model object ./src/com/bookstore/BookRepositoryImpl.java Repository implementation using Java collections (just for testing) ./src/com/bookstore/BookRepository.java Interface to Book Repository so we can swap it out with JDBC, JPA, JCache and MongoDB version later ./src/com/bookstore/web/BookEditorServlet.java Servlet that loads Book (doGet) form and handles Book form submissions (doPost). ./src/com/bookstore/web/BookListServlet.java Servlet that looks up a list of books and displays the listing
./WebContent/WEB-INF/pages/book-form.jsp полный список
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML> <html> <head> <title>Book Form</title> </head> <body> <h1>Book Form</h1> <form method="post" action="${pageContext.request.contextPath}/book"> <fieldset> <legend> <c:choose> <c:when test="${not empty book.id }"> Updating Book </c:when> <c:otherwise> Adding Book </c:otherwise> </c:choose> </legend> <div> <label for="title">Title</label> <input type="text" name="title" id="title" value="${book.title}" /> </div> <div> <label for="description">Description</label> <textarea name="description" id="description" rows="2" cols="60">${book.description}</textarea> </div> <div> <label for="price">Price prebuffer_12lt;/label> <input name="price" id="price" value="${book.price}" /> </div> <div> <label for="pubDate">Publication Date</label> <input name="pubDate" id="pubDate" value="${bookPubDate}" /> <label class="after">(MM/DD/YYYY)</label> </div> <c:if test="${not empty book.id}"> <input type="hidden" name="id" value="${book.id}" /> </c:if> </fieldset> <div class="button-row"> <a href="${pageContext.request.contextPath}/book/">Cancel</a> or <input type="submit" value="Submit" /> </div> </form> </body> </html>
./WebContent/WEB-INF/pages/book-list.jsp полный список
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix ="c" %> <!DOCTYPE HTML> <html> <head> <title>Book listing</title> </head> <body> <a href="${pageContext.request.contextPath}/book">Add Book</a> <table> <tr> <th>Title</th> <th>Description</th> <th>Price</th> <th>Publication Date</th> </tr> <c:forEach var="book" items="${books}"> <tr> <td><a href="${pageContext.request.contextPath}/book?id=${book.id}">${book.title}</a></td> <td>${book.description}</td> <td>${book.price}</td> <td>${book.pubDate}</td> </tr> </c:forEach> </table> </body> </html>
./src/META-INF/beans.xml полный список
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> </beans>
./src/com/bookstore/Book.java
package com.bookstore; import java.math.BigDecimal; import java.util.Date; public class Book implements Cloneable { private String title; private String description; private BigDecimal price; private Date pubDate; private String id; public Book(String id, String title, String description, BigDecimal price, Date pubDate) { this.id = id; this.title = title; this.description = description; this.price = price; this.pubDate = pubDate; } public Book () { } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } public Date getPubDate() { return pubDate; } public void setPubDate(Date pubDate) { this.pubDate = pubDate; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Book cloneMe() { try { return (Book) super.clone(); } catch (CloneNotSupportedException e) { return null; } } @Override public String toString() { return "Book [title=" + title + ", description=" + description + ", price=" + price + ", pubDate=" + pubDate + ", id=" + id + "]"; } }
./src/com/bookstore/BookRepositoryImpl.java полный список (только для тестирования)
package com.bookstore; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.math.BigDecimal; import javax.enterprise.context.ApplicationScoped; @ApplicationScoped public class BookRepositoryImpl implements BookRepository { private SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy"); private int count; private Map<String, Book> idToBookMap = new HashMap<String, Book>(); public BookRepositoryImpl() { synchronized (this) { books(book("War and Peace", "blah blah blah", "5.50", "5/29/1970"), book("Pride and Prejudice", "blah blah blah", "5.50", "5/29/1960"), book("book1", "blah blah blah", "5.50", "5/29/1960"), book("book2", "blah blah blah", "5.50", "5/29/1960"), book("book3", "blah blah blah", "5.50", "5/29/1960"), book("book4", "blah blah blah", "5.50", "5/29/1960"), book("book5", "blah blah blah", "5.50", "5/29/1960"), book("book6", "blah blah blah", "5.50", "5/29/1960"), book("book7", "blah blah blah", "5.50", "5/29/1960"), book("book8", "blah blah blah", "5.50", "5/29/1960"), book("book9", "blah blah blah", "5.50", "5/29/1960"), book("Java for dummies", "blah blah blah", "1.99", "5/29/1960")); } } private Book book(String title, String description, String aPrice, String aPubDate) { Date pubDate = null; BigDecimal price = null; try { price = new BigDecimal(aPrice); }catch (Exception ex) { } try { pubDate = dateFormat.parse(aPubDate); }catch (Exception ex) { } return new Book("" + (count++), title, description, price, pubDate); } private void books(Book... books) { for (Book book : books) { doAddBook(book); } } private void doAddBook(Book book) { synchronized (this) { this.idToBookMap.put(book.getId(), book); } } @Override public Book lookupBookById(String id) { synchronized (this) { return this.idToBookMap.get(id).cloneMe(); } } @Override public void addBook(String title, String description, String price, String pubDate) { doAddBook(book(title, description, price, pubDate)); } @Override public void updateBook(String id, String title, String description, String price, String pubDate) { Book book = book(title, description, price, pubDate); synchronized (this) { book.setId(id); this.idToBookMap.put(id, book); } } private List<Book> doListBooks() { List<Book> books; synchronized (this) { books = new ArrayList<Book>(this.idToBookMap.size()); for (Book book : this.idToBookMap.values()) { books.add(book.cloneMe()); } } return books; } public List<Book> listBooks() { List<Book> books = doListBooks(); Collections.sort(books, new Comparator<Book>() { public int compare(Book bookA, Book bookB) { return bookA.getId().compareTo(bookB.getId()); } }); return books; } @Override public void removeBook(String id) { synchronized(this) { this.idToBookMap.remove(id); } } }
./src/com/bookstore/BookRepository.java полный список
package com.bookstore; import java.util.List; public interface BookRepository { Book lookupBookById(String id); void addBook(String title, String description, String price, String pubDate); void updateBook(String id, String title, String description, String price, String pubDate); void removeBook(String id); List<Book> listBooks(); }
./src/com/bookstore/web/BookEditorServlet.java
package com.bookstore.web; import java.io.IOException; import java.text.SimpleDateFormat; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.bookstore.Book; import com.bookstore.BookRepository; /** * Servlet implementation class BookEditorServlet */ @WebServlet("/book") public class BookEditorServlet extends HttpServlet { @Inject private BookRepository bookRepo; private SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy"); /** Prepare the book form before we display it. */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String id = request.getParameter("id"); if (id != null && !id.isEmpty()) { Book book = bookRepo.lookupBookById(id); request.setAttribute("book", book); request.setAttribute("bookPubDate", dateFormat.format(book.getPubDate())); } /* Redirect to book-form. */ getServletContext().getRequestDispatcher("/WEB-INF/pages/book-form.jsp").forward( request, response); } /** * Handles posting an HTML book form. If the id is null then it is an * "add book", if the id is set then it is an "update book" */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String title = request.getParameter("title"); String description = request.getParameter("description"); String price = request.getParameter("price"); String pubDate = request.getParameter("pubDate"); String id = request.getParameter("id"); if (id == null || id.isEmpty()) { bookRepo.addBook(title, description, price, pubDate); } else { bookRepo.updateBook(id, title, description, price, pubDate); } response.sendRedirect(request.getContextPath() + "/book/"); } }
./src/com/bookstore/web/BookListServlet.java
package com.bookstore.web; import java.io.IOException; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.bookstore.BookRepository; @WebServlet("/book/") public class BookListServlet extends HttpServlet { @Inject private BookRepository bookRepo; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("books", bookRepo.listBooks()); getServletContext().getRequestDispatcher("/WEB-INF/pages/book-list.jsp").forward(request, response); } }
Технический долг
У нас накопился технический долг. Наш пользовательский интерфейс просто HTML, у него нет стиля. Это наверное и недосмотр. Нет нижнего колонтитула или заголовка. Вероятно, должна быть какая-то область заголовка, которая имеет ссылку на домашнюю страницу, и нижний колонтитул с уведомлением об авторских правах или что-то в этом роде. Список книг не форматирует цену даты, это следует.
Мы рассмотрим эти и другие вопросы на следующих уроках.
Контейнер
сервлетов с
открытым исходным кодом
Caucho’s Resin OpenSource Контейнер сервлетов веб-профиля Java EE JavaLobby Article: Учебное пособие по Java EE, посвященное JSP и сервлетам Часть первая Учебное пособие по сервлету Java EE Caucho Wiki: Использование JSP для создания заголовка, области нижнего колонтитула, форматирования и базовых CSS для книжного магазина