MVC是一种分层的设计模式,本章节从Servlet和JSP的局限性谈起,慢慢引入MVC的概念。
一、why MVC
1.仅仅使用Servlet的短处(不擅长写html,擅长写java)
在Servlet的章节中,使用了编辑Hero的Servlet
根据浏览器提交的id,通过HeroDAO找到对应的Hero,然后在Servlet中组织html显示出来。
可以看到这个Servlet不仅要准备数据,还要准备html。 尤其是准备html,可读性非常差,维护起来也很麻烦
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);
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);
}
}
2.仅仅使用JSP的短处(不擅长写java,擅长写html)
因为在Servlet中编写html有这样的短板,所以索性直接在JSP中开发编辑Hero这个功能
这时会发现,虽然编写html方便了,但是写java代码不如在Servlet中那么方便(因为毕竟是写jsp(类似带标签的html)
<%@ 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>
目标:html和java分开写分开读
3.结合Servlet和JSP
既然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显示出来
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);
}
}
<%@ 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>
4.MVC设计模式
上述例子中结合Serlvet和JSP进行数据的显示,就是一种MVC的思想。
M 代表 模型(Model)
V 代表 视图(View)
C 代表 控制器(controller)
模型是什么呢? 模型就是数据,就是dao,bean
视图是什么呢? 就是网页, JSP,用来展示模型中的数据
控制器是什么? 控制器用来把不同的数据,显示在不同的视图上。 在这个例子的,Servlet就是充当控制器的角色,把Hero对象,显示在JSP上。
控制器的作用就是把不同的数据(Model)(id不同,dao对象不同),显示在不同的视图(View)上(jsp有所不同)。
二、结合SERVLET和JSP 实现查询功能
准备:
1.实体类Hero (JAVA BEAN)——(模型,M)
2.HeroDAO用于从数据库查询数据(模型,M)
3.HeroListServlet(控制器,C)
作为控制器的HeroListServlet,其作用就是通过dao获取所有的heros对象,然后放在request中,跳转到listHero.jsp
import bean.Hero;
import dao.HeroDAO;
public class HeroListServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Hero> heros = new HeroDAO().list();
request.setAttribute("heros", heros);
//服务器跳转到listHero.jsp展示
request.getRequestDispatcher("listHero.jsp").forward(request, response);
}
}
然后配置一下web.xml
4.listHero.jsp(视图,V)
<%@ 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>
三、分页
随着数据中记录的增多,网页上显示的数据会越来越多。
当多到一定程度的时候,就会影响用户的体验。
解决办法是通过分页技术,一次只显示数据库中的部分数据,如果要看其他数据,可以通过“下一页” “最后一页” 等翻页操作实现
1.首先准备 DAO
在DAO中提供方法
public List<Hero> list(int start, int count)
start表示开始的个数,count表示取多少条
比如 list(0, 5) , 即表示第一页,每页有5条数据
比如 list(5, 5) , 即表示第二页,每页有5条数据
2.只显示5条数据
修改HeroListServlet
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;
List<Hero> heros = new HeroDAO().list(start, count);
request.setAttribute("heros", heros);
request.getRequestDispatcher("listHero.jsp").forward(request, response);
}
}
即表示只获取5条数据
3.下一页
HeroListServlet :
通过参数获取start,如果浏览器没有传递参数,就设置为0。
根据start,计算next. next的值就是start+count.
然后把next传递给listHero.jsp
listHero.jsp
在最后面增加一个超链接
<a href="?start=${next}">[下一页]</a>
start=${next} 从服务器传递过来的next值
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;
List<Hero> heros = new HeroDAO().list(start, count);
request.setAttribute("next", next);
request.setAttribute("heros", heros);
request.getRequestDispatcher("listHero.jsp").forward(request, response);
}
}
<tr>
<td colspan="6" align="center">
<!-- 获取next的值作为参数,跳转访问?start=next -->
<a href="?start=${next}">[下一页]</a>
</td>
</tr>
4.上一页
HeroListServlet :
根据start,计算pre. pre的值就是start-count.
然后**把pre传递给**listHero.jsp
listHero.jsp
在下一页前增加一个超链
<a href="?start=${pre}">[上一页]</a>
start=${pre} 从服务器传递过来的pre值,注意边界条件
5.第一页
只需要修改listHero.jsp即可
增加
<a href="?start=0">[首 页]</a>
因为首页的start永远都是0
6.最后一页
HeroListServlet :
在HeroListServlet中计算last
last需要根据总数total和每页有多少条数据count来计算得出。
同时,还要看total**是否能够整除count**
假设总数是50,是能够被5整除的,那么最后一页的开始就是45
if (0 == total % count)
last = total - count;
假设总数是51,不能够被5整除的,那么最后一页的开始就是50
last = total - total % count;
listHero.jsp
在下一页后增加一个超链
<a href="?start=${last}">[末 页]</a>
start=${last} 从服务器传递过来的last值
7.边界处理
上一页,下一页有一个问题,
如果在第一页点击上一页,就会看不到数据了,因为在第一页的时候,pre=-5,也就导致传递到serlvet的start=-5;
同样的在最后一页的时候,点击下一页,也有类似的问题。
解决办法是进行边界处理:
pre = pre < 0 ? 0 : pre;
如果pre是负数了,就使用0
next = next > last ? last : next;
如果next大于last,就使用last
四、使用SERVLET 验证用户是否登陆
比如网站提供Hero查询服务,但是前提是用户要登录过才能使用。
如果用户登陆过了,访问listHero,就让用户正常访问,否则就跳转到登陆界面。
这是非常常见的场景,通过使用 session 来实现这个功能。
在处理登录的loginServlet 中使用将用户名保存在session中。
在HeroListServlet 中查看session中是否为空。如果为空,就表示用户没有登陆过,就跳转到登陆页面
1.在LoginServlet 把验证成功的用户加入到 Session
如果用户输入正确的账号密码,就跳转到 listHero,并且把用户名以”userName”放进session(一个放在session的属性)
如果用户输入错误的账号密码,就跳转到 login.html,让用户重新登陆