在学习Servlet的中,在实现编辑Hero功能的servlet时,需要根据浏览器提交的id,通过HeroDAO找到对应的Hero,然后在servlet中组织html显示出来。 可以看到这个Servlet不仅要准备数据,还要准备html。 尤其是准备html,可读性非常差,维护起来也很麻烦
int id = Integer.parseInt(request.getParameter("id")); Hero hero = new HeroDAO().get(id); StringBuffer format = new StringBuffer(); response.setContentType("text/html; charset=UTF-8"); format.append("<!DOCTYPE html>"); format.append("<form action='updateHero' method='post'>"); format.append("名字 : <input type='text' name='name' value='%s' > <br>"); format.append("血量 : <input type='text' name='hp' value='%f' > <br>"); format.append("伤害: <input type='text' name='damage' value='%d' > <br>"); format.append("<input type='hidden' name='id' value='%d'>"); format.append("<input type='submit' value='更新'>"); format.append("</form>"); String html = String.format(format.toString(), hero.getName(), hero.getHp(), hero.getDamage(), hero.getId()); response.getWriter().write(html);因为在Servlet中编写html有这样的短板,所以索性直接在JSP中开发编辑Hero这个功能 ==这时会发现,虽然编写html方便了,但是写java代码不如在Servlet中那么方便 ==
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*,bean.*,java.sql.*"%> <% int id = Integer.parseInt(request.getParameter("id")); Hero hero = null; try { Class.forName("com.mysql.jdbc.Driver"); Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "admin"); Statement s = c.createStatement(); String sql = "select * from hero where id = " + id; ResultSet rs = s.executeQuery(sql); if (rs.next()) { hero = new Hero(); String name = rs.getString(2); float hp = rs.getFloat("hp"); int damage = rs.getInt(4); hero.name = name; hero.hp = hp; hero.damage = damage; hero.id = id; } s.close(); c.close(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } %> <form action='updateHero' method='post'> 名字 : <input type='text' name='name' value='<%=hero.getName()%>'> <br> 血量 :<input type='text' name='hp' value='<%=hero.getHp()%>'> <br> 伤害: <input type='text' name='damage' value='<%=hero.getDamage()%>'> <br> <input type='hidden' name='id' value='<%=hero.getId()%>'> <input type='submit' value='更新'> </form>既然Servlet和JSP都有各自的优势和短板,那么为什么不结合起来扬长避短呢? HeroEditServlet:只用来从数据库中查询Hero对象,然后跳转到JSP页面
request.setAttribute("hero", hero);在request中设置hero
request.getRequestDispatcher("editHero.jsp").forward(request, response);服务端跳转到editHero.jsp,因为是服务端跳转,都属于同一次请求,所以可以在editHero.jsp通过request取出来 editHero.jsp: 不做查询数据库的事情,直接获取从HeroEditServlet传过来的Hero对象,通过EL表达式把request中的hero显示出来
'HeroEditServlet:' package servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import bean.Hero; import dao.HeroDAO; public class HeroEditServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int id = Integer.parseInt(request.getParameter("id")); Hero hero = new HeroDAO().get(id); request.setAttribute("hero", hero); request.getRequestDispatcher("editHero.jsp").forward(request, response); } }editHero.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*,bean.*,java.sql.*"%> <form action='updateHero' method='post'> 名字 : <input type='text' name='name' value='${hero.name}'> <br> 血量 :<input type='text' name='hp' value='${hero.hp}'> <br> 伤害: <input type='text' name='damage' value='${hero.damage}'> <br> <input type='hidden' name='id' value='${hero.id}'> <input type='submit' value='更新'> </form>上述例子中结合Serlvet和JSP进行数据的显示,就是一种MVC的思想。
M 代表 模型(Model) V 代表 视图(View) C 代表 控制器(controller)模型是什么呢? 模型就是数据,就是dao,bean 视图是什么呢? 就是网页, JSP,用来展示模型中的数据
控制器是什么? 控制器用来把不同的数据,显示在不同的视图上。 在这个例子的,Servlet就是充当控制器的角色,把Hero对象,显示在JSP上。 控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上。 ## MVC 查询
作为控制器的HeroListServlet,其作用就是通过dao获取所有的heros对象,然后放在request中,跳转到listHero.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <table align='center' border='1' cellspacing='0'> <tr> <td>id</td> <td>name</td> <td>hp</td> <td>damage</td> <td>edit</td> <td>delete</td> </tr> <c:forEach items="${heros}" var="hero" varStatus="st"> <tr> <td>${hero.id}</td> <td>${hero.name}</td> <td>${hero.hp}</td> <td>${hero.damage}</td> <td><a href="editHero?id=${hero.id}">edit</a></td> <td><a href="deleteHero?id=${hero.id}">delete</a></td> </tr> </c:forEach> </table>在DAO中提供方法分页查询方法:(还是用以前的HeroDAO)
public List<Hero> list(int start, int count)start表示开始的个数,count表示取多少条 比如 list(0, 5) , 即表示第一页,每页有5条数据 比如 list(5, 5) , 即表示第二页,每页有5条数据
修改HeroListServlet
int start = 0; int count = 5; List<Hero> heros = new HeroDAO().list(start, count);即表示只获取5条数据
HeroListServlet : 下一页: 通过参数获取start,如果浏览器没有传递参数,就设置为0。 根据start,计算next. next的值就是start+count. 然后把next传递给listHero.jsp 上一页: 根据start,计算pre. pre的值就是start-count. 然后把pre传递给listHero.jsp listHero.jsp 在最后面增加超链
<a href="?start=${next}">[下一页]</a> <a href="?start=${pre}">[上一页]</a>start=${pre}//start=${next}从服务器传递过来的pre/next值 通过设置属性pre和next,传给jsp界面
request.setAttribute("next", next); request.setAttribute("pre", pre); protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html; charset=UTF-8"); int start = 0; int count = 5; //当浏览器传递参数时,获取start try { start = Integer.parseInt(request.getParameter("start")); } catch (NumberFormatException e) { // 当浏览器没有传参数start时 } int next = start + count; int pre = start - count; request.setAttribute("next", next); request.setAttribute("pre", pre); List<Hero> heros = new HeroDAO().list(start, count); request.setAttribute("heros", heros); request.getRequestDispatcher("listHero.jsp").forward(request, response); }listHero.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <table align='center' border='1' cellspacing='0'> <tr> <td>id</td> <td>name</td> <td>hp</td> <td>damage</td> <td>edit</td> <td>delete</td> </tr> <c:forEach items="${heros}" var="hero" varStatus="st"> <tr> <td>${hero.id}</td> <td>${hero.name}</td> <td>${hero.hp}</td> <td>${hero.damage}</td> <td><a href="editHero?id=${hero.id}">edit</a></td> <td><a href="deleteHero?id=${hero.id}">delete</a></td> </tr> </c:forEach> <tr> <td colspan="6" align="center"> <a href="?start=${pre}">[上一页]</a> <a href="?start=${next}">[下一页]</a> </td> </tr> </table>首页只需要修改listHero.jsp:
<a href="?start=0">[首 页]</a>末页需要在HeroListServlet中计算last, last需要根据总数total和每页有多少条数据count来计算得出。 同时,还要看total是否能够整除count 假设总数是50,是能够被5整除的,那么最后一页的开始就是45
if (0 == total % count) last = total - count;修改listHero.jsp,在下一页后增加一个超链
<a href="?start=${last}">[末 页]</a>start=${last} 从服务器传递过来的last值
'listHero.jsp' <tr> <td colspan="6" align="center"> <a href="?start=0">[首 页]</a> <a href="?start=${pre}">[上一页]</a> <a href="?start=${next}">[下一页]</a> <a href="?start=${last}">[末 页]</a> </td> </tr> 'HeroListServlet.java:' int start = 0; int count = 5; try { start = Integer.parseInt(request.getParameter("start")); } catch (NumberFormatException e) { // 当浏览器没有传参数start时 } int next = start + count; int pre = start - count; int total = new HeroDAO().getTotal(); int last; // 假设总数是50,是能够被5整除的,那么最后一页的开始就是45 if (0 == total % count) last = total - count; // 假设总数是51,不能够被5整除的,那么最后一页的开始就是50 else last = total - total % count; request.setAttribute("next", next); request.setAttribute("pre", pre); request.setAttribute("last", last); List<Hero> heros = new HeroDAO().list(start, count); request.setAttribute("heros", heros); request.getRequestDispatcher("listHero.jsp").forward(request, response);上一页,下一页有一个问题, 如果在第一页点击上一页,就会看不到数据了,因为在第一页的时候,pre=-5,也就导致传递到serlvet的start=-5; 同样的在最后一页的时候,点击下一页,也有类似的问题。 解决办法是进行边界处理:
pre = pre < 0 ? 0 : pre;如果pre是负数了,就使用0
next = next > last ? last : next;如果next大于last,就使用last
'完整HeroListServlet.java' package servlet; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import bean.Hero; import dao.HeroDAO; public class HeroListServlet extends HttpServlet { protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html; charset=UTF-8"); int start = 0; int count = 5; try { start = Integer.parseInt(request.getParameter("start")); } catch (NumberFormatException e) { // 当浏览器没有传参数start时 } int next = start + count; int pre = start - count; int total = new HeroDAO().getTotal(); int last; if (0 == total % count) last = total - count; else last = total - total % count; pre = pre < 0 ? 0 : pre; next = next > last ? last : next; request.setAttribute("next", next); request.setAttribute("pre", pre); request.setAttribute("last", last); List<Hero> heros = new HeroDAO().list(start, count); request.setAttribute("heros", heros); request.getRequestDispatcher("listHero.jsp").forward(request, response); } }比如网站提供Hero查询服务,但是前提是用户要登录过才能使用。 如果用户登陆过了,访问listHero,就让用户正常访问,否则就跳转到登陆界面。 这是非常常见的场景,通过使用 session 来实现这个功能。 在处理登录的loginServlet 中使用将用户名保存在session中。 在HeroListServlet 中查看session中是否为空。如果为空,就表示用户没有登陆过,就跳转到登陆页面
如果用户输入正确的账号密码,就跳转到 listHero,并且把用户名以"userName"放进session 如果用户输入错误的账号密码,就跳转到 login.html,让用户重新登陆
//添加session request.getSession().setAttribute("userName", name); import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String password = request.getParameter("password"); if ("admin".equals(name) && "123".equals(password)) { //添加session request.getSession().setAttribute("userName", name); response.sendRedirect("listHero"); } else { response.sendRedirect("login.html"); } } }从session中取出userName,如果是空,就表示用户没有登录,或者登录已经超过了30分钟。 客户端跳转到login.html,让用户重新登陆