servlet+jsp基礎知識四

honghu79 13年前發布 | 2K 次閱讀 Exponent CMS google應用商城 GeSHi PHP
復習:
1、 JSP
jsp引擎
 一個程序,把jsp文本文件轉換為servlet
jsp的語法
 <%......%>
 <%=表達式%>---<%out.write(表達式);%>
JSP內建對象和隱含對象
 SUN的標準規定的,由JSP引擎聲明好的變量
 out
 request
 response
application
page
config
pageContext

JSP指令
 寫在jsp文件中,指示jsp引擎在生成servlet的時候一些特性或者注意的問題
 <%@ .......%>
 <%@page import="java.sql.*,java.util.*"%>
 <%@include  file="head.jsp"  %>
 
2、cookie技術
 理解原理
 通過協議的方式來理解原理
 第一次發請求的時候
  GET /... HTTP/1.1
  .........
 響應
  HTTP/1.1 200 OK
  Set-Cookie:som_key=1000
  Set-Cookie:other_key=2000
  ...
  HTML....
 瀏覽器就會把這些信息保存在客戶端

 當下一次瀏覽器又訪問時
  GET /... HTTP/1.1
  .........
  Cookie:some_key=1000,other_key=2000

3、了解cookie api
 Cookie cookie=new Cookie("name","value");
 //cookie.setMaxAge(int s);   0  <0  >0
 Response.addCookie(cookie);
 將cookie放入到響應http包頭
 Cookie [] cookies=request.getCookies();//注意null
4、用cookie的注意事情
 a、編碼問題(特殊字符與協議的沖突)
  URLEncoder 和URLDecoder
 b、值得大小和個數
  4k,不能超過20個(一個域名20個cookie)
 c、cookie的安全問題
  cookie可以偽造
 d、路徑問題
  test/a/save_cookie.jsp
  <%
   Cookie cookie = new Cookie("some_key","1000");
   cookie.setPath("/servlet04");//servlet04下面所有應用都帶cookie
   //可以調用cookie的方法,可以設置路徑
   //cookie.setPath("/test");//下一次請求,必須是/test下面的
   //默認值,如果不指定的話,/test/a
   response.addCookie(cookie);
  %>
  test/b/get_cookie.jsp

  <%
   Cookie[] cookies = request.getCookies();
   if (cookies != null) {
    for (int i = 0; i < cookies.length; i++) {
    Cookie cookie = cookies[i];
   
     out.println("<h1>" + cookie.getName() + ": "
     + cookie.getValue() + "</h1>");
    }
   }
  %>
5、session技術

 sesssion技術要利用cookie,cookie記錄了session中的session id
 session 有一個session id,就是這個session的唯一id
 session會對服務器有壓力,session如果用不好的話,服務器就完蛋了。
 什么時候用session,什么時候用cookie
  一個用戶登陸一次之后,1年之內不用再登陸。----〉最好是用cookie,服務器不能存儲那么多的session數據
  一個用戶登陸了,半個小時之內不用再登陸。-----〉最好使用session。
 大型的門戶系統,最好是用cookie;服務器的sesion肯定有個超時的機制
6、session基本原理,例子及抓包分析
我們來看下session到底是怎么回事//////
 基本原理:服務器端先要在內存中做一個對象session,這個session有一個唯一的Id(sessionid),當客戶端來訪問來
服務器時訪,服務器響應的時候會把sessionid放到其cookies里面讓客戶端帶走。當客戶端再次訪問服務器的時候,
就可以通過sessionid來獲得對象。

 通過例子來深入了解session的工作原理
 public void service(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  /*
   * 如果當前的請求信息中有帶有存儲Session ID的Cookie,且該Session ID
   * 對應的Session對象存在,則返回該對象
   *
   * 否則,該方法將創建新的Session對象并且將新產生的Session ID 作為Cookie
   * 通過響應存到瀏覽器端
   */
  HttpSession session = request.getSession();//通過request.getSession獲得一個session對象
  System.out.println(session.getId());//獲得這個session的編號,SESSIONID
  
  
  // void setAttribute(String, Object)
  session.setAttribute("date", new Date());//存session屬性,在這里是存儲一個date對象
  
  //設置最大的不活動時間
  session.setMaxInactiveInterval(60*30);//默認半小時,   秒
  
  // 讓Session失效一般用在login的登出
  //session.invalidate();//一調用這個方法,session立馬失效
  
  // Object getAttribute(String)
  Object obj = session.getAttribute("date");//獲得session的屬性,在這里是獲得一個date對象并返回
  System.out.println(obj);
 }
 session設置的cookie默認為負數

