1. 程式人生 > >關於javaweb的“網上書店”專案知識點總結(一)

關於javaweb的“網上書店”專案知識點總結(一)

這是本人第一次寫部落格,主要記錄用java作專案的一些技術點,方便自己日後回憶。
本次java專案是傳智播客的“網上書店”專案,用的架構是 jsp + servlet +javabean,這個專案是本人的第一個專案。
以下是自己在做專案的過程中自己覺得需要注意的知識點:
一、request.getParameter 和 request.getAttribute是這兩個方法有區別的:
前者是jsp頁面中要有的引數,後者是request域中的自己之前存入的鍵值對此時再拿出來用,比如:
在jsp頁面中出現

<a href="${pageContext.request.contextPath }/order?method=confirmation&oid=${order.oid}">確認收貨</a>

,則因為出現了oid,獲取引數值
時是用request.getParameter。${pageContext.request.contextPath }在jsp中表示本專案的意思,order是OrderServlet這個servlet的訪問路徑(建立servlet時的URL),問號後面全部是引數,引數採用鍵值對的方式,各個引數之間使用&分隔。

二、隱藏域是用來傳遞引數給servlet的,比如:
<input type="hidden" name="bid" value="${book.bid }"/>
name和value是鍵值對

三、jsp中的超連結<a>

標籤既可以直接轉發到jsp頁面,也可以直接發到servlet。所以有時候需要的引數不夠用時,可以先超連結到servlet,用這個servlet把資料存到request域,然後再跳轉到下一個頁面,下一個頁面就可以使用剛剛存入request域中的鍵值對引數了。

四、一個表單有2個submit時,直接就在submit處的name 和value處寫出方法名即可,而在form的action只寫到servlet即可,比如:

<form style="margin:20px;" id="form" action="${pageContext.request.contextPath }/book" method="post">
<input type="submit" name="method" value="del" onclick="return confirm('是否真要刪除該圖書?');"/>
 <input type="submit" name="method" value="mod"/>
 其中name 和value是鍵值對,del 和 mod 分別是兩個方法;action只需寫到servlet即可。

五、表單的提交方式為post時(即<form style="margin:20px;" id="form" action="${pageContext.request.contextPath }/book" method="post">),一定要加隱藏域用來寫方法名(寫方法名:<input type="hidden" name="method" value="addCategory" />)和傳引數(傳引數:<input type="hidden" name="image" value="${book.image }" />)。當表單的提交方式為get時,可以直接就寫方法名和傳引數一步到位(比如:<a href="${pageContext.request.contextPath }/order?method=delivery&oid=${order.oid}">發貨</a>)。

六、jsp中凡是變數都要加 ${ } ,括號裡面寫變數和表示式。

七、BeanUtils.populate(javabean , map)方法中map中存在的鍵值對則可以封裝到javabean中,map中沒有的鍵值對則封裝不到 javabean中,那麼封裝不到的剩餘屬性可以用javabean中的setXXX( )方法來設定。jsp中的name 要和 javabean的屬性名字一樣才能使用 BeanUtils.populate(javabean , map)進行封裝(先用map=request.getParametermap( ) 方法得到map,然後再封裝)

八、當sql語句為 String countSql = "select count(*) from book where isdel=0;"; 時,使用的ResultSetHandler為ScalarHandler,即long totalCountL = (Long) qr.query(countSql, new ScalarHandler());
當sql語句為多表聯合查詢時(此時結果表一條記錄很長很長),有2種解決辦法:一是新建一個javabean與該記錄對應上,然後用beanhandler處理;
二是使用MapListHandler,即:
String sql2 = “select * from orderitem o ,book b where o.bid=b.bid and o.oid=?;”;
List<Map<String,Object>> obList = qr.query(sql2, new MapListHandler(), order.getOid());
此時每條記錄就是一個map,多條記錄就是多個map,用List把很多個map裝起來。一條記錄很長時可分割成多個javabean來封裝,比如:

for(Order order : oList) {
				String sql2 = "select * from orderitem o ,book b where o.bid=b.bid and o.oid=?;";
				List<Map<String,Object>> obList = qr.query(sql2, new MapListHandler(), order.getOid());
				for(Map<String, Object> map : obList) { //一個map是一條很長的記錄
					OrderItem oi = new OrderItem();
					BeanUtils.populate(oi, map);   //一個map的前半部分分割到OrderItem 這個javabean封裝
					
					Book book = new Book();
					BeanUtils.populate(book, map);  //該map的後半部分分割到Book 這個javabean封裝
					
					oi.setBook(book);
					oi.setOrder(order);
					order.getOilist().add(oi);
				}
}

