技术选型
Web层
- Servlet: 前端控制器
- Html: 视图
- Filter: 过滤器
- BeanUtils:数据封装
-
Service层
Javamail: java发送邮件工具
- Redis: nosql内存数据库
-
Dao层
Mysql:数据库
- Druid:数据库连接池
- JdbcTemplate:jdbc工具
创建数据库
功能实现
用户相关功能
注册
- 功能分析
- 代码实现
- 前台效果
- 表单校验
- ajax提交
- 使用异步提交是为了获取服务器响应的数据,因为我们使用的html,不能直接从servlet相关域获取数据,所以需要异步获取数据
- 前台效果
- 修改用户表的status为Y
代码实现
访问servlet,销毁session
跳转到登录页面
//1-销毁session
request.getSession().invalidate();
//2-跳转页面
response.sendRedirect(request.getContextPath()+"/login.html");
优化Servlet(反射 🌟
减少servlet的数量,目前是一个功能一个Servlet,将其优化为一个模块一个Servlet,相当于在数据库中一张表对应一个Servlet,完成用户需求
因为各种功能Servlet,例如(UserServlet,CategoryServlet)继承BaseServlet,所以在BaseServlet获取this时,谁(UserSetvlet\CategoryServlet)调用了BaseServlet,那么这个this就是该对象的class全类名,再通过反射获取该对象当前使用的成员方法,而该成员方法名就通过
request.getRequestURI()
来获取路径然后获取最后一段的成员方法名,再通过invoke方法执行该成员方法来实现方法的分发 ```java public class BaseServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//完成方法分发
//1-获取请求路径
String uri = req.getRequestURI(); // -> /travel/user/add
System.out.println("请求uri:"+uri);
//2-获取方法名称
String methodName = uri.substring(uri.lastIndexOf('/') + 1);
System.out.println("方法名称:"+methodName);
//3-获取方法对象Method
//谁调用了BaseServlet,this就是该对象的全类名
System.out.println(this);//cn.itcast.travel.web.servlet.UserServlet@1887040
try {
Method method = this.getClass().getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
//4-执行方法
method.invoke(this,req,resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} }
<a name="gDlo6"></a>
### 分类信息展示
<a name="qq8il"></a>
#### 获取分类数据

```javascript
//查询分类数据
$.get("/travel/category/findAll",{},function (data) {
// [{cid:1,cname:"xx"},{},{}...]
//首页和收藏排行榜是固定的
var lis = '<li class="nav-active"><a href="index.html">首页</a></li>';
//遍历数组,拼接字符串(<li>)
for (var i=0; i<data.length;i++)
{
var li = '<li><a href="route_list.html">'+data[i].cname+'</a></li>';
lis+=li;
}
//收藏排行榜
lis+='<li><a href="favoriterank.html">收藏排行榜</a></li>';
//将lis字符串设置到ul的html内容中
$("#category").html(lis);
缓存优化分类数据
- 分析发现分类数据在每一次页面加载后都会重新请求数据库来加载,对数据库的压力比较大,而且分类的数据不会经常产生变化,所以可以使用redis来缓存这个数据
期望数据库存储的顺序就是展示的顺序
//1-从redis中查询 //1-1获取jedis Jedis jedis = JedisUtil.getJedis(); //1-2使用sortedset排序 //Set<String> categorys = jedis.zrange("category", 0, -1); //1-3查询sortedset中的score(cid)和值(cname) Set<Tuple> categorys = jedis.zrangeWithScores("category", 0, -1); //2-判断查询集合是否为空 List<Category> cs=null; if(categorys==null || categorys.size()==0) { System.out.println("从数据库查询"); //3-如果为空,第一次访问,需要从数据库查询,再将数据存入redis //3-1从数据库查询 cs = categoryDao.findAll(); //3-2将集合数据存储到redis中名为 category的key for (int i = 0; i < cs.size(); i++) { jedis.zadd("category",cs.get(i).getCid(),cs.get(i).getCname()); } }else { System.out.println("从redis查询"); //4-如果不为空,将set数据存入list,返回 cs = new ArrayList<Category>(); for (Tuple tuple : categorys) { //将set数据存入list Category category = new Category(); category.setCname(tuple.getElement()); category.setCid((int)tuple.getScore()); cs.add(category); } } return cs;
上方代码中在Redis中查询了sortedset的socre,使用的是jedis的zrangWithScore()方法
旅游线路分页展示
点击不同分类,看到的旅游线路不一样,通过分析数据库表结构,旅游线路表和分类表是多对一的关系
- select * from tab_route where cid=? 根据点击的分类id获取不同分类数据
- 使用htmlDOM的location对象获取url路径携带的参数(?cid=x),然后通过分隔字符串获取cid
var id = search.split("=")[1];
- 分析
创建pageBean
/** * 分页对象 */ public class PageBean<T> { private int totalCount; //总记录数 private int totalPage; //总页数 private int currentPage; // 当前页码 private int pageSize; //每页显示条数 private List<T> list; //每页显示的数据集合 get..set..
代码编写
DAO层代码
@Override
public int findTotalCount(int cid) {
String sql = "select count(*) from tab_route where cid = ?";
return template.queryForObject(sql,Integer.class,cid);
}
@Override
public List<Route> findByPage(int cid, int start, int pageSize) {
String sql ="select * from tab_route where cid = ? limit ? , ?";
return template.query(sql,new BeanPropertyRowMapper<Route>(Route.class),cid,start,pageSize);
}
Service层
@Override
public PageBean<Route> pageQuery(int cid, int currentPage, int pageSize) {
//封装pageBean
PageBean<Route> pb = new PageBean<Route>();
pb.setCurrentPage(currentPage);//设置当前页码
pb.setPageSize(pageSize);//设置每页显示条数
int totalCount = routeDao.findTotalCount(cid);
pb.setTotalCount(totalCount);//设置总记录数
//设置当前页面显示集合
int start = (currentPage - 1)*pageSize; //当前查询的开始
List<Route> list = routeDao.findByPage(cid,start,pageSize);
pb.setList(list);
//总页数=总记录数/每页显示条数
int totalPage = (totalCount % pageSize)==0 ? (totalCount / pageSize):(totalCount / pageSize +1);
pb.setTotalPage(totalPage);//设置总页数
return pb;
}
前台代码编写
$(function () {
var search = location.search;//?cid=x
//alert(search);
var cid = search.split("=")[1];//截取cid
//当页面加载完成后调用load方法,发送ajax请求加载数据
load(cid);
});
function load(cid,currentPage) {
//发送ajax请求,请求route/pageQuery,传递cid
$.get("/travel/route/pageQuery",{cid:cid,currentPage:currentPage},function (pb) {
//解析pageBean数据,展示到页面
//1-分页条数据展示
//1.1-展示总页码和总记录书
$("#totalPage").html(pb.totalPage)
$("#totalCount").html(pb.totalCount)
var lis="";
var firstPage = '<li onclick="javascript:load(' + cid + ')" ><a href="javascript:void">首页</a></li>';
//计算上一页页码
var beforeNum = pb.currentPage -1;
if(beforeNum<=0){
beforeNum=1;
}
var beforePage='<li onclick="javascript:load(' + cid + ',' + beforeNum + ')" class="threeword"><a href="javascript:void">上一页</a></li>';
lis+=firstPage;
lis+=beforePage;
//1.2-分页页码
for (let i = 1; i <=pb.totalPage ; i++) {
var li;
//判断当前页码是否等于i
if(pb.currentPage == i){
li='<li class="curPage" onclick="javascript:load('+cid+','+i+')"><a href="javascript:void">'+i+'</a></li>';
}else {
//创建一个页码的li
li = '<li onclick="javascript:load(' + cid + ',' + i + ')"><a href="javascript:void">' + i + '</a></li>';
}
//拼接字符串
lis +=li;
}
//计算下一页页码
var nextNum = pb.currentPage +1;
if(nextNum >= pb.totalPage+1){
nextNum=pb.totalPage;
}
var lastPage='<li onclick="javascript:load(' + cid + ',' + pb.totalPage + ')" class="threeword"><a href="javascript:void">末页</a></li>';
var nextPage='<li onclick="javascript:load(' + cid + ',' + nextNum + ')" class="threeword"><a href="javascript:void">下一页</a></li>';
lis+=nextPage;
lis+=lastPage;
//将lis设置到ul中
$("#pageNum").html(lis);
//2-list集合数据
var route_lis ="";
for (let i = 0; i <pb.list.length ; i++) {
//获取{rid:1,rname:"xxx"...}
var route = pb.list[i];
var li ='<li>\n' +
' <div class="img"><img src="'+route.rimage+'" style="width: 299px;"></div>\n' +
' <div class="text1">\n' +
' <p>'+route.rname+'</p>\n' +
' <br/>\n' +
' <p>'+route.routeIntroduce+'</p>\n' +
' </div>\n' +
' <div class="price">\n' +
' <p class="price_num">\n' +
' <span>¥</span>\n' +
' <span>'+route.price+'</span>\n' +
' <span>起</span>\n' +
' </p>\n' +
' <p><a href="route_detail.html">查看详情</a></p>\n' +
' </div>\n' +
' </li>';
route_lis += li;
}
$("#route").html(route_lis);
});
}
页码优化,一共展示10个页码,前5后4
- 前边不足5个,则后边补充
后边不足4个,则前边补充
//1.2-分页页码 //定义开始位置和结束位置 var begin;//开始 var end;//结束 //1要显示10个页码 if(pb.totalPage< 10){ //总页码小于10的情况 begin = 1; end=pb.totalPage; }else{ //总页码超过10页 begin = pb.currentPage -5; end = pb.currentPage +4; //如果前边不够5个,后边补齐 if(begin <1){ begin =1; end=begin+9; } //后边不够4个,前边补齐 if(end >pb.totalPage){ end = pb.totalPage; begin = end - 9; } } for (let i = begin; i <=end ; i++) { //将循环判断修改为begin和end
最后将页面定位到顶部
//定位到页面顶部 window.scrollTo(0,0);