抓包分析Session工作原理
第一次訪問
 請求信息
 GET /servlet/session HTTP/1.1
 HOST:localhost:8080
 ......
 Connection:keep-alive
 響應
 HTTP/1.1 200 OK
 Server:apache-coyote/1.1
 Set-Cookie:JSESSIONID=BCC703F862DC0CEF7CDEB6088C58E82A;Path=/servlet
 content-type:image/x-icon
 content-length:21333
 Date:...
 
 通過console顯示sessionid也為:BCC703F862DC0CEF7CDEB6088C58E82A
 
刷新頁面,實現第二次訪問
 驗證同一session:通過console查看sessionid還是BCC703F862DC0CEF7CDEB6088C58E82A
 第2次請求
 GET /servlet/session HTTP/1.1
 HOST:localhost:8080
 ......
 Connection:keep-alive
 Cookie:JSESSIONID=BCC703F862DC0CEF7CDEB6088C58E82A
 Cache-Control:max-age=0
 HTTP/1.1 200 OK
 Server:apache-coyote/1.1
 content-type:image/x-icon
 content-length:21333
 Date:...
關閉瀏覽器:重新訪問該頁面
 console上顯示:其Cookie:JSESSIONID=F7CC58E8862DC0CEDEB60882ABCC703F
 
7、session與cookie的聯系點 
session用cookie存儲session id
session可以存儲任何對象,而cookie相對來說編碼麻煩點
8、session的序列化和反序列化
接著上面的例子,重新做個實驗:
 1、第一次瀏覽session
 2、第二次瀏覽同一session
 3、第三四瀏覽器同一session
 4、關閉tomcat服務,但不關閉瀏覽器
 5、開啟tomcat服務,刷新瀏覽器頁面
 6、sessionid沒有變化
 ??為什么呢?這就是序列化和反序列化
 
 服務器關閉的時候,會把當前內存中session都存儲到服務器硬盤的一個文件里面。
當服務器啟動起來之后,再把文件里面的內容讀取到內存中。這就是序列化和反序列化session
注意:正常關閉的情況下才能序列化

 也就是說往session對象里面放置的對象必須是可序列化的!!!這也是一個重要規則
(從管理維護的角度來看,在用的系統時,如需關機維護時,需先停止服務,使其序列化,然后再關機
等計算機啟動起來之后,再啟動服務,使其反序列化)
如下例:
public class User implements java.io.Serializable {
 //============================
  //必須要加上這個才能序列化
  //必須實現了序列化接口才能最終序列化
 private String id;
 private String name;
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
9、極品問題
 a、
 ........
 response.sendRedirect("1.jsp");//執行完這句還執行下句嗎?
 System.out.println("hahahhahah");
 .....
  肯定是要執行的。
  response.sendRedirect只是在響應的時候,告訴瀏覽器響應代碼為302,其location為1.jsp而已
  整個這個方法還是要執行完的。
 b、......
    response.sendRedirect("1.jsp");//執行完這句還執行下句嗎?如果執行的話,會輸出到1.jsp中嗎?
    PrintWriter out=response.getWriter();
    out.println("hahahhahaha");
    ......
  肯定會執行,但是在真正響應的時候,只是將其轉向到1.jsp,而后面語句的html語句不會帶到1.jsp中
  會丟棄掉,只發送一個響應302轉發而已。
  
10、文件上傳和驗證碼  
驗證碼程序
 1、客戶端首先要訪問服務器,servlet就產生一個驗證碼圖片(服務器要保存驗證碼)
 2、客戶端輸入一個識別的驗證碼,發給服務器
 3、服務器要對驗證碼進行比對,如果一樣,則通過
 是不是可以通過session來保存驗證碼呢? 
 
 
前面的模式:
 a、servlet或者jsp里面包含對數據庫的連接和操作,也包含了相關的業務邏輯
 b、這種情況下,如果程序比較大的話,后期的維護工作量比較大
 怎么來解決這種問題呢?
  把對數據庫的連接和操作封裝起來,比如說弄個工具類
  
  JSP+JavaBean
 
11、JSP+JavaBean,一般不寫servlet;
   JSP負責頁面,盡量不寫servlet;
   JavaBean一堆java的類:封裝員工數據本身,封裝對數據的操作
  
12、例子:顯示員工列表,帶分頁功能
   a、一堆jsp:
     顯示結果jsp:如emp_list.jsp
     表單jsp:login_form.jsp
     操作jsp:接收用戶輸入,可以調用javabean類來訪問相關數據
    jsp中只會調用javaBean,不能寫sql語句
     
