(尚硅谷)JavaWeb新版教程10-书城项目的实现(第一部分)
目录1、熟悉书城业务需求2、数据库设计2.1 抽取实体2.2 分析其中的属性2.3 分析实体之间的关系3、根据数据库的表新建 pojo 类(ORM编程思想)3.1 数据库表3.2 创建 pojo 类3.2.1 Book 类3.2.2 CartItem 项3.2.3 OrderBean 类3.2.4 OrderItem 类3.2.5 User 类4、前期配置5、登录验证5.1 修改 login.ht
·
目录
1、熟悉书城业务需求
- 用户登录、用户注册(涉及到验证码、正则表达式);
- 主界面上展示了图书的基本信息,并且分页展示;
- 用户可以点击添加图书,添加到购物车里,然后购物车可以对图书数量进行加减、展示总金额、删除,用户点击去结账之后生成一个订单;
- 订单界面可以看到,订单编号、订单日期、订单金额、订单数量、订单状态等;
- 后台管理人员登录管理员账号之后,可以有订单详情界面,也可以对图书进行管理(这里要给用户设定一栏角色栏)。
注意:
- 文中的静态页面已经提供好的,如果没有去尚硅谷公众号下载,直接使用其样式和静态页面即可。
2、数据库设计
2.1 抽取实体
- 图书 Book
- 用户 User
- 订单 OrderBean
- 订单详情 OrderItem
- 购物车项 CartItem
2.2 分析其中的属性
- 图书 : 书名、作者、价格、销量、库存、封面、状态
- 用户 : 用户名、密码、邮箱
- 订单 : 订单编号、订单日期、订单金额、订单数量、订单状态、用户(给用户看的)
- 订单详情 : 图书、数量、所属订单(后台管理)
- 购物车项 : 图书、数量、所属用户(用户)
2.3 分析实体之间的关系
- 用户:购物车项 1:N(一个用户可以有多个购物车项)
- 订单:订单详情 1:N(一个订单有多个订单详情,订单详情表示购物车的一项)
- 用户:订单 1: N(一个用户可以有多个订单)
- 图书:订单详情 1:N(一本图书会在多本订单图书中出现)
- 图书:购物车项 1:N(一本图书可以出现在多个购物车项中)
3、根据数据库的表新建 pojo 类(ORM编程思想)
ORM 编程思想:(object relational mapping),有点万事万物皆对象那种意思
- 一个数据表对应一个 java 类
- 表中的一条记录对应 java 类的一个对象
- 表中的一个字段对应 java 类的一个属性
3.1 数据库表
从尚硅谷的官方公众号去获取资料,导入的数据库表如下图所示:
- t_book:图书表
- t_cart_item:购物车项(表示购物车的某一行,某一项,对应订单详情中的某一项)
- t_order:订单表
- t_order_item:订单详情表(当购物车点击结账之后,每一项购物车项生成对应的一项订单详情)
- t_user:用户表
3.2 创建 pojo 类
- 这里没有特别展示的都需要创建每个属性的 get/set 方法;
- java 类中的属性名字尽量和数据库的列名相同,如果确实不相同的话,后面写 sql 语句的时候,记得起列的别名。
3.2.1 Book 类
public class Book {
private Integer id ;
private String bookImg ;
private String bookName ;
private Double price ;
private String author ;
private Integer saleCount ; //销量
private Integer bookCount ; //库存
private Integer bookStatus = 0 ; //0:正常 -1:无效
public Book(){}
public Book(Integer id) {
this.id = id;
}
}
3.2.2 CartItem 项
public class CartItem {
private Integer id ;
private Book book ;
private Integer buyCount ;
private User userBean ;
public CartItem(){}
public CartItem(Integer id) {
this.id = id;
}
}
3.2.3 OrderBean 类
public class OrderBean {
private Integer id ;
private String orderNo ;
private LocalDateTime orderDate;
private User orderUser ;
private Double orderMoney ;
private Integer orderStatus;
private List<OrderItem> orderItemList ; //1:N
public OrderBean(){}
public OrderBean(Integer id){
this.id= id;
}
}
3.2.4 OrderItem 类
public class OrderItem {
private Integer id ;
private Book book ; //M:1
private Integer buyCount ;
private OrderBean orderBean ; //M:1
public OrderItem(){}
public OrderItem(Integer id) {
this.id = id;
}
}
3.2.5 User 类
public class User {
private Integer id ;
private String uname ;
private String pwd ;
private String email;
private Integer role ;
private List<OrderBean> orderList ; //1:N
public User(){}
public User(Integer id) {
this.id = id;
}
}
4、前期配置
- 将静态页面粘贴到 WEB-INF 界面下,不让别人随便访问,但是我们可以通过设置让别人访问到。
- 添加包的依赖关系,配置 Tomcat,这里不仔细讲,前面说过,需要添加的 jar 包有数据库连接的包、thymeleaf 的包、Durid 数据库连接池的包,如果你把 ssm 打包成了 jar 包,还需要加入那个 jar 包。
- 配置文件:
配置文件 applicationContext.xml 和 jdbc.properties
applicationContext.xml 文件配置,上面 !DOCTYPE
这部分可写可不写,写了后面少依赖关系,会报错提醒,并且放置在 src 目录下(这里将所有后面所有要用到的配置都写上来,后面就不专门写配置文件这一步了):
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE beans [
<!ELEMENT beans (bean*)>
<!ELEMENT bean (property*)>
<!ELEMENT property (#PCDATA)>
<!ATTLIST bean id ID #REQUIRED>
<!ATTLIST bean class CDATA #IMPLIED>
<!ATTLIST property name CDATA #IMPLIED>
<!ATTLIST property ref IDREF #IMPLIED>
]>
<beans>
<bean id="page" class="com.atguigu.myssm.myspringmvc.PageController"/>
<!-- DAO -->
<bean id="userDAO" class="com.atguigu.book.dao.impl.UserDAOImpl"/>
<bean id="bookDAO" class="com.atguigu.book.dao.impl.BookDAOImpl"/>
<bean id="cartItemDAO" class="com.atguigu.book.dao.impl.CartItemDAOImpl"/>
<bean id="orderDAO" class="com.atguigu.book.dao.impl.OrderDAOImpl"/>
<bean id="orderItemDAO" class="com.atguigu.book.dao.impl.OrderItemDAOImpl"/>
<!-- service -->
<bean id="userService" class="com.atguigu.book.service.impl.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
<bean id="bookService" class="com.atguigu.book.service.impl.BookServiceImpl">
<property name="bookDAO" ref="bookDAO"/>
</bean>
<bean id="cartItemService" class="com.atguigu.book.service.impl.CartItemServiceImpl">
<property name="cartItemDAO" ref="cartItemDAO"/>
<property name="bookService" ref="bookService"/>
</bean>
<bean id="orderService" class="com.atguigu.book.service.impl.OrderServiceImpl">
<property name="orderDAO" ref="orderDAO"/>
<property name="orderItemDAO" ref="orderItemDAO"/>
<property name="cartItemDAO" ref="cartItemDAO"/>
</bean>
<!-- controller -->
<bean id="user" class="com.atguigu.book.controller.UserController">
<property name="userService" ref="userService"/>
<property name="cartItemService" ref="cartItemService"/>
</bean>
<bean id="book" class="com.atguigu.book.controller.BookController">
<property name="bookService" ref="bookService"/>
</bean>
<bean id="cart" class="com.atguigu.book.controller.CartController">
<property name="cartItemService" ref="cartItemService"/>
</bean>
<bean id="order" class="com.atguigu.book.controller.OrderController">
<property name="orderService" ref="orderService"/>
</bean>
</beans>
jdbc.properties 数据库连接配置,这里使用的是 Durid 数据库连接池:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bookdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
username=root
password=abc123
# 初始化时建立物理连接的个数
initialSize=5
# 最大连接池数量
maxActive=10
# 最大等待时间
maxWait=3000
配置文件 web.xml ,因为我们将静态页面粘贴到 WEB-INF 界面下,所以文件中视图前缀改成 /WEB-INF/pages/
,:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>view-prefix</param-name>
<param-value>/WEB-INF/pages/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
</web-app>
5、登录验证
5.1 修改 login.html 界面
- 将静态页面所有引入 css 样式的文件路径改成 th:xxxx 格式,例如:
th:href="@{/static/css/style.css}"
; - 用户名和密码的 form 表单改成
th:action="@{/user.do}"
,并且要实现一个登陆方法<input type="hidden" name="operate" value="login"/>
- 其中表单要传两个参数,
name="uname"
和name="pwd"
;
<div class="form">
<form th:action="@{/user.do}" method="post">
<input type="hidden" name="operate" value="login"/>
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1"
name="uname" id="username" value="lina"/>
<br/>
<br/>
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1"
name="pwd" id="password" value="ok"/>
<br/>
<br/>
<input type="submit" value="登录" id="sub_btn"/>
</form>
<div class="tit">
<a th:href="@{/page.do?operate=page&page=user/regist}">立即注册</a>
</div>
</div>
5.2 新建 UserController 控制器
- 实现 login 方法,需要 uname 和 pwd 两个参数,登陆成功之后将 user 保存到 session 作用域中,需要新增一个 session 参数
public class UserController {
private UserService userService ;
private CartItemService cartItemService ;
//用户登陆验证
public String login(String uname , String pwd , HttpSession session){
User user = userService.login(uname, pwd);
if(user!=null){
session.setAttribute("currUser",user);
return "redirect:book.do";
}
//如果没有登陆成功,重新回到login登录页面
return "user/login";
}
}
解释:
- 首先调用
userService
层中的登陆方法进行登陆验证; - 登陆成功之后将获取到的 user 类设置到 session 作用域中,方便之后调用。
5.3 新建 UserService 层
实现登陆验证 login 方法:
public class UserServiceImpl implements UserService {
private UserDAO userDAO ;
@Override
public User login(String uname, String pwd) {
return userDAO.getUser(uname,pwd);
}
}
5.4 新建 UserDAO 层
实现 getUser 方法从数据库中获取用户名和密码信息:
public class UserDAOImpl extends BaseDAO<User> implements UserDAO {
@Override
public User getUser(String uname, String pwd) {
return load("select * from t_user where uname like ? and pwd like ? " , uname , pwd );
}
}
6、主页面图书显示
- 上一步登录成功之后,
return "redirect:book.do";
重定向到bookController
控制器中去做图书的相关控制; - 在中央控制器中,如果没有发送 operate 值,默认
index
方法,所以我们在 bookController 控制器中新建 index 方法展示图书列表;
6.1 修改 index.html 页面
- 将静态页面所有引入 css 样式的文件路径改成 th:xxxx 格式;
- 遍历每一本书:
th:each="book : ${session.bookList}"
;
<div class="list-content">
<div href="" class="list-item" th:each="book : ${session.bookList}" th:object="${book}">
<img th:src="@{|/static/uploads/*{bookImg}|}" alt="">
<p th:text="|书名:*{bookName}|">书名:活着</p>
<p th:text="|作者:*{author}|">作者:余华</p>
<p th:text="|价格:¥*{price}|">价格:¥66.6</p>
<p th:text="|销量:*{saleCount}|">销量:230</p>
<p th:text="|库存:*{bookCount}|">库存:1000</p>
<button th:onclick="|addCart(*{id})|">加入购物车</button>
</div>
</div>
解释:
- 表示这个块中引用的都是 book 这个对象,下面就不用再 book.xxx 来调用它的属性了:
th:object="${book}"
; - 下面我调用该对象的 某个属性只需要
*{}
就可以了,比如调用 book 中的图片属性:th:src="@{|/static/uploads/*{bookImg}|}"
。
6.2 新建 BookController 控制器
- 实现 index 方法,调用 BookService 层的方法获取图书列表;
- 并且将获取到的图书列表 set 进 session 作用域中;
public class BookController {
private BookService bookService ;
//在中央控制器中,如果没有发送operate默认index方法
public String index(HttpSession session){
List<Book> bookList = bookService.getBookList();
session.setAttribute("bookList",bookList);
return "index";
}
}
6.3 新建 BookService 层
public class BookServiceImpl implements BookService {
private BookDAO bookDAO ;
@Override
public List<Book> getBookList() {
return bookDAO.getBookList();
}
}
6.4 新建 BookDAO 层
public class BookDAOImpl extends BaseDAO<Book> implements BookDAO {
@Override
public List<Book> getBookList() {
return executeQuery("select * from t_book where bookStatus=0");
}
}
7、添加到购物车按钮功能
7.1 修改index界面
- 点击某一本图书加入购物车:
th:onclick="|addCart(*{id})|"
;
<button th:onclick="|addCart(*{id})|">加入购物车</button>
解释;
- 这里的
*{id}
是和上面衔接起来的,因为我们在整个图书列表的 div 块上定义了th:object="${book}"
。
- 需要补一个 addCart 的 js 方法:
function addCart(bookId){
window.location.href='cart.do?operate=addCart&bookId='+bookId;
}
7.2 功能分析
- 既然上面跳转到 cartController 控制器中的 addCart 方法,所以我们需要根据这个 bookId 实现一个添加功能
- 将指定图书添加到当前用户的购物车中,判断当前用户的购物车中是否有这本书的 CartItem,有的话对这一购物车项进行 update,没有的话需要新增一个购物车项即 add;
1. 如果当前用户的购物车中已经存在这个图书了,那么将购物车中这本图书的数量+1;
2. 否则,在我的购物车中新增一个这本图书的 CartItem,数量是1;
7.2.1 新建 CartItemDAO 层
- 需要 CartItemDAO 中新增购物车项,将购物车中没有的图书添加进来;
- 需要在 CartItemDAO 中修改特定的购物车项,如果图书在购物车中已经存在;
public class CartItemDAOImpl extends BaseDAO<CartItem> implements CartItemDAO {
@Override
public void addCartItem(CartItem cartItem) {
executeUpdate("insert into t_cart_item values(0,?,?,?)",cartItem.getBook().getId(),cartItem.getBuyCount(),cartItem.getUserBean().getId());
}
@Override
public void updateCartItem(CartItem cartItem) {
executeUpdate("update t_cart_item set buyCount = ? where id = ? " , cartItem.getBuyCount(),cartItem.getId()) ;
}
}
7.2.2 新建购物车 pojo 类
- 要判断购物车中是否有某一图书项,购物车在哪里?新建购物车 pojo 类,这个和数据库没啥关系,只是用于逻辑判断、页面展示;
- 修改 getTotalMoney 方法、getTotalCount 方法、getTotalBookCount 方法(而且这方法是只读属性,没有set方法);
- 建完这个 pojo 类再去实现 CartItemService 层的方法;
Cart 类的实现,其中存放着购物车项,用来和其他类进行连接:
public class Cart {
private Map<Integer,CartItem> cartItemMap; //购物车中购物车项的集合 , 这个Map集合中的key是Book的id
private Double totalMoney ; //购物车的总金额
private Integer totalCount ; //购物车中购物项的数量,总共有多少项,而不是物品总件数
private Integer totalBookCount ; //购物车中书本的总数量,而不是购物车项的总数量
public Cart(){}
public Map<Integer, CartItem> getCartItemMap() {
return cartItemMap;
}
public void setCartItemMap(Map<Integer, CartItem> cartItemMap) {
this.cartItemMap = cartItemMap;
}
public Double getTotalMoney() {
totalMoney = 0.0;
//如果购物车中有东西,将购物车每一项从map中取出来,根据cartItem确定数的价格和购买的数量
if(cartItemMap!=null && cartItemMap.size()>0){
Set<Map.Entry<Integer, CartItem>> entries = cartItemMap.entrySet();
for(Map.Entry<Integer,CartItem> cartItemEntry : entries){
CartItem cartItem = cartItemEntry.getValue();
totalMoney = totalMoney + cartItem.getBook().getPrice() * cartItem.getBuyCount() ;
}
}
return totalMoney;
}
public Integer getTotalCount() {
totalCount = 0 ;
if(cartItemMap!=null && cartItemMap.size()>0){
totalCount = cartItemMap.size() ;
}
return totalCount;
}
public Integer getTotalBookCount() {
totalBookCount = 0 ;
if(cartItemMap!=null && cartItemMap.size()>0){
for (CartItem cartItem : cartItemMap.values()){
totalBookCount = totalBookCount + cartItem.getBuyCount() ;//每一购物车项它的数量
}
}
return totalBookCount;
}
}
注意:
- 这里的 cartItemMap 表示购物车中购物车项的集合 , 这个 Map 集合中的 key 是 Book 的 id ,也就是将图书和购物车项进行关联,最后放置在一个 map 中,如果我们又添加了一本书,那我们就看 map 中是否能找到对应的 key,没有的话就进行新增,有的话,将对应的 value 值取出来,然后将其中的数量属性 +1 之后 set 回去;
- 对于 totalMoney 、totalCount 、totalBookCount 属性,我们只需要它们的 get 方法,因为它们是根据购物车项算出来的,而不能我们自己进行设置;
- 即如果购物车中有东西,我们把每一个购物车项取出来,然后根据 cartItem 获取书的金额和目前购物车项的数量进行一个计算。
7.2.3 新建 CartItemService 层
- addOrUpdateCartItem 方法,判断购物车中到底有没有某一本图书,这里需要将购物车 cart 作为一个参数传进来;
- 实现添加购物车项功能即 addCartItem 方法;
- 实现更新购物车项的功能即 updateCartItem 方法;
public class CartItemServiceImpl implements CartItemService {
private CartItemDAO cartItemDAO ;
private BookService bookService ;
@Override
public void addCartItem(CartItem cartItem) {
cartItemDAO.addCartItem(cartItem);
}
@Override
public void updateCartItem(CartItem cartItem) {
cartItemDAO.updateCartItem(cartItem);
}
@Override
public void addOrUpdateCartItem(CartItem cartItem, Cart cart) {
//判断当前用户的购物车中是否有这本书的CartItem,有->update , 无->add
if (cart != null) {
Map<Integer, CartItem> cartItemMap = cart.getCartItemMap();
//如果购物车中的购物车项为空,新建一个map
if (cartItemMap == null) {
cartItemMap = new HashMap<>();
}
//1.如果当前用户的购物车中已经存在这个图书了,那么将购物车中这本图书的数量+1
if (cartItemMap.containsKey(cartItem.getBook().getId())) {
CartItem cartItemTemp = cartItemMap.get(cartItem.getBook().getId());
cartItemTemp.setBuyCount(cartItemTemp.getBuyCount() + 1);
updateCartItem(cartItemTemp);
} else {
//2.否则,在我的购物车中新增一个这本图书的CartItem,数量是1
addCartItem(cartItem);
}
} else { //连购物车都没有的情况
addCartItem(cartItem);
}
}
}
解释:
- 如果购物车中的购物车项为空,新建一个 map,防止下面遍历 map 的时候发生空指针错误;
- 这里就能看出来购物车中将 Book 的 Id 和购物车项封装成一个 map 的好处了,我们判断购物车中有没有某一本书,根据 Book 的 Id 来进行查询即可,
cartItemMap.containsKey(cartItem.getBook().getId())
; - 如果当前用户的购物车中存在这本书,将购物车中对应这本书的那一项取出来,将它的数量 +1 之后再 set 回去,最后更新购物车项;
- 如果购物车中不存在这本书,我们直接新增购物车项。
7.2.4 新建 CartController 控制器
- 我们要实现 addCart 的话要调用 CartItemService 层的 addOrUpdateCartItem 方法,那么我们需要两参数,这两参数怎么获取呢?
- 我们登录成功的时候,应该已经加载了当前用户的购物车信息(下一个功能实现),所以我们有当前用户的购物车信息;
- 要新建一个 cartItem 项(用三参数构造器),其中 book 可以根据 bookId 来构造,buyCount 设置为1,user 就是当前用户;
public class CartController {
private CartItemService cartItemService ;
//加书到购物车中
public String addCart(Integer bookId , HttpSession session){
User user = (User)session.getAttribute("currUser");
CartItem cartItem = new CartItem(new Book(bookId),1,user);
//将指定的图书添加到当前用户的购物车中
cartItemService.addOrUpdateCartItem(cartItem,user.getCart());
//这里我们不要点击一本书就跳转到购物车,所以将这里的重定向页面改成index页面
// return "redirect:cart.do";
return "index";
}
}
7.2.5 cartItem 类中新建构造器
- 新建一个三参数构造器:
public CartItem(Book book, Integer buyCount, User userBean) {
this.book = book;
this.buyCount = buyCount;
this.userBean = userBean;
}
7.2.6 错误排查
- script 不能用单标签,只能用双标签!
- 将老师的项目粘贴过来的话,会打不开 URL,IDEA启动项目报错:
Cannot open URL.Please check this URL is correct
,参考文献:IDEA启动项目报错:Cannot open URL.Please check this URL is correct,原因是过滤器部分的设置,先将那个包删掉。 java.lang.NoSuchMethodException: java.lang.Double.<init>(java.lang.Integer)
;发现是在 BaseDAO 中判断是否 isNotMyType 的时候,没有将 Double 类型设置上去,所以它默认会去找 Double 类型的构造器,要把它封装成一个 Double 对象,将这句话添加上去。
BaseDAO 修改:
private static boolean isNotMyType(String typeName) {
return "java.lang.Integer".equals(typeName)
|| "java.lang.String".equals(typeName)
|| "java.util.Date".equals(typeName)
|| "java.sql.Date".equals(typeName)
|| "java.time.LocalDateTime".equals(typeName)
|| "java.lang.Double".equals(typeName);
}
8、登录成功之后首页上显示欢迎语和购物车数量
- 购物车项在用户登陆之后进行绑定,而不是在 index 展示页面进行购物车绑定,万一人家想浏览你的首页但是还没注册登录呢。
8.1 修改 index.html 界面
- 判断如果没有登陆显示某一个登录块:
th:if="${session.currUser==null}"
; - 如果已经登陆显示欢迎xxx已经登陆:
th:text="${session.currUser.getUname()}"
; - 购物车数量改成真实的:
th:text="${session.currUser.cart.getTotalCount()}"
;
<div class="topbar-right" th:if="${session.currUser==null}">
<a th:href="@{/page.do?operate=login&page=user/login}" class="login">登录</a>
<a th:href="@{/page.do?operate=login&page=user/regist}" class="register">注册</a>
<a href="cart/cart.html" class="cart iconfont icon-gouwuche">
购物车
<div class="cart-num">3</div>
</a>
<a href="manager/book_manager.html" class="admin">后台管理</a>
</div>
<div class="topbar-right" th:unless="${session.currUser==null}">
<!--登录后风格-->
<span>欢迎你<b th:text="${session.currUser.getUname()}">张总</b></span>
<a href="#" class="register">注销</a>
<a th:href="@{/page.do(operate='page',page='cart/cart')}" class="cart iconfont icon-gouwuche">
购物车
<div class="cart-num" th:text="${session.currUser.cart.totalCount}">3</div>
</a>
<a href="./pages/manager/book_manager.html" class="admin">后台管理</a>
</div>
解释:
- 这里展示购物车数量的时候,既可以用
th:text="${session.currUser.cart.getTotalCount()}"
,也可以用th:text="${session.currUser.cart.totalCount}"
,因为 thymeleaf 作为一个后端的组件,在调用这个属性的时候会自动的调用其中给的 get 方法。
8.2 CartController 控制器
- 加载当前用户的购物车信息:index方法
//加载当前用户的购物车信息
public String index(HttpSession session){
User user =(User)session.getAttribute("currUser");
Cart cart = cartItemService.getCart(user);
user.setCart(cart);
session.setAttribute("currUser",user);
return "cart/cart";
}
解释:
- 我们先获取当前登录用户信息;
- 然后调用 CartItemService 层的方法获取到该用户关联的购物车信息(从数据库中获取数据);
- 并且将购物车信息 set 进用户的 cart 属性中,所以我们要在 user 类中新增 cart 属性;
- 之后再将用户保存到 session 作用域中;最后进行视图渲染即可更新购物车信息。
8.3 UserController 类
- 登录方法中,登陆成功之后,获取用户关联的购物车:cartItemService.getCart(user),因此用户要有一个购物车;
- user 新增属性:
private Cart cart
,并且给它设置 get/set 方法。
//用户登陆验证
public String login(String uname , String pwd , HttpSession session){
User user = userService.login(uname, pwd);
if(user!=null){
Cart cart = cartItemService.getCart(user);
user.setCart(cart);
session.setAttribute("currUser",user);
return "redirect:book.do";
}
//如果没有登陆成功,重新回到login登录页面
return "user/login";
}
8.4 CartItemService 层
- 加载特定用户的购物车信息:getCart(User user)方法,依赖于用户信息的;
@Override
public Cart getCart(User user) {
//拿到购物车项,我们需要将每一项放到一个map中,然后封装到购物车中
List<CartItem> cartItemList = cartItemDAO.getCartItemList(user);
Map<Integer, CartItem> cartItemMap = new HashMap<>();
for (CartItem cartItem : cartItemList) {
cartItemMap.put(cartItem.getBook().getId(), cartItem);
}
//如果当前用户的购物车项为空,那么我们cart也不为空,只不过map为空
Cart cart = new Cart();
cart.setCartItemMap(cartItemMap);
return cart;
}
解释:
- 拿到购物车项,我们需要将每一项放到一个 map 中,然后封装到购物车中;
- 如果当前用户的购物车项为空,那么我们新建一个,保证用户的 cart 不为空,只不过 map 为空。
8.5 CartItemDAO 层
- 获取当前登录用户的所有购物车项:getCartItemList(User user)方法
@Override
public List<CartItem> getCartItemList(User user) {
return executeQuery("select * from t_cart_item where userBean = ? " , user.getId());
}
9、展示购物车详情
9.1 CartController 控制器
- 新建 index 方法,加载当前用户的购物车信息,调用 CartItemService 层的
getCart(User user)
方法,我们之前已经创建过这个方法了; - 加载完购物车信息之后,跳转到 cart.html 这个界面,注意这里不是静态页面,要经过 thymeleaf 渲染;
//加载当前用户的购物车信息
public String index(HttpSession session){
User user =(User)session.getAttribute("currUser");
Cart cart = cartItemService.getCart(user);
user.setCart(cart);
session.setAttribute("currUser",user);
return "cart/cart";
}
9.2 修改 cart.html 页面
- 迭代显示购物车项:
th:each="cartItem : ${session.currUser.cart.cartItemMap.values()}"
,表示从当前用户绑定的购物车中的购物车项 map 中获取它的 values 即那些 cartItem 项; - 显示购物车项的图片:
th:src="@{|/static/uploads/${cartItem.book.bookImg}|}"
- 书名:
th:text="${cartItem.book.bookName}"
- 购物车中该本书的数量:
th:value="${cartItem.buyCount}"
- 共多少件商品(所有书的数目的总和):
th:text="${session.currUser.cart.totalBookCount}"
- 总金额:
th:text="${session.currUser.cart.totalMoney}"
<table>
<tbody>
<tr th:each="cartItem : ${session.currUser.cart.cartItemMap.values()}">
<td>
<img th:src="@{|/static/uploads/${cartItem.book.bookImg}|}" alt=""/>
</td>
<td th:text="${cartItem.book.bookName}">活着</td>
<td>
<span class="count" >-</span>
<input class="count-num" type="text" value="1" th:value="${cartItem.buyCount}"/>
<span class="count">+</span>
</td>
<td th:text="${cartItem.book.price}">36.8</td>
<td><a href="">删除</a></td>
</tr>
</tbody>
</table>
<div class="footer">
<div class="footer-right">
<div>共<span th:text="${session.currUser.cart.totalBookCount}">3</span>件商品</div>
<div class="total-price">总金额<span th:text="${session.currUser.cart.totalMoney}">99.9</span>元</div>
<a class="pay" th:href="@{/order.do(operate='checkout')}">去结账</a>
</div>
</div>
9.3 错误排查
- totalMoney 查询不到,因为 CartItemService 层在 Cart getCart(User user); 这个方法的时候,调用了 cartItemDAO.getCartItemList(user); 这个方法的时候,我们只从 t_cart_item 这个表中获取到了所有 book 的 Id,还没有将 cartItem 和 book 关联起来,也就是还没有将 book 属性 set 进 cartItem 这一项中。
解决:
- 修改 CartItemService 层:
- 获取指定用户的所有购物车项列表(需要注意的是,这个方法内部查询的时候,会将book的详细信息设置进去)
- 调用 bookService 层的获取书的详细信息的方法,然后将得到的书 set 进去;
@Override
public List<CartItem> getCartItemList(User user) {
List<CartItem> cartItemList = cartItemDAO.getCartItemList(user);
for (CartItem cartItem : cartItemList) {
Book book = bookService.getBook(cartItem.getBook().getId());
cartItem.setBook(book);
}
return cartItemList;
}
- 修改 bookService 层:
1. 实现根据 Id 获取每一本书的详细信息的方法:
@Override
public Book getBook(Integer id) {
return bookDAO.getBook(id);
}
更多推荐
已为社区贡献1条内容
所有评论(0)