九、關於絕對路徑:重定向(response.sendRedirect( ))和在瀏覽器中輸入網址時二者都要加上專案名;而使用轉發時(request.getDispacher( uri ).forward( ))不用加專案名(即uri不用加專案名)。因為重定向可以定位到網路上任何網站和資源,而轉發只能在本專案中進行轉發。

十、jsp中一定要有page指令(自己老是忘記加上),其實 把request 域 或session域裡面的資料動態地、有選擇性地新增到jsp 頁面時只需要用到<c:forEach var=" " items=" "><c:if test=""></c:if> 這兩個jstl就可以了,就這兩個jstl語句基本可以實現絕大部分需求了。

十一、步驟:先建庫建表 → 再寫實體javabean → 再servlet → service → dao/daoImple;其中servlet可以先寫個通用基類BaseServlet(該類繼承HttpServlet),然後各個功能模組都繼承通用基類BaseServlet,要保持一個功能模組對應一個servlet就夠了,別對應多個servlet;
其中servlet 、service 直接就是類,而dao層是要先寫個介面再寫個實現類實現該dao介面。

十二、呼叫過程:http請求 → 到達後臺servlet → servlet 呼叫 service → service呼叫 dao → dao操作資料庫得到結果集 → dao將結果集返回給service → service返回給servlet →servlet再把資料存到request域或session域中 → jsp 從request域或session域拿出資料展示給使用者

十三、跳轉到下一個頁面有兩種方式:轉發 和 重定向,其中轉發用的更多

十四、如果用BaseServlet作為通用基類(該類繼承HttpServlet),後面各個功能模組都繼承BaseServlet,那麼BaseServlet作為通用基類定義如下:

