Статьи

Учебное пособие по Java EE: JSP 2.2 и сервлетам 3.0 с контейнером сервлетов из смолы. Часть вторая

Учебник по сервлету Java EE: добавление создания, обновления и удаления в список книжного магазина.  

Это учебное пособие является частью учебного пособия по Java EE, охватывающего JSP_2.2 и сервлеты 3.0.

содержание

  • Учебник по сервлету Java EE: реализация базового списка CRUD
  • Добавление ссылки в список книг для редактирования книги

    • Добавление ссылки на редактирование книги в список book-list.jsp
  • Добавление ссылки в список книг для добавления книги

    • Добавление ссылки на добавление книги в список book-list.jsp
  • Servlet doGet для загрузки формы книги

    • BookEditorServlet.java doGet
    • Делегат BookEditorServlet.java doGet () на странице book-form.jsp
    • Делегат BookEditorServlet.java doGet () на странице book-form.jsp
  • Визуализация формы книги HTML

    • book-form.jsp Предоставляет форму для обновления или добавления книги
    • book-form.jsp с использованием JSTL c: выбрать отображение обновления или добавить статус
    • book-form.jsp с использованием JSTL c: если скрыть поле id для операции редактирования / обновления
  • Создание метода doPost для обработки отправки формы

    • BookEditorServlet.java doPost
  • Краткий обзор того, что мы имеем до сих пор

    • ./WebContent/WEB-INF/pages/book-form.jsp полный список
    • ./WebContent/WEB-INF/pages/book-list.jsp полный список
    • ./src/META-INF/beans.xml полный список
    • ./src/com/bookstore/Book.java
    • ./src/com/bookstore/BookRepositoryImpl.java полный список (только для тестирования)
    • ./src/com/bookstore/BookRepository.java полный список
    • ./src/com/bookstore/web/BookEditorServlet.java
    • ./src/com/bookstore/web/BookListServlet.java
  • Технический долг

Учебник по сервлету 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}

$ {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 для книжного магазина