前言
鞏固Servlet+JSP開發模式,做一個比較完整的小項目.
成果圖
該項目包含了兩個部分,前台和后台。
前台用於顯示
后台用於管理
該項目可分為5個模塊來組成:分類模塊,用戶模塊,圖書模塊,購買模塊,訂單模塊。
搭建環境
建立包結構
導入開發包
前台分幀頁面
- index.jsp【沒有body標簽的】
<frameset rows="25%,*">
<frame src="${pageContext.request.contextPath}/client/head.jsp"/>
<frame src="${pageContext.request.contextPath}/client/body.jsp"/>
</frameset>
- head.jsp
<body style="text-align: center">
<h1>歡迎來到購物中心</h1>
-
body是空白的jsp頁面
-
效果:
后台分幀頁面
- manager.jsp【嵌套了framset標簽,也是沒有body標簽的】
<frameset rows="25%,*">
<frame src="${pageContext.request.contextPath}/background/head.jsp"/>
<frameset cols="15%,*">
<frame src="${pageContext.request.contextPath}/background/left.jsp"/>
<frame src="${pageContext.request.contextPath}/background/body.jsp"/>
</frameset>
</frameset>
- head.jsp
<body style="text-align: center">
<h1>后台管理</h1>
- left.jsp
<a href="#">分類管理</a>
<br>
<br>
<a href="#">圖書管理</a>
<br>
<br>
<a href="#">訂單管理</a>
<br>
<br>
-
body.jsp是空白的
-
效果:
分幀的文件夾目錄結構
值得注意的是:
- 文件夾的名字不能使用“manager”,不然會出現:403 Access Denied錯誤
- frameset標簽是可以嵌套的,分列用“cols”,分行用“rows”
導入工具類和方法的代碼
- 過濾中文亂碼數據
- HTML轉義
- DAOFactory
- JDBC連接池
- UUID工具類
- c3p0.xml配置文件
這些代碼都可以在我的博客分類:代碼庫中找到!
分類模塊
首先,我們來做分類模塊吧
創建實體Category
private String id;
private String name;
private String description;
//各種setter、getter
在數據庫創建表
CREATE TABLE category (
id VARCHAR(40) PRIMARY KEY,
name VARCHAR(10) NOT NULL UNIQUE ,
description VARCHAR(255)
);
編寫CategoryDAO
/**
* 分類模塊
* 1:添加分類
* 2:查找分類
* 3:修改分類
*
*
* */
public class CategoryImpl {
public void addCategory(Category category) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "INSERT INTO category (id, name, description) VALUES(?,?,?)";
try {
queryRunner.update(sql, new Object[]{category.getId(), category.getName(), category.getDescription()});
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Category findCategory(String id) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM category WHERE id=?";
try {
Category category = (Category) queryRunner.query(sql, id, new BeanHandler(Category.class));
return category;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public List<Category> getAllCategory() {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM category";
try {
List<Category> categories = (List<Category>) queryRunner.query(sql, new BeanListHandler(Category.class));
return categories;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
測試DAO
public class demo {
@Test
public void add() {
Category category = new Category();
category.setId("2");
category.setName("數據庫系列");
category.setDescription("這是數據庫系列");
CategoryImpl category1 = new CategoryImpl();
category1.addCategory(category);
}
@Test
public void find() {
String id = "1";
CategoryImpl category1 = new CategoryImpl();
Category category = category1.findCategory(id);
System.out.println(category.getName());
}
@Test
public void getAll() {
CategoryImpl category1 = new CategoryImpl();
List<Category> categories = category1.getAllCategory();
for (Category category : categories) {
System.out.println(category.getName());
}
}
}
抽取成DAO接口
public interface CategoryDao {
void addCategory(Category category);
Category findCategory(String id);
List<Category> getAllCategory();
}
后台頁面的添加分類
- 在超鏈接上,綁定顯示添加分類的頁面
<a href="${pageContext.request.contextPath}/background/addCategory.jsp" target="body">添加分類</a>
- 顯示添加分類的JSP頁面
<form action="${pageContext.request.contextPath}/CategoryServlet?method=add" method="post">
分類名稱:<input type="text" name="name"><br>
分類描述:<textarea name="description"></textarea><br>
<input type="submit" value="提交">
</form>
- 處理添加分類的Servlet
if (method.equals("add")) {
try {
//把瀏覽器帶過來的數據封裝到bean中
Category category = WebUtils.request2Bean(request, Category.class);
category.setId(WebUtils.makeId());
service.addCategory(category);
request.setAttribute("message", "添加分類成功!");
} catch (Exception e) {
request.setAttribute("message","添加分類失敗");
e.printStackTrace();
}
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
- 效果:
后台頁面的查看分類
- 在超鏈接上,綁定處理請求的Servlet
else if (method.equals("look")) {
List<Category> list = service.getAllCategory();
request.setAttribute("list", list);
request.getRequestDispatcher("/background/lookCategory.jsp").forward(request, response);
}
- 顯示分類頁面的JSP
<c:if test="${empty(list)}">
暫時還沒有分類數據哦,請你添加把
</c:if>
<c:if test="${!empty(list)}">
<table border="1px">
<tr>
<td>分類名字</td>
<td>分類描述</td>
<td>操作</td>
</tr>
<c:forEach items="${list}" var="category">
<tr>
<td>${category.name}</td>
<td>${category.description}</td>
<td>
<a href="#">刪除</a>
<a href="#">修改</a>
</td>
</tr>
</c:forEach>
</table>
</c:if>
- 效果:
圖書模塊
分析
在設計圖書管理的時候,我們應該想到:圖書和分類是有關系的。一個分類可以對應多本圖書。
為什么要這樣設計?這樣更加人性化,用戶在購買書籍的時候,用戶能夠查看相關分類后的圖書,而不是全部圖書都顯示給用戶,讓用戶一個一個去找。
設計實體
private String id;
private String name;
private String author;
private String description;
private double price;
//記住圖片的名稱
private String image;
//記住分類的id
private String category_id;
//各種setter和getter
設計數據庫表
CREATE TABLE book (
id VARCHAR(40) PRIMARY KEY,
name VARCHAR(10) NOT NULL UNIQUE,
description VARCHAR(255),
author VARCHAR(10),
price FLOAT,
image VARCHAR(100),
category_id VARCHAR(40),
CONSTRAINT category_id_FK FOREIGN KEY (category_id) REFERENCES category (id)
);
編寫DAO
/**
* 圖書模塊
* 1:添加圖書
* 2:查看圖書
* 3:查找圖書的分頁數據【圖書一般來說有很多,所以要分頁】
*/
public class BookDaoImpl {
public void addBook(Book book) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "INSERT INTO book (id,name,description,author,price,image,category_id) VALUES(?,?,?,?,?,?,?)";
try {
queryRunner.update(sql, new Object[]{book.getId(), book.getName(), book.getDescription(), book.getAuthor(), book.getPrice(),book.getImage(), book.getCategory_id()});
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Book findBook(String id) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM book WHERE id=?";
try {
return (Book) queryRunner.query(sql, id, new BeanHandler(Book.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**得到圖書的分頁數據*/
public List<Book> getPageData(int start, int end) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT * FROM book limit ?,?";
try {
return (List<Book>) queryRunner.query(sql, new BeanListHandler(Book.class), new Object[]{start, end});
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**得到按照分類圖書的分頁數據*/
public List<Book> getPageData(int start, int end,String category_id) {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
//WHERE字句在limit字句的前邊,注意Object[]的參數位置!
String sql = "SELECT * FROM book WHERE category_id=? limit ?,?";
try {
return (List<Book>) queryRunner.query(sql, new BeanListHandler(Book.class), new Object[]{ category_id,start, end});
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 得到圖書的總記錄數
*/
public int getTotalRecord() {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT COUNT(*) FROM book";
try {
return (int) queryRunner.query(sql, new ScalarHandler());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 得到分類后圖書的總記錄數
* getCategoryTotalRecord
*/
public long getCategoryTotalRecord(String category_id) {
try {
QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource());
String sql = "SELECT COUNT(*) FROM book WHERE category_id=?";
return (long) queryRunner.query(sql, category_id, new ScalarHandler());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
測試DAO
public class BookDemo {
BookDaoImpl bookDao = new BookDaoImpl();
@Test
public void add() {
Book book = new Book();
book.setId("5");
book.setName("SQLServer");
book.setAuthor("我也不知道");
book.setImage("33333332432");
book.setPrice(33.22);
book.setDescription("這是一本好書");
book.setCategory_id("2");
bookDao.addBook(book);
}
@Test
public void look() {
List<Book> bookList = bookDao.getPageData(3, 3);
for (Book book : bookList) {
System.out.println(book.getName());
}
List<Book> books = bookDao.getPageData(0,2,"2");
for (Book book : books) {
System.out.println(book.getName());
}
}
@Test
public void find() {
String id = "2";
Book book = bookDao.findBook(id);
System.out.println(book.getName());
}
}
抽取成DAO接口
public interface BookDao {
void addBook(Book book);
Book findBook(String id);
List<Book> getPageData(int start, int end);
List<Book> getPageData(int start, int end, String category_id);
long getTotalRecord();
long getCategoryTotalRecord(String category_id);
}
編寫Service層
/*添加圖書*/
public void addBook(Book book) {
bookDao.addBook(book);
}
/*查找圖書*/
public Book findBook(String id) {
return bookDao.findBook(id);
}
/*查找圖書*/
public Book findBook(String id) {
return bookDao.findBook(id);
}
/*獲取圖書的分頁數據*/
public Page getPageData(String pageNum) {
Page page=null;
if (pageNum == null) {
page = new Page(1, bookDao.getTotalRecord());
} else {
page = new Page(Integer.valueOf(pageNum), bookDao.getTotalRecord());
}
List<Book> books = bookDao.getPageData(page.getStartIndex(), page.getLinesize());
page.setList(books);
return page;
}
/*獲取圖書分類后的分頁數據*/
public Page getPageData(String currentPageCount,String category_id) {
Page page=null;
if (currentPageCount == null) {
page = new Page(1, bookDao.getCategoryTotalRecord(category_id));
} else {
page = new Page(Integer.valueOf(currentPageCount), bookDao.getCategoryTotalRecord(category_id));
}
List<Book> books = bookDao.getPageData(page.getStartIndex(), page.getLinesize(), category_id);
page.setList(books);
return page;
}
后台添加圖書
后台要添加圖書的時候,應該說明圖書的類型是什么。
要想在顯示添加圖書的頁面上知道全部類型的id,就要經過Servlet把類型的集合傳送過去
綁定鏈接
<a href="${pageContext.request.contextPath}/BookServlet?method=addUI" target="body">添加圖書</a><br>
傳送類型集合的Servlet
String method = request.getParameter("method");
BussinessServiceImpl service = new BussinessServiceImpl();
if (method.equals("addUI")) {
List<Category> list = service.getAllCategory();
request.setAttribute("list", list);
request.getRequestDispatcher("/background/addBook.jsp").forward(request, response);
}
顯示JSP頁面
<form action="${pageContext.request.contextPath}/BookServlet?method=add" method="post" enctype="multipart/form-data">
<table border="1px" width="30%">
<tr>
<td> 圖書名稱:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td> 作者:</td>
<td><input type="text" name="author"></td>
</tr>
<tr>
<td> 圖書價錢:</td>
<td><input type="text" name="price"></td>
</tr>
<tr>
<td>類型:</td>
<td>
<select name="category_id">
<c:forEach items="${list}" var="category">
<option value="${category.id}">${category.name}</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td> 上傳圖片</td>
<td><input type="file" name="image"></td>
</tr>
<tr>
<td>詳細描述</td>
<td><textarea name="description"></textarea></td>
</tr>
<tr>
<td>
<input type="submit" value="提交">
<input type="reset" value="重置">
</td>
</tr>
</table>
</form>
處理表單數據Servlet
else if (method.equals("add")) {
//上傳文件和普通數據分割開,封裝到Book對象上
Book book = uploadData(request);
book.setId(WebUtils.makeId());
service.addBook(book);
request.setAttribute("message", "添加圖書成功");
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
- uploadData()方法代碼
private Book uploadData(HttpServletRequest request) {
Book book = new Book();
try{
//1.得到解析器工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.得到解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//設置編碼
upload.setHeaderEncoding("UTF-8");
//為上傳表單,則調用解析器解析上傳數據
List<FileItem> list = upload.parseRequest(request); //FileItem
//遍歷list,得到用於封裝第一個上傳輸入項數據fileItem對象
for(FileItem item : list){
if(item.isFormField()){
//得到的是普通輸入項
String name = item.getFieldName(); //得到輸入項的名稱
String value = item.getString("UTF-8");
//使用BeanUtils封裝數據
BeanUtils.setProperty(book, name, value);
}else{
//得到上傳輸入項
//得到上傳文件名全路徑
String filename = item.getName();
//截取文件名
filename = filename.substring(filename.lastIndexOf("\\")+1);
InputStream in = item.getInputStream(); //得到上傳數據
int len = 0;
byte buffer[]= new byte[1024];
//如果沒有這個目錄,就創建它
String savepath = this.getServletContext().getRealPath("/image");
File file = new File(savepath);
if (!file.exists()) {
file.mkdir();
}
FileOutputStream out = new FileOutputStream(savepath + "\\" + filename);
while((len=in.read(buffer))>0){
out.write(buffer, 0, len);
}
//設置圖片的名字
book.setImage(filename);
in.close();
out.close();
//關閉臨時文件
item.delete();
}
}
}catch (Exception e) {
e.printStackTrace();
}
return book;
}
- 效果:
后台顯示圖書模塊
由於我們用的是分頁技術,所以我們導入之前寫過的Page類和jsp吧.....這些代碼可以在我分類的代碼庫中找到
綁定超鏈接
<a href="${pageContext.request.contextPath}/BookServlet?method=look" target="body">查看圖書</a>
Servlet處理請求
else if (method.equals("look")) {
String currentPageCount = request.getParameter("currentPageCount");
Page page = service.getPageData(currentPageCount);
request.setAttribute("page",page);
request.getRequestDispatcher("/background/listBook.jsp").forward(request, response);
}
顯示圖書JSP頁面
Servlet端傳過來的是Page對象,而不是list集合
可以根據記載在Book對象的圖片名稱,弄一個超鏈接,超鏈接指向服務端的圖片,這樣就可以查看圖片了!
<c:if test="${empty(page.list)}">
暫時還沒有任何圖書哦
</c:if>
<c:if test="${!empty(page.list)}">
<table border="1px">
<tr>
<td>書名</td>
<td>作者</td>
<td>價錢</td>
<td>描述</td>
<td>圖片</td>
<td>操作</td>
</tr>
<c:forEach var="book" items="${page.list}" >
<tr>
<td>${book.name}</td>
<td>${book.author}</td>
<td>${book.price}</td>
<td>${book.description}</td>
<td><a href="${pageContext.request.contextPath}/image/${book.image}">查看圖片</a></td>
<td>
<a href="#">刪除</a>
<a href="#">修改</a>
</td>
</tr>
</c:forEach>
</table>
<br>
<jsp:include page="page.jsp"/>
</c:if>
效果:
前台頁面
看回我們前台頁面的成果圖,我們可以把整個body頁面看成是三個div
- body占整個div
- 導航條是一個div
- 顯示圖書的地方是一個div
設計好大概的布局
- html代碼引入css
<link rel="stylesheet" href="body.css" type="text/css">
- HTML三個div
<div id="body">
<div id="category">
<c:forEach items="${categorys}" var="category">
</c:forEach>
這是導航條
</div>
<div id="bookandpages">
<div id="books">
這是書籍的地方
</div>
<div id="page">
這是頁碼
</div>
</div>
</div>
- CSS代碼:
#body {
position: relative;
}
#category {
border: 1px solid #000;
position: absolute;
width: 300px;
height: 400px;
float: left;
left: 200px;
top: 70px;;
}
#bookandpages {
border: 1px solid #000000;
position: absolute;
width: 600px;
height: 600px;;
float: left;
left: 500px;
margin-left: 50px;
}
#books {
border: 1px solid #000;
width: 600px;
height: 550px;;
}
#page {
border: 1px solid #000;
position: absolute;
height: 48px;
width: 600px;
}
- 大概的布局
IndexServlet
在顯示首頁的下部分的時候,應該先去尋找一個Servlet來把數據交給對應的JSP。
因為我們的JSP一般都是放在WEB-INF下,是不能直接訪問的。還有就是JSP往往是需要我們后台的數據的,因此我們使用Servlet來獲取得到數據,再交由JSP來展示就最好不過了。
<frame src="${pageContext.request.contextPath}/IndexServlet"/>
- Servlet代碼:
//得到所有的分類數據,給body頁面
BussinessServiceImpl service = new BussinessServiceImpl();
List<Category> categories = service.getAllCategory();
request.setAttribute("categories", categories);
String currentPageCount = request.getParameter("currentPageCount");
//得到所有分類的圖書,給body頁面
Page page = service.getPageData(currentPageCount);
request.setAttribute("page", page);
request.getRequestDispatcher("/client/body.jsp").forward(request,response);
JSP顯示數據
<div id="body">
<div id="category">
書籍分類 :
<br>
<c:forEach items="${categories}" var="categories">
<li>
<a href="${pageContext.request.contextPath}/ListBookServlet?category_id=${categories.id}">${categories.name}</a>
</li>
</c:forEach>
</div>
<div id="bookandpages">
<c:forEach items="${page.list}" var="book">
<div id="books">
<div id="image">
<img src="${pageContext.request.contextPath}/image/${book.image}" width="83px" height="118px">
</div>
<div id="bookinfo">
<li>
書名:${book.name}
</li>
<li>價格:${book.price}</li>
<li>作者:${book.author}</li>
</div>
</div>
<%--這里要清除浮動,十分重要!--%>
<div style="clear: both"></div>
</c:forEach>
</div>
<div id="page">
<jsp:include page="/client/page.jsp"/>
</div>
</div>
CSS代碼:
重要的是:如果div浮動都黏貼在一起了,那么在后邊多加個div,用於清除浮動效果
#body {
position: relative;
}
#category {
border: 1px solid #000;
position: absolute;
width: 300px;
height: 400px;
float: left;
left: 200px;
top: 70px;;
}
#bookandpages {
border: 1px solid #000000;
position: absolute;
width: 780px;
height: 538px;;
float: left;
left: 500px;
margin-left: 50px;
}
#books{
margin-left: 50px;
margin-top: 30px;
}
#image{
float: left;
}
#bookinfo{
float: left;
}
#page {
height: 62px;
width: 780px;
position: fixed;
margin-left: 549px;
margin-top: 477px;
text-align: center;
line-height: 50px;
}
- 效果:
按照分類顯示圖書
我們可以根據左邊的導航條來顯示相對應的分類圖書。
- Servlet代碼:
BussinessServiceImpl service = new BussinessServiceImpl();
String currentPageCount = request.getParameter("currentPageCount");
String category_id = request.getParameter("category_id");
Page page = service.getPageData(currentPageCount, category_id);
List<Category> categories = service.getAllCategory();
request.setAttribute("page", page);
request.setAttribute("categories", categories);
request.getRequestDispatcher("/client/body.jsp").forward(request,response);
效果:
如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y