显示列表
访问案例网站时默认调用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> <a href="<%=basePath%>MainSvl">返回</a> </td> </tr></table>