public class BaseServlet extends HttpServlet{
	public void service(HttpServletRequest req , HttpServletResponse resp)
	throws ServletException,IOException {
		
		req.setCharacterEncoding("UTF-8");
		resp.setContentType("text/html;charset=UTF-8");
		
		String methodName = req.getParameter("method");
		if(methodName == null || methodName.trim().isEmpty()) {
			throw new RuntimeException("please input a  method !");
		}
		Class clazz = this.getClass();
		Method method = null;
		try {
			method = clazz.getMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
		} catch (Exception e) {	
			throw new RuntimeException("no such method !");
		}
		String uri = null;
		try {
			uri = (String) method.invoke(this, req,resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			if(uri != null && !uri.trim().isEmpty()) {
				req.getRequestDispatcher(uri).forward(req, resp);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

它的具體過程:http請求→呼叫HttpServlet的第一個service方法→該HttpServlet的第一個service方法把ServletRequest強轉成HttpServletRequest,然後第一個service方法呼叫第二個service方法並把剛剛強轉得到的HttpServletRequest作為函式引數傳給第二個service方法→而通用類 BaseServlet重寫了第二個service方法,所以第一個service方法呼叫了通用類 BaseServlet的重寫service方法。BaseServlet的重寫service方法中使用了反射技術,使用反射技術可以寫出很通用的程式碼。

十五、整個專案分成兩個系統,分別是前臺系統和後臺系統;前臺系統是給使用者看的,前臺系統查詢資料庫,再將查詢結果放到jsp中展示商品給使用者購買;後臺系統是給賣家用的,賣家在後臺系統新增分類和新增圖書,結果就是把資料新增到資料庫中,前臺系統查詢資料庫得到更新後的資料,所以前臺和後臺的互動就是通過資料庫。

十六、刪除資料庫中的記錄有兩種辦法:真刪除和假刪除。真刪除就是使用delete的sql語句真正把該記錄從資料庫中刪掉;假刪除是邏輯刪除,就是在表中增加一個欄位isdel,當isdel=0時我們認為記錄是存在於資料庫的,當isdel=1時我們認為該記錄已被刪除(實際上該記錄仍然存在於資料庫中,只是isdel欄位的值變了,使用的sql語句是update而不是del)。

十七、從jsp頁面往servlet傳資料一般是傳 ID 值,servlet根據ID值去查詢資料庫,查詢返回給jsp一般是返回一個javabean或一個List<javabean>(也就是把javabean或List存到request域或session域中)。

十八、設計思想:設計方法時方法的形參一般是類的形式最好(往往是javabean,比如:
public void updateBook(Book book)), 後面寫sql語句查詢資料庫需要引數時用javabean的getXXX()方法就行(比如book.getBid())。體現面向物件的思想。

十九、設計思想:設計javabean類時屬性也可以是一個類,但是後面寫sql語句查詢資料庫需要引數時要用兩次javabean的getXXX()方法,體現面向物件的思想。比如:

public class Order {	
	private String oid;
	private double total;
	private String ordertime;
	private int state;
	private String address;
	private User user; //該屬性是一個類,這也是一個javabean,後面寫sql語句時用兩次getXXX()方法即可
	
private List<OrderItem> oilist = new ArrayList<OrderItem>();

public String getOid() {
	return oid;
}

public void setOid(String oid) {
	this.oid = oid;
}

public double getTotal() {
	return total;
}

public void setTotal(double total) {
	this.total = total;
}

public String getOrdertime() {
	return ordertime;
}

public void setOrdertime(String ordertime) {
	this.ordertime = ordertime;
}

public int getState() {
	return state;
}

public void setState(int state) {
	this.state = state;
}

public String getAddress() {
	return address;
}

public void setAddress(String address) {
	this.address = address;
}

public User getUser() {
	return user;
}

public void setUser(User user) {
	this.user = user;
}

public List<OrderItem> getOilist() {
	return oilist;
}

public void setOilist(List<OrderItem> oilist) {
	this.oilist = oilist;
}

}
查詢資料庫時用兩次getXXX()方法,比如:

public void saveOrder(Connection conn, Order order) {
		QueryRunner qr = new QueryRunner();
		String sql = "insert into orders values(?,?,?,?,?,?);";
		Object[] params = {order.getOid(),order.getTotal(),order.getOrdertime(),
				order.getState(),order.getAddress(),*order.getUser().getUid()*};//用了兩次getXXX()方法,第一次	
		try {                                                                   //getXXX()方法是得到user,第二次getXXX()
			qr.update(conn, sql, params);                                       //方法得到user物件中的uid屬性
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

二十、jsp頁面中凡是涉及到地址的都要在前面加上${pageContext.request.contextPath },也就是包括超連結<a href="">中的href、圖片標籤的<img src=>中的src屬性、form表單的<form action=>中的action屬性三者都要在前面加上:

${pageContext.request.contextPath }

二十一、src資料夾下至少要建5個包:vo(存放javabean類)、servlet(存放所有servlet類)、service(存放所有service類)、dao(存放所有dao介面和dao介面的實現類daoImple)、util(存放所有工具類);而且src資料夾下還要有c3p0.xml的配置檔案(如果用到c3p0的jar包的話)。

二十二、關於分頁查詢,分頁查詢要建一個PageBean的javabean,定義如下:

public class PageBean<T> {   //這是一個泛型類,以適用於任何類
	private int pageCode;    //當前頁
	//private int totalPage;   //總頁數可以計算出來,所以不需要這個屬性
	private int totalCount;   //總記錄數
	private int pageSize;     //每頁存放的記錄數
	private List<T> beanList;     
	private String url;
	public int getPageCode() {
		return pageCode;
	}
	public void setPageCode(int pageCode) {
		this.pageCode = pageCode;
	}
	public int getTotalPage() {
		int totalPage = totalCount / pageSize;
		if(totalCount % pageSize == 0) {
			return totalPage;
		} else {
			return totalPage + 1;
		}
	}
	/*public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}*/
	public int getTotalCount() {
		return totalCount;
	}
	public void setTotalCount(int totalCount) {
		this.totalCount = totalCount;
	}
	public int getPageSize() {
		return pageSize;
	}
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}
	public List<T> getBeanList() {
		return beanList;
	}
	public void setBeanList(List<T> beanList) {
		this.beanList = beanList;
	}
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
}

在servlet中的方法:

public String findByPage(HttpServletRequest request, HttpServletResponse response) {
		int pageCode = getPageCode(request); //得到當前頁函式
		int pageSize = 3; //每頁存放3條記錄
		BookService bs = new BookService();
		PageBean<Book> page = bs.findByPage(pageCode,pageSize);
		request.setAttribute("page", page);
		
		return "/adminjsps/admin/book/list.jsp";
	}
	
public int getPageCode(HttpServletRequest request) {
		String pc = request.getParameter("pc"); //從jsp中獲取當前頁
		if(pc == null || pc.trim().isEmpty()) {
			return 1;  //返回第一頁
		}
		return Integer.parseInt(pc);
}

用sql語句查詢資料庫時寫法:

public PageBean<Book> findByPage(int pageCode, int pageSize) {
		PageBean<Book> page = new PageBean<Book>();
		page.setPageCode(pageCode);
		page.setPageSize(pageSize);
		
		String countSql = "select count(*) from book where isdel=0;";
		QueryRunner qr = new QueryRunner(MyJdbcUtil.getDataSource());
		try {
			long totalCountL = (Long) qr.query(countSql, new ScalarHandler());
			int totalCount = (int) totalCountL;
			page.setTotalCount(totalCount);
			
			String limitSql = "select * from book where isdel=0 limit ?,?";
			List<Book> beanList = qr.query(limitSql, new BeanListHandler<Book>(Book.class), (pageCode-1)*pageSize,pageSize);
			page.setBeanList(beanList);  //存的是當前頁的資訊
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		return page;
}

在jsp頁面中:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>圖書分類</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<meta http-equiv="content-type" content="text/html;charset=utf-8">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
<style type="text/css">
	body {
		font-size: 10pt;
		background: rgb(254,238,189);
	}
	.icon {
		margin:10px;
		border: solid 2px gray;
		width: 160px;
		height: 190px;
		text-align: center;
		float: left;
	}
</style>
  </head>
  
  <body>
  <c:forEach var="book" items="${page.beanList }">
	   <div class="icon">
	    <a href="${pageContext.request.contextPath }/book?method=bookDesc&bid=${book.bid}"><img src="${pageContext.request.contextPath }/${book.image}" width="120" height="150" border="0"/></a>
	      <br/>
	   	<a href="${pageContext.request.contextPath }/book?method=bookDesc&bid=${book.bid}">${book.bname }</a>
	  </div>
   </c:forEach>
  <br>
  <div style="clear : left">
  	第${page.pageCode }頁/共${page.totalPage }頁
  	<a href="${pageContext.request.contextPath }/book?method=findByPage&pc=1">首頁</a>//所以servlet要獲取pc的引數值,pc值就是當前頁的意思。
  	<c:if test="${page.pageCode > 1 }">
  		<a href="${pageContext.request.contextPath }/book?method=findByPage&pc=${page.pageCode-1}">上一頁</a>
  	</c:if>      	
  	<c:if test="${page.pageCode < page.totalPage }">
  		<a href="${pageContext.request.contextPath }/book?method=findByPage&pc=${page.pageCode+1}">下一頁</a>
  	</c:if>      	
  	<a href="${pageContext.request.contextPath }/book?method=findByPage&pc=${page.totalPage}">尾頁</a>
  </div>      
  </body> 

二十三、jsp檔案、圖片檔案、css和.js檔案、MANIFEST.MF和 *.sql 檔案都要放在WebRoot根目錄下,要建立資料夾來分別裝這些檔案,以方便分類。在WebRoot目錄下有個WEB-INF資料夾,這個資料夾下有個lib資料夾和web.xml檔案;lib資料夾用來存放這個專案所需要的第三方jar包(我們所匯入的第三方jar包就放在這裡,直接把這些包複製貼上到lib下就行),web.xml檔案主要描述servlet和歡迎檔案的配置資訊。

二十四、javabean類中只要有getXxxx()方法,就算在這個bean中沒有明顯地定義寫出xxxx屬性,那麼這個屬性xxxx也是存在的,在jsp頁面中可以直接使用${ javabean.xxxx }。這是因為它符合規範:get後面第一個字母大寫,且有返回值,那麼xxxx就是一個屬性,可以直接使用這個屬性。

二十五、在瀏覽器中只輸入到專案名為止然後看到的頁面是什麼取決於在web.xml檔案中的index.jsp這個的配置。本專案在瀏覽器中只輸入到專案名為止看到的頁面是index.jsp檔案。

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

二十六、前臺系統的jsp等檔案和後臺系統的jsp等檔案都是放在WebRoot根目錄下,前臺和後臺共用servlet、service、dao/daoImple這些類,前臺和後臺只是往這些類增加不同的方法而已。

二十七、jsp中的<select> 標籤和<option>標籤是合一起的,即<select> 標籤的name和<option>標籤的value是一對鍵值對。

二十八、form表單一定是直接提交到servlet,而超連結即可以到servlet,也可以到jsp頁面。

二十九、自己做的網頁如果想和網路上的其他任何網站發生連線必須要用重定向發給瀏覽器,不能用轉發,因為轉發只能在本專案內轉發,而重定向可以定位到任何頁面。

三十、get請求在jsp中的url寫法:直接就是${pageContext.request.contextPath }/servlet?method=methodName&引數名=引數值&引數名=引數值...
而post請求必須加隱藏域,url寫法:${pageContext.request.contextPath }/servlet//寫到servlet即可
再加上<input type="hidden" name="method" value="methodName"/> //這個是寫方法的
傳引數還要加:<input type="hidden" name="引數名" value="引數值"/>
如果要傳多個引數,則繼續加:<input type="hidden" name="引數名" value="引數值"/>