本次购物网站小项目涉及的主体功能如下:
1、商品信息显示
2、详细商品信息显示
3、浏览过的商品信息列表(以五条为轮换)
4、购物车信息的增删改查
下面将展示最终结果,描述整体设计逻辑,并解释部分重要代码。整体代码以及涉及到的网站图片、js、css将会在最后附上。
使用eclipse的伙计们,导代码的时候要注意,我是用idea写的,工程结构和路径可能会和它有一点点的差异,想了解这俩工程结构具体有啥差异的小伙伴们可以参考尚硅谷java基础课程,大概在第404课左右。
对了!还有访问路径一定要记得修改。
最终结果图:
整体设计逻辑
对象设计
设计了两个对应的实体类,一个是商品信息对应类Items,一个是购物车信息对应类ShopCar。由于购物车信息包括商品信息,所以ShopCar类是将Items类与购买数量及连在一起的。如图:
商品信息
全部商品信息的获取以及输出
这个比较简单只需要连接数据库进行进行查找即可,可以参看代码。这里值得提出的是利用JSTL以及EL表达式进行输出会使代码更简单。
查看单个商品信息
对应的方法传参是findByID,可以在ShoppingServlet中找到这一块对应的代码,这一模块涉及到实现查看单个商品信息以及查看浏览商品列表信息,可以通过查看代码备注以及下面的查看浏览记录的逻辑进行理解。
如何显示浏览商品信息的记录
这里利用会话机制,使用的是cookie去存储查看过的商品信息的列表。
购物车信息
这个地方因为未涉及到连接数据库进行数据交互,所以只需要设计一个单独的类进行增删改查。(最好将代码按功能分开,让他们之间的耦合度没有那么高)
增删改只需要用for-each循环遍历购物车信息列表就可进行实现。
这里应该注意的是在进行购物车某一条信息的增加时应注意是否已经存在该商品,存在就不能重复添加了。
部分代码解释
findByID方法块–实现功能:页面跳转时通过ID查找商品并将商品信息输出在页面;展示浏览商品记录。
case "findByID":
// System.out.println("进入findbyid方法");
//当前窗口可以查询单个商品对应信息,并且可以记录浏览情况,存入cookie中
String idstr = req.getParameter("id");
String ToplistAll = "";
//获取浏览记录列表,防止因再次访问而丢失信息
Cookie[] cookies = req.getCookies();
for (Cookie cookie:cookies){
if(cookie.getName().equals("listViewCookie")){
ToplistAll = URLDecoder.decode(cookie.getValue()) ;
}
}
//在更新信息之前先查看当前列表的信息数量,如果超过1000则清零
String[] arr = ToplistAll.split(",");
if(arr.length>=1000){
ToplistAll = "";
}
//更新浏览记录
/**
* 需特别注意的是: "," 该分隔符在cookie中属于特殊字符,编码时可能出现 Cookie值中存在无效字符[44] 提示
* 为解决此问题,可以使用 URLEncoder.encode 进行编码,要记得使用时用 URLDecoder.decode 解码
*/
ToplistAll += req.getParameter("id")+",";
Cookie cookie = new Cookie("listViewCookie", URLEncoder.encode(ToplistAll));
//重新保存
resp.addCookie(cookie);
//将前五条记录保存到浏览器中,以便用户查看
// System.out.println("toplist"+myshoppingRepsitory.SelectTop5(ToplistAll));
req.setAttribute("Toplist",myshoppingRepsitory.SelectTop5(ToplistAll));
//以下是根据ID查找对应商品信息
Integer id = Integer.parseInt(idstr);
req.setAttribute("item",myshoppingRepsitory.FindById(id));
// System.out.println(myshoppingRepsitory.FindById(id).toString());
req.getRequestDispatcher("details.jsp").forward(req,resp);
break;
我们可以分两块看这处代码。
跳转时通过ID查找商品并将商品信息输出在页面
case "findByID":
// System.out.println("进入findbyid方法");
//当前窗口可以查询单个商品对应信息,并且可以记录浏览情况,存入cookie中
String idstr = req.getParameter("id");
//以下是根据ID查找对应商品信息
Integer id = Integer.parseInt(idstr);
req.setAttribute("item",myshoppingRepsitory.FindById(id));
req.getRequestDispatcher("details.jsp").forward(req,resp);
break;
把该功能对应的代码摘出来就是这个了。这个逻辑结果比较简单,获取id,利用方法,跳转页面;需要注意的只有一点就是从浏览器获取的id是字符串类型的,要记得转换数据类型。
展示浏览商品记录
case "findByID":
// System.out.println("进入findbyid方法");
//当前窗口可以查询单个商品对应信息,并且可以记录浏览情况,存入cookie中
String ToplistAll = "";
//获取浏览记录列表,防止因再次访问而丢失信息
Cookie[] cookies = req.getCookies();
for (Cookie cookie:cookies){
if(cookie.getName().equals("listViewCookie")){
ToplistAll = URLDecoder.decode(cookie.getValue()) ;
}
}
//在更新信息之前先查看当前列表的信息数量,如果超过1000则清零
String[] arr = ToplistAll.split(",");
if(arr.length>=1000){
ToplistAll = "";
}
//更新浏览记录
/**
* 需特别注意的是: "," 该分隔符在cookie中属于特殊字符,编码时可能出现 Cookie值中存在无效字符[44] 提示
* 为解决此问题,可以使用 URLEncoder.encode 进行编码,要记得使用时用 URLDecoder.decode 解码
*/
ToplistAll += req.getParameter("id")+",";
Cookie cookie = new Cookie("listViewCookie", URLEncoder.encode(ToplistAll));
//重新保存
resp.addCookie(cookie);
//将前五条记录保存到浏览器中,以便用户查看
// System.out.println("toplist"+myshoppingRepsitory.SelectTop5(ToplistAll));
req.setAttribute("Toplist",myshoppingRepsitory.SelectTop5(ToplistAll));
// System.out.println(myshoppingRepsitory.FindById(id).toString());
req.getRequestDispatcher("details.jsp").forward(req,resp);
break;
把该功能的代码摘出来就是这个了。这里会给大家解释以下几点:
- 逻辑:我们可以这样想:
当前想要通过cookie对商品信息列表进行操作,那么第一步应该是拥有一个列表对应的cookie,也就是Cookie cookie = new Cookie("listViewCookie", ToplistAll);
但是我们需要考虑到另一点:获取之前这个列表是否在cookie中已经存在了呢?不存在我们是不是需要定义,存在我们是不是要进行更新?所以我们将这一逻辑进行优化:在获取列表之前先进行判断:
//拿出cookie列表
Cookie[] cookies = req.getCookies();
for (Cookie cookie:cookies){
//判断是否存在
if(cookie.getName().equals("listViewCookie")){
//如果存在将此cookie对应的value取出,放入我们定义好的ToplistAll 中
ToplistAll = cookie.getValue() ;
}
}
这样获取这条ToplistAll 信息的逻辑才够完整。之后我们可以对获取的ToplistAll 进行最多多少条的定义,而如何计算已经到了存储最大值,我们可以利用数组来进行,因为我们的ToplistAll 列表使用’,‘进行分割的,所以我们可以利用’,'将ToplistAll 分割成一个数组再进行判断。
String[] arr = ToplistAll.split(",");
if(arr.length>=1000){
ToplistAll = "";
}
最后一定要注意cookie一旦更新务必要进行存储也就是resp.addCookie(cookie);
语句。
- cookie的编码格式
细心的伙计们可以能注意到刚刚分段解释的部分代码和之前的总体代码有些出入,也就是以下两块儿:
Cookie[] cookies = req.getCookies();
for (Cookie cookie:cookies){
if(cookie.getName().equals("listViewCookie")){
ToplistAll = URLDecoder.decode(cookie.getValue()) ;
}
}
Cookie cookie = new Cookie("listViewCookie", URLEncoder.encode(ToplistAll));
为什么整合的代码加入了URLDecoder.decode()以及URLEncoder.encode()?
由于不同的版本 ‘,’ 在cookie的编码中是不能识别的,所以需要统一编码格式来避免报错,具体的解释小伙伴们可以参考我的另一篇博文:Cookie值中存在无效字符[44]的解决办法.
购物车操作类MyShopCarRepsitory的部分代码解释
使用static类型定义方法的原因
这是我在进行测试时出现了一个错误而使用的改错的方法:
出现问题:查询和增加商品信息操作执行后查看商品列表永远只有一个商品信息。也就是说每次操作执行后列表都只是存在一个对象,通过对每一个方法的测试我发现编写的方法是没有问题的,出现问题的主要原因是:购物车对应的servlet中每次进入都会重新new一个MyShopCarRepsitory以便进行操作,而MyShopCarRepsitory中有一个全局列表变量以便于所有方法对一个购物车进行操作:List<ShopCar> shopCarlist = new ArrayList<>();
这就导致了每次newMyShopCarRepsitory就会也new一个列表,这样下来每次进入servlet都是新的列表对象,所以会出现问题。
如果定义成static类型那么可以直接通过类名调用方法,不需要new对象,这样就一直都是那一条信息,就可以解决这个问题了。
删除商品信息代码解释
public static void delectShopCarByName(String name){
System.out.println("进入删除方法");
Iterator iterator = shopCarlist.iterator();
// int i =1;
while(iterator.hasNext()) {
System.out.println("进入删除方法的for循环");
ShopCar shopCar = (ShopCar) iterator.next();
if(shopCar.getItems().getName().equals(name)){
iterator.remove();
}
}
}
为什么这段代码里不直接进行遍历列表,然后判断存在,再删除呢?
因为ArrayList类中不允许对列表进行重复操作,ArrayList中就已经存在remove方法,该方法是属于迭代器类型的,所以在这里直接遍历删除会报错,要用迭代器去解决这一问题。具体解释小伙伴可以参看我的另一篇博文:Java ConcurrentModificationException异常原因和单线程下的解决方法
这样大体上就解释完这个小项目的部分难懂代码了,有什么问题可以私信,下面是css,js,images,sql文件,jar包,以及代码分享。
资料分享
网盘链接:https://pan.baidu.com/s/1iFMd3C8GsbPOEeQVKCm35A
提取码:ling
链接:https://pan.baidu.com/s/15WPvZQp1cvFNGmuEyKOtwA
提取码:ling