   b、java類:封裝對數據的訪問增刪改查
設計從后往前方式:從無到有
 1、數據庫
  table---emp
 2、javaBean
  a、先考慮好類名,不要考慮其方法和屬性如:Emp類
  b、類名Dao:封裝一些方法,完成對員工數據的增刪改查 EmpDao
  一般情況下,一個table對應一個類,一個Dao
 3、頁面
  emp_list.jsp
  
搭建:
 org.e2learning.domain//存放類
 org.e2learning.dao//存放操作數據庫的類

需求:
 員工列表
  -----------------------  |
  |姓名   id    salary  |
  |          |
  |       |
  |       |
  |       |
  |上一頁  2   下一頁
jsp頁面
 emp_list
瀏覽器          jsp              DAO
 | ——〉  |  方法  |    獲得第一頁的數據
 |     |————〉  |
 | <——  |     |
 | ——〉  |     |   再次發請求,必須顯示哪一頁n的請求
 |     |————〉  |方法n
 |  <—— |     |
 |     |     |
 |     |     |隱含存在一個返回一個總的頁數
 |     |     |
 |     |     |

13、構建一個工具類,通過properties類來將數據庫的連接導入
dbo.properties
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.0.20:1521:test1
user=scott
password=tiger
thin:
 連接數據庫的這一端不用oracle的任何組件

package org.whatisjava.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class ConnectionUtils {
 private static String driver;
 private static String url;
 private static String user;
 private static String password;
// Class.forName("");
// Connection con=DriverManager.getConnection("","","");
// //一般情況下,我們會把數據庫等的驅動,用戶名密碼等寫到一個配置文件中
// //數據結構比較簡單用屬性文件,比較復雜的話用xml
 static {
  //靜態塊初始化這些屬性
  try {
   Properties props = new Properties();
   //Properites類
   
   //props.load(new FileInputStream("c:/db.properties"));//難點
   //在這里開發,在這里運行。以后開發的時候,這里開發,放到其它服務器上安裝
   //在這里不要用文件流了。按照下面的方式
   //放到src下面的非java文件都會copy到classes目錄下
   //java虛擬機可以提供一種機制,可以把classes下面的所有文件裝載。如下
   //像以前學的hibernate
   //按照java反射
   props.load(ConnectionUtils.class.getClassLoader()
     .getResourceAsStream("org/whatisjava/dao/db.properties"));
   driver = props.getProperty("driver");
   url = props.getProperty("url");
   user = props.getProperty("user");
   password = props.getProperty("password");
   Class.forName(driver);
  } catch (Exception e) {
  }
 }
 public static Connection getConnection() throws SQLException {
  Connection con = DriverManager.getConnection(url, user, password);
  return con;
 }
}
 
14、我們接下來看下EmpDao是怎么寫的?
package org.whatisjava.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.whatisjava.domain.Emp;
public class EmpDao {
 private static final String getEmpList =
  "select empno, ename, sal from(select empno,ename,sal, " +
  "rownum num from emp where rownum<?) where num>=?";
 //SQL里面的分頁技術
 //select empno,enmae,sal from emp where rownum<=20
 //rownum使用<號是沒有問題的。使用〉號會出現問題
 //rownum是數據先查詢出來,再進行的rownum編號
 //
 //select empno,ename,sal from(select empno,ename,sal,rownum num from emp where rownum<?) where num>=?
 
 //問題:我要對工資進行排序之后再分頁
 //要搞清楚先order by還是先rownum,
 //也可以用hibernate的分頁查詢
 
 public List<Emp> getEmpList(int page, int rowsPerPage) throws SQLException {
  //返回參數
  //首先要獲得連接,用剛才寫得類
  Connection con = ConnectionUtils.getConnection();
  PreparedStatement stmt = con.prepareStatement(getEmpList);
  //計算公式可以按照實際的來進行計算
  int start = (page - 1) * rowsPerPage + 1;
  int end = start + rowsPerPage;

  //
  stmt.setInt(1, end);
  stmt.setInt(2, start);
  ResultSet rs = stmt.executeQuery();
  ArrayList empList = new ArrayList();
  while (rs.next()) {
   Emp emp = new Emp();
   emp.setId(rs.getInt(1));
   emp.setName(rs.getString(2));
   emp.setSalary(rs.getDouble(3));
   empList.add(emp);
  }
  return empList;
 }

