看完上一个章节,相信你已经掌握了一个高效无误地编写SQL的办法,学会了完成小项目的注册功能,对如何使用之前学过的一些知识有了一定的认识,今天我们继续学习,一起来搞懂前后端分离的套路。
在搞懂MVC框架原理一文中,上面这个MVC的模型图片大家应该有点印象了,客户端发起响应,服务端使用模板技术从当了view的角色,在服务端的应用服务器中渲染html,然后返回给客户端,客户端完成解析。
但实际上,对于浏览器而言,关心的只是html,css,js等元素,对于是谁返回给它的,它并不关心。浏览器解析html,完成样式渲染,加载运行脚本语言。在这个过程中,js是可以去改变页面结构的,也可以再次发起请求。那么问题就来了,js可以改变页面结构,那渲染页面html代码的事情,交给它不就完了?
从这个点考虑,前端脚本只用做两件事情就可以了——获取后端动态数据和改变html的页面结构。后端只用提供改变html的结构所需要的数据就好了。这样子,前端的开发人员可以只用关注前端的事情,后端的开发人员关注返回的数据,职责上明确了,程序的开发方式也就明确了。前端和后端程序的开发相对独立了,也就是所谓的前后端分离了。分离的是职责,是工作方式,具体使用什么技术,做到了就好,而不是框架,不是一堆技术名词……
返回静态页面的事情,可以交给web服务器,在这种静态文件的处理能力上,web服务器比应用服务器强悍多了。浏览器获取到web服务器返回的静态资源,html,css,js……然后在渲染的过程中,js通常发起一个异步请求,到后端获取数据,至于为什么后端应用服务器中的程序返回JSON格式的数据,其实只是JSON是一种比较方便的能够序列化对象数据的格式而已,用其他的也可以,只是JSON格式的数据和javascript的对象和数组可以方便转换,用着比较方便。
这样做了之后,页面还可以保留一些静态节点,即使后端程序挂了,这些静态节点的存在,也不影响页面的展示功能。
导航栏属于公共头部的一部分,导航栏的内容,实际上属于ul标签下的li标签。想要动态的展示导航栏的内容,需要从数据库里查询分类数据(查询travel_category表),可以使用json的方式返回。页面需要解析json数据,拼接成导航内容的,然后将内容插入到ul标签下即可。
后端代码
package com.pz.route.dao;
importjava.util.List;
importcom.pz.route.domain.TravelCategory;
/**
*
* @author pangzi
*
*/
public interface TravelCategoryDao {
/**
* 查询所有
* @return
*/
public List<TravelCategory>findAllTravelCategory();
}
public class TravelCategoryDaoImpl implements TravelCategoryDao {
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List<TravelCategory>findAllTravelCategory() {
String sql = "select travel_cid,travel_name fromtravel_category ";
returntemplate.query(sql,newBeanPropertyRowMapper<TravelCategory>(TravelCategory.class));
}
}
package com.pz.route.service;
import java.util.List;
import com.pz.route.domain.TravelCategory;
public interface TravelCategoryService {
public List<TravelCategory>findAll();
}
package com.pz.route.service.impl;
import java.util.List;
import com.pz.route.dao.TravelCategoryDao;
import com.pz.route.dao.impl.TravelCategoryDaoImpl;
import com.pz.route.domain.TravelCategory;
import com.pz.route.service.TravelCategoryService;
public class TravelCategoryServiceImpl implements TravelCategoryService {
//初始化数据访问接口
private TravelCategoryDao travelCategoryDao= new TravelCategoryDaoImpl();
@Override
public List<TravelCategory> findAll(){
List<TravelCategory> catrgoryList= null;
//调用数据访问接口从数据库查询类目信息
catrgoryList =travelCategoryDao.findAllTravelCategory();
return catrgoryList;
}
}
编写类目servlet
package com.pz.route.web.servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.pz.route.domain.TravelCategory;
import com.pz.route.service.TravelCategoryService;
import com.pz.route.service.impl.TravelCategoryServiceImpl;
//继承之前封装的BaseServlet通配符匹配所有/category/的路径
@WebServlet("/category/*")
public class CategoryServlet extends BaseServlet {
//初始化service接口
private TravelCategoryService service = new TravelCategoryServiceImpl();
/**
* 查询所有
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
publicvoidfindAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.调用service查询所有
List<TravelCategory> cs = service.findAll();
//返回json数据
responseJson(cs,response);
}
}
前端脚本
$.get("category/findAll",{},function (data) {
//通过url获取travelCid参数,决定哪个分类被选中
var reg = new RegExp('(^|&)' + cid + '=([^&]*)(&|$)', 'i');
var r =window.location.search.substr(1).match(reg);
var cid=null;
var lis ='';
cid=getUrlParam("cid");
//[{travelCid:1,travelCname:国内游},{},{}]
if(cid==null){
lis= '<liid="indexPage"><a href="index.html">首页</a></li>';
}else{
lis= '<liid="indexPage"><a href="index.html">首页</a></li>';
}
//遍历数组,拼接字符串(<li>)
for (var i = 0; i < data.length; i++) {
var li ='';
if(cid!=null&&cid==data[i].travelCid){
var li = '<liclass="nav-active"><a href="route_list.html?cid='+data[i].travelCid+'">'+data[i].travelCname+'</a></li>';
}
else{
li= '<li><ahref="route_list.html?cid='+data[i].travelCid+'">'+data[i].travelCname+'</a></li>';
}
lis += li;
}
//拼接收藏排行榜的li,<li><ahref="favoriterank.html">收藏排行榜</a></li>
lis+= '<li id="favlist"><ahref="favoriterank.html">收藏排行榜</a></li>';
//将lis字符串,设置到ul的html内容中
$("#category").html(lis);
$(window).trigger("categoryLoaded");
});
我们在动态生成的导航栏上加了a标签,点击之后页面会发生跳转到分类列表页面,所以我们需要编写一个新的页面——分类列表页面。页面跳转时,浏览器会发起get请求,将类目的id传过来,所以我们通过分类id来确定列表页面需要展示哪个分类下线路信息的数据,后端程序完成线路数据的查询,返回给列表页。由于线路的数据包含:线路基本信息、线路图片信息、线路商家信息、线路收藏信息、所以一次操作可能涉及多个表的数据。因为某个类型的线路信息可能很多,一次也没必要展示完全,所以列表页下方,有分页标记,我们浏览数据需要像“翻书”一样查看。也就是说,我们需要设定分页的大小,每一次,获取每一页的数据就好了。
后端代码
package com.pz.route.domain;
import java.util.List;
/**
* 分页列表对象在list基础长增加分页的参数
* @author pangzi
*
* @param<T>
*/
public class PageList<T> {
private int totalCount;//总记录数
private int totalPage;//总页数
private int currentPage;//当前页码
private int pageSize;//每页显示的条数
private List<T> list;//每页显示的数据集合
publicintgetTotalCount() {
returntotalCount;
}
publicvoid setTotalCount(int totalCount) {
this.totalCount =totalCount;
}
publicint getTotalPage(){
returntotalPage;
}
publicvoid setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
publicintgetCurrentPage() {
returncurrentPage;
}
publicvoidsetCurrentPage(int currentPage) {
this.currentPage =currentPage;
}
publicint getPageSize(){
returnpageSize;
}
publicvoid setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public List<T> getList() {
returnlist;
}
publicvoidsetList(List<T> list) {
this.list = list;
}
}
package com.pz.route.service;
import com.pz.route.domain.PageList;
import com.pz.route.domain.TravelRoute;
/**
* 线路Service
*/
public interface TravelRouteService {
/**
* 根据类别进行分页查询
* @param travelRouteCid
* @param currentPage
* @param pageSize
* @param travelRouteName
* @return
*/
publicPageList<TravelRoute> pageQuery(long travelRouteCid,int currentPage,intpageSize,String travelRouteName);
/**
* 根据id查询
* @param rid
* @return
*/
public TravelRoutefindTravelRouteById(String travelRouteId);
}
package com.pz.route.service.impl;
import java.util.List;
import com.pz.route.dao.TravelFavoriteDao;
import com.pz.route.dao.TravelRouteDao;
import com.pz.route.dao.TravelRouteImgDao;
import com.pz.route.dao.TravelSellerDao;
import com.pz.route.dao.impl.TravelFavoriteDaoImpl;
import com.pz.route.dao.impl.TravelRouteDaoImpl;
import com.pz.route.dao.impl.TravelRouteImgDaoImpl;
import com.pz.route.dao.impl.TravelSellerDaoImpl;
import com.pz.route.domain.PageList;
import com.pz.route.domain.TravelRoute;
import com.pz.route.domain.TravelRouteImg;
import com.pz.route.domain.TravelSeller;
import com.pz.route.service.TravelRouteService;
public class TravelRouteServiceImpl implements TravelRouteService {
private TravelRouteDaotravelRouteDao = new TravelRouteDaoImpl();
private TravelRouteImgDaotravelRouteImgDao = new TravelRouteImgDaoImpl();
private TravelSellerDaotravelsellerDao = new TravelSellerDaoImpl();
private TravelFavoriteDaotravelFavoriteDao = new TravelFavoriteDaoImpl();
@Override
publicPageList<TravelRoute> pageQuery(long travelRouteCid,int currentPage,intpageSize,String travelRouteName) {
//创建分页bean
PageList<TravelRoute>page = new PageList<TravelRoute>();
//设置默认当前页,默认为第一页
if(0==currentPage){
currentPage=1;
}
//设置默认分页大小,默认为10条每页
if(0==pageSize){
pageSize=10;
}
//设置当前页码
page.setCurrentPage(currentPage);
//设置每页显示条数
page.setPageSize(pageSize);
//设置线路总数
int totalCount =travelRouteDao.findTotalCount(travelRouteCid,travelRouteName);
page.setTotalCount(totalCount);
//设置当前页显示的数据集合,mysql分页第一个参数,从0开始
int start = (currentPage -1) * pageSize;
//返回符合查询条件的线路列表
List<TravelRoute>list = travelRouteDao.findTravelRouteByPage(travelRouteCid,start,pageSize,travelRouteName);
page.setList(list);
//计算总页数,需要考虑能除尽和不能除尽的情况,不能除尽的情况下,会多一页
int totalPage = totalCount% pageSize == 0 ? totalCount / pageSize :(totalCount / pageSize) + 1 ;
page.setTotalPage(totalPage);
return page;
}
@Override
public TravelRoutefindTravelRouteById(String travelRouteId) {
//1.根据线路id查询线路对象
TravelRoute travelRoute =travelRouteDao.findTravelRouteById(Long.parseLong(travelRouteId));
//2.根据线路id查询线路图片
List<TravelRouteImg>routeImgList =travelRouteImgDao.findByTravelRouteId(travelRoute.getTravelRouteId());
travelRoute.setRouteImgList(routeImgList);
//3.根据商家ID查询商家
TravelSeller seller =travelsellerDao.findTravelSellerById(travelRoute.getTravelRouteSellerId());
travelRoute.setSeller(seller);
//4. 查询收藏次数
int count =travelFavoriteDao.findCountByTravelRouteId(travelRoute.getTravelRouteId());
travelRoute.setTravelRouteCount(count);
return travelRoute;
}
}
package com.pz.route.dao.impl;
import com.pz.route.dao.TravelRouteDao;
import com.pz.route.domain.TravelRoute;
import com.pz.route.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.List;
public class TravelRouteDaoImpl implements TravelRouteDao {
private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public int findTotalCount(longtravelRouteCid,String travelRouteName) {
//String sql = "selectcount(*) from tab_route where cid = ?";
//1.定义sql模板
String sql = "selectcount(*) from travel_route where 1=1 ";
StringBuilder sb = newStringBuilder(sql);
List params = newArrayList();//条件们
//2.判断参数是否有值
if(travelRouteCid != 0){
sb.append( " andtravel_route_cid = ? ");
params.add(travelRouteCid);//添加?对应的值
}
if(travelRouteName != null&& travelRouteName.length() > 0){
sb.append(" andtravel_route_name like ? ");
params.add("%"+travelRouteName+"%");
}
sql = sb.toString();
returntemplate.queryForObject(sql,Integer.class,params.toArray());
}
@Override
public List<TravelRoute>findTravelRouteByPage(long travelRouteCid , int start , int pageSize,StringtravelRouteName) {
//String sql = "select* from tab_route where cid = ? and rname like ? limit ? , ?";
String sql = " selecttravel_route_id,travel_route_name,travel_route_price,travel_route_introduce,travel_route_flag,travel_route_date,travel_route_count,travel_route_cid,travel_route_image,travel_route_seller_idfrom travel_route where 1 = 1 ";
//1.定义sql模板
StringBuilder sb = newStringBuilder(sql);
List params = newArrayList();//条件们
//2.判断参数是否有值
if(travelRouteCid != 0){
sb.append( " andtravel_route_cid = ? ");
params.add(travelRouteCid);//添加?对应的值
}
if(travelRouteName != null&& travelRouteName.length() > 0){
sb.append(" and travel_route_name likeconcat('%',?,'%') ");
params.add(travelRouteName);
}
sb.append(" limit ? ,? ");//分页条件
sql = sb.toString();
params.add(start);
params.add(pageSize);
return template.query(sql,newBeanPropertyRowMapper<TravelRoute>(TravelRoute.class),params.toArray());
}
@Override
public TravelRoutefindTravelRouteById(long travelRouteId) {
String sql = "selecttravel_route_id,travel_route_name,travel_route_price,travel_route_introduce,travel_route_flag,travel_route_date,travel_route_count,travel_route_cid,travel_route_image,travel_route_seller_idfrom travel_route where travel_route_id = ?";
returntemplate.queryForObject(sql,new BeanPropertyRowMapper<TravelRoute>(TravelRoute.class),travelRouteId);
}
}
package com.pz.route.dao.impl;
import com.pz.route.dao.TravelRouteImgDao;
import com.pz.route.domain.TravelRouteImg;
import com.pz.route.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class TravelRouteImgDaoImpl implements TravelRouteImgDao {
private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
@Override
publicList<TravelRouteImg> findByTravelRouteId(long travelRouteId) {
String sql = "selecttravel_route_img_id,travel_route_id,travel_route_img_big_pic,travel_route_img_small_picfrom travel_route_img where travel_route_id = ? ";
returntemplate.query(sql,newBeanPropertyRowMapper<TravelRouteImg>(TravelRouteImg.class),travelRouteId);
}
}
package com.pz.route.dao.impl;
import com.pz.route.dao.TravelFavoriteDao;
import com.pz.route.domain.TravelFavorite;
import com.pz.route.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Date;
public class TravelFavoriteDaoImpl implements TravelFavoriteDao {
private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public TravelFavoritefindByTravelRouteIdAndUserId(long travelRouteId, long travelRouteUserId) {
TravelFavorite favorite =null;
try {
String sql = "select travel_route_id,travel_create_date,travel_user_id from travel_favoritewhere travel_route_id = ? and travel_user_id = ?";
favorite =template.queryForObject(sql, newBeanPropertyRowMapper<TravelFavorite>(TravelFavorite.class),travelRouteId, travelRouteUserId);
} catch(DataAccessException e) {
e.printStackTrace();
}
return favorite;
}
@Override
public intfindCountByTravelRouteId(long travelRouteId) {
String sql = "SELECTCOUNT(*) FROM travel_favorite WHERE travel_route_id = ?";
return template.queryForObject(sql,Integer.class,travelRouteId);
}
@Override
public void add(longtravelRouteId, long travelUserId) {
String sql = "insertinto travel_favorite values(?,?,?)";
template.update(sql,travelRouteId,new Date(),travelUserId);
}
}
package com.pz.route.dao.impl;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import com.pz.route.dao.TravelSellerDao;
import com.pz.route.domain.TravelSeller;
import com.pz.route.util.JDBCUtils;
public class TravelSellerDaoImpl implements TravelSellerDao {
private JdbcTemplate template =new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public TravelSellerfindTravelSellerById(long travelSellerId) {
String sql = "selecttravel_seller_id,travel_seller_name,travel_seller_consphone,travel_seller_address from travel_seller where travel_seller_id = ?";
returntemplate.queryForObject(sql,newBeanPropertyRowMapper<TravelSeller>(TravelSeller.class),travelSellerId);
}
}
前端脚本
$(function () {
//获取cid的参数值
var cid = getUrlParam("cid");
//获取rname的参数值
var rname = getUrlParam("rname");
//判断rname如果不为null或者""
if(rname){
//url解码
rname =window.decodeURIComponent(rname);
}
//当页码加载完成后,调用load方法,发送ajax请求加载数据
load(cid,null,rname);
});
function load(cid ,currentPage,rname){
//发送ajax请求,请求route/pageQuery,传递cid
$.get("route/pageQuery",{cid:cid,currentPage:currentPage,rname:rname},function (pb) {
//解析pagebean数据,展示到页面上
//1.分页工具条数据展示
//1.1 展示总页码和总记录数
$("#totalPage").html(pb.totalPage);
$("#totalCount").html(pb.totalCount);
var lis = "";
var fristPage = '<li onclick="javascipt:load('+cid+',1,\''+rname+'\')"><ahref="javascript:void(0)">首页</a></li>';
//计算上一页的页码
var beforeNum = pb.currentPage - 1;
if(beforeNum <= 0){
beforeNum = 1;
}
var beforePage = '<li onclick="javascipt:load('+cid+','+beforeNum+',\''+rname+'\')"class="threeword"><a href="javascript:void(0)">上一页</a></li>';
lis += fristPage;
lis += beforePage;
//1.2 展示分页页码
/*
1.一共展示10个页码,能够达到前5后4的效果
2.如果前边不够5个,后边补齐10个
3.如果后边不足4个,前边补齐10个
*/
// 定义开始位置begin,结束位置 end
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 ;
//2.如果前边不够5个,后边补齐10个
if(begin < 1){
begin = 1;
end = begin + 9;
}
//3.如果后边不足4个,前边补齐10个
if(end > pb.totalPage){
end = pb.totalPage;
begin = end - 9 ;
}
}
for (var i = begin; i <= end ; i++) {
var li;
//判断当前页码是否等于i
if(pb.currentPage == i){
li = '<lionclick="javascipt:load('+cid+','+i+',\''+rname+'\')"><ahref="javascript:void(0)">'+i+'</a></li>';
}else{
//创建页码的li
li = '<li onclick="javascipt:load('+cid+','+i+',\''+rname+'\')"><ahref="javascript:void(0)">'+i+'</a></li>';
}
//拼接字符串
lis += li;
}
var nextPage =null;lastPage=null;
if(pb.totalPage==1){
lastPage = '<li><ahref="javascript:;">末页</a></li>';
nextPage = '<li><ahref="javascript:;">下一页</a></li>';
}else{
lastPage = '<li onclick="javascipt:load('+cid+','+pb.totalPage+',\''+rname+'\')"><ahref="javascript:void(0)">末页</a></li>';
nextPage='<li onclick="javascipt:load('+cid+','+(pb.currentPage+1)+',\''+rname+'\')"><ahref="javascript:void(0)">下一页</a></li>';
}
lis += nextPage;
lis += lastPage;
//将lis内容设置到 ul
$("#pageNum").html(lis);
//2.列表数据展示
var route_lis = "";
for (var i = 0; i < pb.list.length; i++) {
//获取{rid:1,rname:"xxx"}
var route = pb.list[i];
var li = '<li>\n' +
' <divclass="img"><img src="'+route.travelRouteImage+'" style="width:299px;"></div>\n' +
' <divclass="text1">\n' +
' <p>'+route.travelRouteName+'</p>\n' +
' <br/>\n' +
' <p>'+route.travelRouteIntroduce+'</p>\n' +
' </div>\n' +
' <divclass="price">\n' +
' <pclass="price_num">\n' +
' <span>¥</span>\n' +
' <span>'+route.travelRoutePrice+'</span>\n' +
' <span>起</span>\n' +
' </p>\n' +
' <p><ahref="route_detail.html?rid='+route.travelRouteId+'">查看详情</a></p>\n' +
' </div>\n' +
' </li>';
route_lis += li;
}
$("#route").html(route_lis);
//定位到页面顶部
window.scrollTo(0,0);
});
}
//根据传递过来的参数name获取对应的值
functiongetUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)","i");
var r = location.search.substr(1).match(reg);
if (r!=null) return (r[2]); returnnull;
}