iT邦幫忙

1

【左京淳的JAVA WEB學習筆記】第十一章 顯示列表、圖片、商品細節

顯示列表

訪問案例網站時默認調用index.jsp,在此頁面設定轉向MainSvl。
http://localhost:8080/BookShop
新建index.jsp

<%request.getRequestDispatcher("/MainSvl").forward(request, response);%>

新建MainSvl(控制層)

@WebServlet("/MainSvl")
public class MainSvl extends HttpServlet {
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      BookBiz biz = new BookBiz();
      try {
        List<TBook> books = biz.getAllBooks();
        request.setAttribute("books", books);
        request.getRequestDispatcher("/WEB-INF/main/main.jsp").forward(request, response);
      }catch(Exception e){
        request.setAttribute("msg", "網路異常,請跟網站管理員聯繫。");
        request.getRequestDispatcher("/error.jsp").forward(request, response);
      }
    }
  (餘略)
}

新建BookBiz(服務層)

public class BookBiz {
  public List<TBook> getAllBooks() throws Exception{
    IBookDao dao = new BookDaoMysql();
    try {
      return dao.getAllBooks();
    }finally{
      dao.closeConnection();
    }
  }
}

新建BookDaoMysql(持久層:負責DB訪問)

public class BookDaoMysql extends BaseDao{
  public List<TBook> getAllBooks() throws Exception{
    List<TBook> books = null;
    String sql = "select isbn,name,press,price,pdate from tbook order by isbn";
    //從BaseDao取得connection = DriverManager.getConnection(dbInfo.getUrl(), dbInfo.getUname(), dbInfo.getPwd());
    this.openConnection(); 
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ResultSet rs = ps.executeQuery();
    if(rs != null) {
      books = new ArrayList<TBook>();
      //當rs裡面還有下一件資料時
      while(rs.next()) {
        //把取出的資料都保存到TBook物件中
        TBook bk = new TBook();
        bk.setBname(rs.getString("bname"));
        bk.setIsbn(rs.getString("isbn"));
        bk.setPdate(rs.getString("Pdate"));
        bk.setPress(rs.getString("press"));
        bk.setPrice(rs.getString("price"));
        //將TBook物件加入列表
        books.add(bk);
      }
    }
    return books;

記憶重點

  • 使用DriverManager取得connection
  • 使用connection內的prepareStatement(sql)裝載sql文
  • 使用connection內的executeQuery()發送sql文並取得resultSet
  • 使用迴圈將resultSet的資料保存至javaBean物件中,並將物件加入列表。
  • 由服務層呼叫closeConnection()

在JSP頁面顯示列表

使用JSTL的<c:forEath>標籤即可簡單取出列表
var為保存資料用的變數名(可自取),items為資料來源列表的名字。

<table border="1" width=100%>
  <c:forEach var="bk" items="${books }">
    <tr>
      <td rowspan=3>
        <img width=100 height=100 src="<%=basePath%>BookPicSvl?isbn=${bk.isbn }">
      </td>
      <td colspan=2 align=center style="color:red">
        <a href="<%=basePath%>BookDetailSvl?isbn=${bk.isbn }">${bk.bname }</a>
      </td>
    </tr>
    <tr><td>商品價格</td><td>${bk.price }</td></tr>
    <tr><td>出版社</td><td>${bk.press }</td></tr>
  </c:forEach>
</table>

在這個案例中,使用了標籤和<%=basePath%>,這兩個部分是常用的代碼,可以抽取出來放在base.jsp頁面中。
使用以下指令進行靜態引用

<%@include file ="/WEB-INF/base.jsp" %>

另外一種引用方法為動態引用,不過動態引用就無法取得base頁面裡的變數。得要在頁面靜態編譯時便建立好變數才能引用。
<jsp:include page="/WEB-INF/base.jsp"></jsp:include>

c標籤也有引用頁面的import方法

<c:import url="base.jsp"></c:import>

不過這樣做的話就必須先加載以下標籤庫

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

在本案例中標籤庫的加載交給base頁面做了,所以選擇include file指令更方便。

顯示圖片

當用戶進行下載圖片等比較耗時的處理,可以使用NIO(非阻塞流)技術(一種異步處理技術)。

新建BookPicSvl並開啟支援異步模式。

@WebServlet(urlPatterns="/BookPicSvl",asyncSupported=true)
public class BookPicSvl extends HttpServlet {
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
      String isbn = request.getParameter("isbn");
      if(isbn == null) {
        throw new RuntimeException("isbn不能為空");
      }
      BookBiz biz = new BookBiz();
      try {
        byte[] pic = biz.getBookPic(isbn);
        if(pic != null) {
          AsyncContext acontext = request.startAsync();
          ServletOutputStream out = acontext.getResponse().getOutputStream();
          out.setWriteListener(new MyPicWriter(out,acontext,pic));
        }
      }catch(Exception e){
        Log.logger.error(e.getMessage(), e);
        request.setAttribute("msg", "網路異常,請檢查。");
        request.getRequestDispatcher("/error.jsp").forward(request, response);
      }
    }
(餘略)
}

記憶重點

