[TOC]
## 步骤 1 : 先运行,看到效果,再学习
先将完整的 spring 项目(向老师要相关资料),配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。
## 步骤 2 : 模仿和排错
在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较**正确答案** ( 可运行项目 ) 和自己的代码,来定位问题所在。
采用这种方式,**学习有效果,排错有效率**,可以较为明显地提升学习速度,跨过学习路上的各个槛。
## 步骤 3 : 页面截图
重启tomcat,通过访问地址
`http://127.0.0.1:8080/tmall_ssm/admin_product_list?categoryId=12`
可以看到产品管理的界面
注: 这categoryId=12是分类的id,根据你的实际运行情况,采取不同的id值
## 步骤 4 : Product
Product在自动生成的基础上增加category属性
~~~
package com.dodoke.tmall.pojo;
import java.util.Date;
public class Product {
private Integer id;
private String name;
private String subTitle;
private Float originalPrice;
private Float promotePrice;
private Integer stock;
private Date createDate;
private Integer categoryId;
/*非数据库字段*/
private Category category;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public String getSubTitle() {
return subTitle;
}
public void setSubTitle(String subTitle) {
this.subTitle = subTitle == null ? null : subTitle.trim();
}
public Float getOriginalPrice() {
return originalPrice;
}
public void setOriginalPrice(Float originalPrice) {
this.originalPrice = originalPrice;
}
public Float getPromotePrice() {
return promotePrice;
}
public void setPromotePrice(Float promotePrice) {
this.promotePrice = promotePrice;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Integer getCategoryId() {
return categoryId;
}
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
~~~
## 步骤 5 : ProductService
新增ProductService,提供CRUD一套
~~~
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Product;
public interface ProductService {
void add(Product c);
void delete(int id);
void update(Product c);
Product get(int id);
List list(int categoryId);
}
~~~
## 步骤 6 : ProductServiceImpl
新增ProductServiceImpl ,提供CRUD一套。 值得一提的是, get和list方法都会把取出来的Product对象设置上对应的category
~~~
package com.dodoke.tmall.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.dodoke.tmall.mapper.ProductMapper;
import com.dodoke.tmall.pojo.Category;
import com.dodoke.tmall.pojo.Product;
import com.dodoke.tmall.pojo.ProductExample;
import com.dodoke.tmall.service.CategoryService;
import com.dodoke.tmall.service.ProductService;
public class ProductServiceImpl implements ProductService {
@Autowired
ProductMapper productMapper;
@Autowired
CategoryService categoryService;
@Override
public void add(Product c) {
productMapper.insert(c);
}
@Override
public void delete(int id) {
productMapper.deleteByPrimaryKey(id);
}
@Override
public void update(Product p) {
productMapper.updateByPrimaryKeySelective(p);
}
@Override
public Product get(int id) {
Product p = productMapper.selectByPrimaryKey(id);
setCategory(p);
return p;
}
private void setCategory(Product p) {
int categoryId = p.getCategoryId();
Category category = categoryService.get(categoryId);
p.setCategory(category);
}
@Override
public List list(int categoryId) {
ProductExample example = new ProductExample();
example.createCriteria().andCategoryIdEqualTo(categoryId);
example.setOrderByClause("id desc");
List result = productMapper.selectByExample(example);
setCategory(result);
return result;
}
public void setCategory(List<Product> ps) {
for (Product p : ps) {
setCategory(p);
}
}
}
~~~
## 步骤 7 : ProductController
准备ProductController类
~~~
package com.dodoke.tmall.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.dodoke.tmall.pojo.Category;
import com.dodoke.tmall.pojo.Product;
import com.dodoke.tmall.service.CategoryService;
import com.dodoke.tmall.service.ProductService;
import com.dodoke.tmall.util.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@RequestMapping("")
@Controller
public class ProductController {
@Autowired
CategoryService categoryService;
@Autowired
ProductService productService;
@RequestMapping("admin_product_add")
public String add(Model model, Product p) {
productService.add(p);
return "redirect:admin_product_list?categoryId=" + p.getCategoryId();
}
@RequestMapping("admin_product_delete")
public String delete(int id) {
Product p = productService.get(id);
productService.delete(id);
return "redirect:admin_product_list?categoryId=" + p.getCategoryId();
}
@RequestMapping("admin_product_edit")
public String edit(Model model, int id) {
Product p = productService.get(id);
Category c = categoryService.get(p.getCategoryId());
p.setCategory(c);
model.addAttribute("p", p);
return "admin/editProduct";
}
@RequestMapping("admin_product_update")
public String update(Product p) {
productService.update(p);
return "redirect:admin_product_list?categoryId=" + p.getCategoryId();
}
@RequestMapping("admin_product_list")
public String list(int categoryId, Model model, Page page) {
Category c = categoryService.get(categoryId);
PageHelper.offsetPage(page.getStart(), page.getCount());
List<Product> ps = productService.list(categoryId);
int total = (int) new PageInfo<>(ps).getTotal();
page.setTotal(total);
page.setParam("&categoryId=" + c.getId());
model.addAttribute("ps", ps);
model.addAttribute("c", c);
model.addAttribute("page", page);
return "admin/listProduct";
}
}
~~~
## 步骤 8 : listProduct.jsp+editProduct.jsp
然后是查询和编辑的jsp页面
注: listProduct.jsp里的firstProductImage相关部分都注释掉了,因为这部分内容要到**产品图片管理** 才提供
listProduct.jsp:
```
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@include file="../include/admin/adminHeader.jsp"%>
<%@include file="../include/admin/adminNavigator.jsp"%>
<title>产品管理</title>
<div class="workingArea">
<ol class="breadcrumb">
<li><a href="admin_category_list">所有分类</a></li>
<li><a href="admin_product_list?categoryId=${c.id}">${c.name}-产品</a></li>
<li class="active">产品管理</li>
</ol>
<div class="listDataTableDiv">
<table class="table table-striped table-bordered table-hover table-condensed">
<thead>
<tr class="success">
<th>ID</th>
<th>图片</th>
<th>产品名称</th>
<th>产品小标题</th>
<th width="53px">原价格</th>
<th width="80px">优惠价格</th>
<th width="80px">库存数量</th>
<th width="80px">图片管理</th>
<th width="80px">设置属性</th>
<th width="42px">编辑</th>
<th width="42px">删除</th>
</tr>
</thead>
<tbody>
<c:forEach items="${ps}" var="p">
<tr>
<td>${p.id}</td>
<td>
<%--<c:if test="${!empty p.firstProductImage}">--%>
<%--<img width="40px" src="img/productSingle/${p.firstProductImage.id}.jpg">--%>
<%--</c:if>--%>
</td>
<td>${p.name}</td>
<td>${p.subTitle}</td>
<td>${p.originalPrice}</td>
<td>${p.promotePrice}</td>
<td>${p.stock}</td>
<td><a href="admin_productImage_list?pid=${p.id}"><span
class="glyphicon glyphicon-picture"></span></a></td>
<td><a href="admin_propertyValue_edit?pid=${p.id}"><span
class="glyphicon glyphicon-th-list"></span></a></td>
<td><a href="admin_product_edit?id=${p.id}"><span
class="glyphicon glyphicon-edit"></span></a></td>
<td><a deleteLink="true"
href="admin_product_delete?id=${p.id}"><span
class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div class="pageDiv">
<%@include file="../include/admin/adminPage.jsp" %>
</div>
<div class="panel panel-warning addDiv">
<div class="panel-heading">新增产品</div>
<div class="panel-body">
<form method="post" id="addForm" action="admin_property_add" enctype="multipart/form-data">
<table class="addTable">
<tr>
<td>产品名称</td>
<td><input id="name" name="name" type="text" class="form-control"></td>
</tr>
<tr>
<td>产品小标题</td>
<td><input id="subTitle" name="subTitle" type="text"
class="form-control"></td>
</tr>
<tr>
<td>原价格</td>
<td><input id="originalPrice" value="99.98" name="originalPrice" type="text"
class="form-control"></td>
</tr>
<tr>
<td>优惠价格</td>
<td><input id="promotePrice" value="19.98" name="promotePrice" type="text"
class="form-control"></td>
</tr>
<tr>
<td>库存</td>
<td><input id="stock" value="99" name="stock" type="text"
class="form-control"></td>
</tr>
<tr class="submitTR">
<td colspan="2" align="center">
<input type="hidden" name="categoryId" value="${c.id}">
<button type="submit" class="btn btn-success">提 交</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<%@include file="../include/admin/adminFooter.jsp"%>
<script>
$(function(){
$("#addForm").submit(function(){
if(!checkEmpty("name","产品名称")) {
return false;
}
return true;
});
});
</script>
```
editProduct.jsp:
```
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@include file="../include/admin/adminHeader.jsp"%>
<%@include file="../include/admin/adminNavigator.jsp"%>
<title>编辑产品</title>
<div class="workingArea">
<ol class="breadcrumb">
<li><a href="admin_category_list">所有分类</a></li>
<li><a href="admin_product_list?categoryId=${p.category.id}">${p.category.name}-产品</a></li>
<li class="active">编辑产品</li>
</ol>
<div class="panel panel-warning editDiv">
<div class="panel-heading">编辑产品</div>
<div class="panel-body">
<form method="post" id="editForm" action="admin_product_update" enctype="multipart/form-data">
<table class="editTable">
<tr>
<td>产品名称</td>
<td><input id="name" name="name" value="${p.name}" type="text" class="form-control"></td>
</tr>
<tr>
<td>产品小标题</td>
<td><input id="subTitle" name="subTitle" type="text"
value="${p.subTitle}"
class="form-control"></td>
</tr>
<tr>
<td>原价格</td>
<td><input id="originalPrice" value="${p.originalPrice}" name="originalPrice" type="text"
class="form-control"></td>
</tr>
<tr>
<td>优惠价格</td>
<td><input id="promotePrice" value="${p.promotePrice}" name="promotePrice" type="text"
class="form-control"></td>
</tr>
<tr>
<td>库存</td>
<td><input id="stock" value="${p.stock}" name="stock" type="text"
class="form-control"></td>
</tr>
<tr class="submitTR">
<td colspan="2" align="center">
<input type="hidden" name="id" value="${p.id}">
<input type="hidden" name="categoryId" value="${p.category.id}">
<button type="submit" class="btn btn-success">提 交</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<%@include file="../include/admin/adminFooter.jsp"%>
<script>
$(function(){
$("#editForm").submit(function(){
if(!checkEmpty("name","产品名称")) {
return false;
}
if (!checkNumber("originalPrice", "原价格"))
return false;
if (!checkNumber("promotePrice", "优惠价格"))
return false;
if (!checkInt("stock", "库存"))
return false;
return true;
});
});
</script>
``````
```
## 步骤 9 : 查询功能讲解
查询地址admin_product_list映射的是ProductController的list()方法
1. 获取分类 categoryId,和分页对象page
2. 通过PageHelper设置分页参数
3. 基于categoryId,获取当前分类下的产品集合
4. 通过PageInfo获取产品总数
5. 把总数设置给分页page对象
6. 拼接字符串"&categoryId="+c.getId(),设置给page对象的Param值。 因为产品分页都是基于当前分类下的分页,所以分页的时候需要传递这个categoryId
7. 把产品集合设置到 request的 "ps" 产品上
8. 把分类对象设置到 request的 "c" 产品上。 ( 这个c有什么用呢? 在 其他-面包屑导航 中会用于显示分类名称)
9. 把分页对象设置到 request的 "page" 对象上
10. 服务端跳转到admin/listProduct.jsp页面
11. 在listProduct.jsp页面上使用c:forEach 遍历ps集合,并显示
![](https://box.kancloud.cn/22ccdf122a9d3336fb3f83daaea4f7e2_1792x270.png)
ProductController.java:
~~~
@RequestMapping("admin_product_list")
public String list(int categoryId, Model model, Page page) {
Category c = categoryService.get(categoryId);
PageHelper.offsetPage(page.getStart(), page.getCount());
List<Product> ps = productService.list(categoryId);
int total = (int) new PageInfo<>(ps).getTotal();
page.setTotal(total);
page.setParam("&categoryId=" + c.getId());
model.addAttribute("ps", ps);
model.addAttribute("c", c);
model.addAttribute("page", page);
return "admin/listProduct";
}
~~~
listProduct.jsp:
~~~
<c:forEach items="${ps}" var="p">
<tr>
<td>${p.id}</td>
<td>
<%--<c:if test="${!empty p.firstProductImage}">--%>
<%--<img width="40px" src="img/productSingle/${p.firstProductImage.id}.jpg">--%>
<%--</c:if>--%>
</td>
<td>${p.name}</td>
<td>${p.subTitle}</td>
<td>${p.originalPrice}</td>
<td>${p.promotePrice}</td>
<td>${p.stock}</td>
<td><a href="admin_productImage_list?pid=${p.id}"><span
class="glyphicon glyphicon-picture"></span></a></td>
<td><a href="admin_propertyValue_edit?pid=${p.id}"><span
class="glyphicon glyphicon-th-list"></span></a></td>
<td><a href="admin_product_edit?id=${p.id}"><span
class="glyphicon glyphicon-edit"></span></a></td>
<td><a deleteLink="true"
href="admin_product_delete?id=${p.id}"><span
class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
~~~
## 步骤 10 : 增加功能讲解
1. 在listProduct.jsp提交数据的时候,除了提交产品名称,小标题,原价格,优惠价格,库存外还会提交categoryId
2. 在ProductController中获取Product对象,并插入到数据库
3. 客户端跳转到admin_product_list,并带上参数categoryId
![](https://box.kancloud.cn/494b99c2c5939518037cda142071e14e_514x429.png)
ProductController.java:
~~~
@RequestMapping("admin_product_add")
public String add(Model model, Product p) {
productService.add(p);
return "redirect:admin_product_list?categoryId=" + p.getCategoryId();
}
~~~
listProduct.jsp:
~~~
<form method="post" id="addForm" action="admin_product_add" enctype="multipart/form-data">
<table class="addTable">
<tr>
<td>产品名称</td>
<td><input id="name" name="name" type="text" class="form-control"></td>
</tr>
<tr>
<td>产品小标题</td>
<td><input id="subTitle" name="subTitle" type="text"
class="form-control"></td>
</tr>
<tr>
<td>原价格</td>
<td><input id="originalPrice" value="99.98" name="originalPrice" type="text"
class="form-control"></td>
</tr>
<tr>
<td>优惠价格</td>
<td><input id="promotePrice" value="19.98" name="promotePrice" type="text"
class="form-control"></td>
</tr>
<tr>
<td>库存</td>
<td><input id="stock" value="99" name="stock" type="text"
class="form-control"></td>
</tr>
<tr class="submitTR">
<td colspan="2" align="center">
<input type="hidden" name="categoryId" value="${c.id}">
<button type="submit" class="btn btn-success">提 交</button>
</td>
</tr>
</table>
</form>
~~~
## 步骤 11 : 编辑功能讲解
1. 在ProductController的edit方法中,根据id获取product对象
2. 根据product对象的categoryId产品获取Category对象,并把其设置在product对象的category产品上
3. 把product对象放在request的 "p" 产品中
3. 服务端跳转到admin/editProduct.jsp
4. 在editProduct.jsp中显示产品名称
5. 在editProduct.jsp中隐式提供id和categoryId( categoryId 通过 p.category.id 获取)
![](https://box.kancloud.cn/411ceb3af954666ba6bec3b9d43fea68_103x189.png)
ProductController.java:
~~~
@RequestMapping("admin_product_edit")
public String edit(Model model, int id) {
Product p = productService.get(id);
model.addAttribute("p", p);
return "admin/editProduct";
}
~~~
editProduct.jsp:
~~~
<input type="hidden" name="categoryId" value="${p.category.id}">
<button type="submit" class="btn btn-success">提 交</button>
~~~
## 步骤 12 : 修改功能讲解
1. 在ProductController的update方法中获取Product对象
2. 借助productService更新这个对象到数据库
3. 客户端跳转到admin_product_list,并带上参数categoryId
![](https://box.kancloud.cn/1614497bbde99a56d214669108c5d8d3_516x437.png)
~~~
@RequestMapping("admin_product_update")
public String update(Product p) {
productService.update(p);
return "redirect:admin_product_list?categoryId=" + p.getCategoryId();
}
~~~
## 步骤 13 : 删除功能讲解
1. 在ProductController的delete方法中获取id
2. 根据id获取Product对象
3. 借助productService删除这个对象对应的数据
4. 客户端跳转到admin_product_list,并带上参数categoryId
![](https://box.kancloud.cn/6e5c3489a6eb639922fc9a86cecffaf2_95x202.png)
~~~
@RequestMapping("admin_product_delete")
public String delete(int id) {
Product p = productService.get(id);
productService.delete(id);
return "redirect:admin_product_list?categoryId=" + p.getCategoryId();
}
~~~
## 步骤 14 : 自己做一遍
关于产品管理这一块的学习,推荐如下思路:
1. 先拿到本章节对应项目,配置好了跑一遍
2. 根据接着的讲解,把里面的每一块代码的逻辑,思路,设计思想搞明白,不明白的及时问老师
3. 都理清楚之后,把可运行项目删掉,按照:查询,增加,删除,编辑,修改的顺序自己从头到尾做一遍
只有,能够独立的做出来,才叫做把这些知识点的内容转化为自己的技能
> 删除中需要注意:
> * 先简单点做,产品下有没有对应属性值,有没有图片,没有属性值并且没有图片才能删除,学员自行添加相应逻辑。
> * 复杂点用标记:要删除还是挺麻烦的,最好标记成为删除状态比较好。 否则要删除的数据太多了,产品图片信息,属性值信息,订单项信息,评价信息,这些都是依赖产品的。产品毕竟是核心数据,还是使用标记的好,不要轻易删除的好。
- 项目简介
- 功能一览
- 前台
- 后台
- 开发流程
- 需求分析-展示
- 首页
- 产品页
- 分类页
- 搜索结果页
- 购物车查看页
- 结算页
- 确认支付页
- 支付成功页
- 我的订单页
- 确认收货页
- 确认收货成功页
- 评价页
- 需求分析-交互
- 分类页排序
- 立即购买
- 加入购物车
- 调整订单项数量
- 删除订单项
- 生成订单
- 订单页功能
- 确认付款
- 确认收货
- 提交评价信息
- 登录
- 注册
- 退出
- 搜索
- 前台需求列表
- 需求分析后台
- 分类管理
- 属性管理
- 产品管理
- 产品图片管理
- 产品属性设置
- 用户管理
- 订单管理
- 后台需求列表
- 表结构设计
- 数据建模
- 表与表之间的关系
- 后台-分类管理
- 可运行的项目
- 静态资源
- JSP包含关系
- 查询
- 分页
- 增加
- 删除
- 编辑
- 修改
- 做一遍
- 重构
- 分页方式
- 分类逆向工程
- 所有逆向工程
- 后台其他页面
- 属性管理实现
- 产品管理实现
- 产品图片管理实现
- 产品属性值设置
- 用户管理实现
- 订单管理实现
- 前端
- 前台-首页
- 可运行的项目
- 静态资源
- ForeController
- home方法
- home.jsp
- homePage.jsp
- 前台-无需登录
- 注册
- 登录
- 退出
- 产品页
- 模态登录
- 分类页
- 搜索
- 前台-需要登录
- 购物流程
- 立即购买
- 结算页面
- 加入购物车
- 查看购物车页面
- 登录状态拦截器
- 其他拦截器
- 购物车页面操作
- 订单状态图
- 生成订单
- 我的订单页
- 我的订单页操作
- 评价产品
- 总结