 public int getTotalPages(int rowsPerPage) {
  //每一頁有多少行的情況下,返回多少頁 
  return 0;
 }
}

15、Oracle中ROWNUM偽列
重點: rownum是在結果集上增加的一列
**********************************************************************************************
ORACLE 中ROWNUM用法總結!(來源于網絡)
對于 Oracle 的 rownum 問題,很多資料都說不支持>,>=,=,between...and,只能用以上符號(<、<=、!=),
并非說用>,>=,=,between..and 時會提示SQL語法錯誤,而是經常是查不出一條記錄來,還會出現似乎是莫
名其妙的結果來,其實您只要理解好了這個 rownum 偽列的意義就不應該感到驚奇,同樣是偽列,rownum
與 rowid 可有些不一樣,下面以例子說明
假設某個表 t1(c1) 有 20 條記錄
如果用 select rownum,c1 from t1 where rownum < 10, 只要是用小于號,查出來的結果很容易地與一般
理解在概念上能達成一致,應該不會有任何疑問的。
可如果用 select rownum,c1 from t1 where rownum > 10 (如果寫下這樣的查詢語句,這時候在您的頭腦
中應該是想得到表中后面10條記錄),你就會發現,顯示出來的結果要讓您失望了,也許您還會懷疑是不誰
刪了一些記錄,然后查看記錄數,仍然是 20 條啊?那問題是出在哪呢?
先好好理解 rownum 的意義吧。因為ROWNUM是對結果集加的一個偽列,即先查到結果集之后再加上去的一
個列 (強調:先要有結果集)。簡單的說 rownum 是對符合條件結果的序列號。它總是從1開始排起的。所以
你選出的結果不可能沒有1,而有其他大于1的值。所以您沒辦法期望得到下面的結果集:
11 aaaaaaaa
12 bbbbbbb
13 ccccccc
.................
rownum >10 沒有記錄,因為第一條不滿足去掉的話,第二條的ROWNUM又成了1,所以永遠沒有滿足條件的記錄。
或者可以這樣理解:
ROWNUM是一個序列,是oracle數據庫從數據文件或緩沖區中讀取數據的順序。它取得第一條記錄則rownum值為1,
第二條為2,依次類推。如果你用>,>=,=,between...and這些條件,因為從緩沖區或數據文件中得到的第一條記
錄的rownum為1,則被刪除,接著取下條,可是它的rownum還是1,又被刪除,依次類推,便沒有了數據。
有了以上從不同方面建立起來的對 rownum 的概念,那我們可以來認識使用 rownum 的幾種現象
select rownum,c1 from t1 where rownum != 10 為何是返回前9條數據呢?它與 select rownum,c1 from
tablename where rownum < 10 返回的結果集是一樣的呢?
因為是在查詢到結果集后,顯示完第 9 條記錄后,之后的記錄也都是 != 10,或者 >=10,所以只顯示前面9條記錄。
也可以這樣理解,rownum 為9后的記錄的 rownum為10,因條件為 !=10,所以去掉,其后記錄補上,rownum又是10,
也去掉,如果下去也就只會顯示前面9條記錄了
為什么 rownum >1 時查不到一條記錄,而 rownum >0 或 rownum >=1 卻總顯示所以的記錄
因為 rownum 是在查詢到的結果集后加上去的,它總是從1開始
為什么 between 1 and 10 或者 between 0 and 10 能查到結果,而用 between 2 and 10 卻得不到結果
原因同上一樣,因為 rownum 總是從 1 開始
從上可以看出,任何時候想把 rownum = 1 這條記錄拋棄是不對的,它在結果集中是不可或缺的,少了rownum=1
就像空中樓閣一般不能存在,所以你的 rownum 條件要包含到 1
但如果就是想要用 rownum > 10 這種條件的話話就要用嵌套語句,把 rownum 先生成,然后對他進行查詢。
select *
from (selet rownum as rn,t1.* from a where ...)
where rn >10
一般代碼中對結果集進行分頁就是這么干的。
另外:rowid 與 rownum 雖都被稱為偽列,但它們的存在方式是不一樣的,rowid 可以說是物理存在的,表示記錄在
表空間中的唯一位置ID,在DB中唯一。只要記錄沒被搬動過,rowid是不變的。rowid 相對于表來說又像表中的一般列,
所以以 rowid 為條件就不會有 rownum那些情況發生。
另外還要注意:rownum不能以任何基表的名稱作為前綴。

根據以上理解:
 select  empno,ename,sal from emp where rownum<=20 and rownum>11;//此語句有問題
 select empno,ename,sal from(select  empno,ename,sal,rownum num from emp where rownnum<20)
  where num>11;
 

 
 本文由用戶 honghu79 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!