  • 使用request.startAsync(),取得AsyncContext物件。
  • 使用acontext.getResponse().getOutputStream(),取得ServletOutputStream物件。
  • 使用out.setWriteListener(new MyPicWriter(out,acontext,pic)),將剛取得的AsyncContext物件、ServletOutputStream物件及欲處理資料都丟進WriteListener進行處理。

新建MyPicWriter

public class MyPicWriter implements WriteListener {
  private ServletOutputStream out;
  private AsyncContext ac;
  private byte[] pic;
  
  public MyPicWriter(ServletOutputStream out,AsyncContext ac,byte[] pic) {
    this.ac = ac;
    this.out = out;
    this.pic = pic;
  }
  
  @Override
  public void onError(Throwable t) {
    t.printStackTrace();
  }

  @Override
  public void onWritePossible() throws IOException {
    try {
      if(pic != null && out.isReady()) {
        out.write(pic);
        //out.flush(); --此處不能使用flush
        out.close();
      }
    }catch(Exception e){
      e.printStackTrace();
    }finally {
      ac.complete();
    }
  }
}

記憶重點

當圖片資料不為空,且輸出流已經準備好時,使用write(pic)將資料寫出後關閉輸出流。最後關閉異步處理物件。

服務層的動作

public class BookBiz {
  public byte[] getBookPic(String isbn) throws Exception{
    IBookDao dao = new BookDaoMysql();
    try {
      return dao.getBookPic(isbn);
    }finally{
      dao.closeConnection();
    }
  }
}

DAO層的動作

public class BookDaoMysql extends BaseDao{
  public byte[] getBookPic(String isbn) throws Exception{
    byte[] pic = null;
    String sql = "select pic from tbook where isbn = ?";
    this.openConnection(); 
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, isbn);
    ResultSet rs = ps.executeQuery();
    if(rs != null) {
      while(rs.next()) {
        pic = rs.getBytes("pic");
        break;
      }
    }
    rs.close();
    ps.close();
    return pic;
  }
}

利用特定條件取得資料時,SQL文內會放置"?"佔位符。跟取得全部列表時不一樣。
當SQL文內有佔位符時,依照其數量,使用PreparedStatement物件的setString(index, value)將值代入;
例如ps.setString(1, isbn);,意思是將isbn變數內的值代入第一個佔位符內。

使用while迴圈來確保至少有一行資料,然後將其讀取。
疑問:之前的教材中沒有關閉rs和ps,不知道為什麼。

jsp頁面的設置

在main.jsp和BookDetail.jsp中,使用標籤來請求圖片的數據流。

<img src="<%=basePath%>BookPicSvl?isbn=${bk.isbn}"/>

商品細節頁面

在首頁列表中點擊任一商品之後,跳轉到商品細節頁面。連結長得像下面這樣
http://localhost:8080/BookShop/BookDetailSvl?isbn=is001

新建BookDetailSvl

@WebServlet("/BookDetailSvl")
public class BookDetailSvl extends HttpServlet {
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      String isbn = request.getParameter("isbn");
      if(isbn == null) {
        throw new RuntimeException("isbn不能為空");
      }      
      BookBiz biz = new BookBiz();
      try {
        TBook book = biz.getBookDetail(isbn);
        request.setAttribute("book", book);
        request.getRequestDispatcher("/WEB-INF/main/BookDetail.jsp").forward(request, response);
      }catch(Exception e){
        Log.logger.error(e.getMessage(),e);
        request.setAttribute("msg", "網路異常,請跟網站管理員聯繫。");
        request.getRequestDispatcher("/error.jsp").forward(request, response);
      }
    }
}

記憶重點

  • Servlet負責呼叫service層取得資料。
  • 將資料存進request物件裡。
  • 帶著資料,轉發到目標頁面。

服務層
新增getBookDetail(isbn)方法

  public TBook getBookDetail(String isbn) throws Exception{
    IBookDao dao = new BookDaoMysql();
    try {
      return dao.getBookDetail(isbn);
    }finally{
      dao.closeConnection();
    }
  }

DAO層
新增getBookDetail(String isbn)方法

  public TBook getBookDetail(String isbn) throws Exception{
    TBook book = null;
    String sql = "select isbn,bname,press,price,pdate from tbook where isbn = ?";
    this.openConnection(); 
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, isbn);
    ResultSet rs = ps.executeQuery();
    if(rs != null) {
      while(rs.next()) {
        book.setBname(rs.getString("bname"));
        book.setIsbn(rs.getString("isbn"));
        book.setPdate(rs.getString("Pdate"));
        book.setPress(rs.getString("press"));
        book.setPrice(rs.getString("price"));
        break;
      }
    }
    return book;
  }

BookDetail.jsp頁面

(前略)
<table border="1" width=100%>
  <tr>
    <td rowspan=3>
      <img width=100 height=100 src="<%=basePath%>BookPicSvl?isbn=${book.isbn }">
    </td>
    <td colspan=2 align=center style="color:red">
      ${book.bname }
    </td>
  </tr>
  <tr><td>商品價格</td><td>${book.price }</td></tr>
  <tr><td>出版社</td><td>${book.press }</td></tr>
  <tr><td height=300 colspan=3>圖書簡介</td></tr>
  <tr><td colspan=3 align="center">
    <a href="<%=basePath%>user/ShopCarAddSvl?isbn=${book.isbn }">加入購物車</a>&nbsp;
    <a href="<%=basePath%>MainSvl">返回</a>
    </td>
  </tr>
</table>

尚未有邦友留言

立即登入留言