[TOC]
## 步驟 1 : 先運行,看到效果,再學習
先將完整的 spring 項目(向老師要相關資料),配置運行起來,確認可用之后,再學習做了哪些步驟以達到這樣的效果。
## 步驟 2 : 模仿和排錯
在確保可運行項目能夠正確無誤地運行之后,再嚴格照著教程的步驟,對代碼模仿一遍。
模仿過程難免代碼有出入,導致無法得到期望的運行結果,此時此刻通過比較**正確答案** ( 可運行項目 ) 和自己的代碼,來定位問題所在。
采用這種方式,**學習有效果,排錯有效率**,可以較為明顯地提升學習速度,跨過學習路上的各個檻。
## 步驟 3 : 效果
重啟tomcat,通過訪問地址
`http://localhost:8080/tmall_ssm/admin_property_list?categoryId=30`
可以看到屬性管理的界面
注: 這categoryId=12是分類的id,根據你的實際運行情況,采取不同的id值

## 步驟 4 : Property
Property實體類已經和其他所有的實體類一起,在**所有逆向工程** 這個環節就一起自動生成好了。 不過僅僅有自動生成的實體類代碼,還不足以支撐業務需要,所以在Property基礎上增加了一個Category 字段。 這個屬性的用途將會在**編輯功能講解** 步驟里進行講解。
> 生成category字段對應getter/setter方法。
```
package com.dodoke.tmall.pojo;
public class Property {
private Integer id;
private String name;
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 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 : PropertyService
新建PropertyService,提供CRUD一套。
需要注意的是,因為在業務上需要查詢某個分類下的屬性,所以list方法會帶上對應分類的id。
```
package com.dodoke.tmall.service;
import java.util.List;
import com.dodoke.tmall.pojo.Property;
public interface PropertyService {
void add(Property c);
void delete(int id);
void update(Property c);
Property get(int id);
List list(int categoryId);
}
```
## 步驟 6 : PropertyServiceImpl
新增PropertyServiceImpl實現PropertyService對應的方法。通過調用自動生成的PropertyMapper就可以實現大部分方法了。
值得注意的是查詢的時候用到了輔助查詢類:PropertyExample
它的使用也很方便,這一行表示查詢categoryId字段
`example.createCriteria().andCategoryIdEqualTo(categoryId);`
```
package com.dodoke.tmall.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dodoke.tmall.mapper.PropertyMapper;
import com.dodoke.tmall.pojo.Property;
import com.dodoke.tmall.pojo.PropertyExample;
import com.dodoke.tmall.service.PropertyService;
@Service
public class PropertyServiceImpl implements PropertyService {
@Autowired
PropertyMapper propertyMapper;
@Override
public void add(Property c) {
propertyMapper.insert(c);
}
@Override
public void delete(int id) {
propertyMapper.deleteByPrimaryKey(id);
}
@Override
public void update(Property c) {
propertyMapper.updateByPrimaryKeySelective(c);
}
@Override
public Property get(int id) {
return propertyMapper.selectByPrimaryKey(id);
}
@Override
public List list(int categoryId) {
PropertyExample example =new PropertyExample();
example.createCriteria().andCategoryIdEqualTo(categoryId);
example.setOrderByClause("id desc");
return propertyMapper.selectByExample(example);
}
}
```
## 步驟 7 : PropertyController
然后就是控制器類,用于映射不同路徑的訪問。 這個類每個方法的詳解將在后續步驟一一展開。
```
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.Property;
import com.dodoke.tmall.service.CategoryService;
import com.dodoke.tmall.service.PropertyService;
import com.dodoke.tmall.util.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
@RequestMapping("")
@Controller
public class PropertyController {
@Autowired
CategoryService categoryService;
@Autowired
PropertyService propertyService;
@RequestMapping("admin_property_add")
public String add(Model model, Property p) {
propertyService.add(p);
return "redirect:admin_property_list?categoryId="+p.getCategory_id();
}
@RequestMapping("admin_property_delete")
public String delete(int id) {
Property p = propertyService.get(id);
propertyService.delete(id);
return "redirect:admin_property_list?categoryId="+p.getCategory_id();
}
@RequestMapping("admin_property_edit")
public String edit(Model model, int id) {
Property p = propertyService.get(id);
Category c = categoryService.get(p.getCategory_id());
p.setCategory(c);
model.addAttribute("p", p);
return "admin/editProperty";
}
@RequestMapping("admin_property_update")
public String update(Property p) {
propertyService.update(p);
return "redirect:admin_property_list?categoryId="+p.getCategory_id();
}
@RequestMapping("admin_property_list")
public String list(int categoryId, Model model, Page page) {
Category c = categoryService.get(categoryId);
PageHelper.offsetPage(page.getStart(),page.getCount());
List<Property> ps = propertyService.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/listProperty";
}
}
```
## 步驟 8 : listProperty.jsp和editProperty.jsp
然后是新建查詢和編輯的jsp頁面
## 步驟 9 : 查詢功能講解
查詢地址`admin_property_list`映射的是PropertyController的`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/listProperty.jsp`頁面
11. 在listProperty.jsp頁面上使用c:forEach 遍歷ps集合,并顯示

PropertyController片段:
```
@RequestMapping("admin_property_list")
public String list(int categoryId, Model model, Page page) {
Category c = categoryService.get(categoryId);
PageHelper.offsetPage(page.getStart(), page.getCount());
List<Property> ps = propertyService.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/listProperty";
}
```
listProperty.jsp:
```
<c:forEach items="${ps}" var="p">
<tr>
<td>${p.id}</td>
<td>${p.name}</td>
<td><a href="admin_property_edit?id=${p.id}"><span class="glyphicon glyphicon-edit"></span></a></td>
<td><a deleteLink="true" href="admin_property_delete?id=${p.id}"><span class="glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
```
## 步驟 10 : 增加功能講解
1. 在listProperty.jsp頁面提交數據的時候,除了提交屬性名稱,還會提交categoryId
2. 在PropertyController通過參數Property 接受注入
3. 通過propertyService保存到數據庫
4. 客戶端跳轉到admin_property_list,并帶上參數categoryId

listProperty.jsp:
```
<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 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>
```
PropertyController:
```
@RequestMapping("admin_property_add")
public String add(Model model, Property p) {
propertyService.add(p);
return "redirect:admin_property_list?categoryId=" + p.getCategoryId();
}
```
## 步驟 11 : 編輯功能講解
1. 在PropertyController的edit方法中,根據id獲取Property對象
2. 根據properoty對象的categoryId屬性獲取Category對象,并把其設置在Property對象的category屬性上
3. 把Property對象放在request的 "p" 屬性中
4. 服務端跳轉到`admin/editProperty.jsp`
``5. 在editProperty.jsp中顯示屬性名稱
6. 在editProperty.jsp中隱式提供id和categoryId( categoryId 通過 `p.category.id` 獲取)
```
<input type="hidden" name="id" value="${p.id}">
<input type="hidden" name="categoryId" value="${p.category.id}">
```

PropertyController:
```
@RequestMapping("admin_property_edit")
public String edit(Model model, int id) {
Property p = propertyService.get(id);
Category c = categoryService.get(p.getCategoryId());
p.setCategory(c);
model.addAttribute("p", p);
return "admin/editProperty";
}
```
editProperty.jsp:
```
<input type="hidden" name="id" value="${p.id}">
<input type="hidden" name="categoryId" value="${p.category.id}">
```
## 步驟 12 : 修改功能講解
1. 在PropertyController的update方法中獲取Property對象
2. 借助propertyService更新這個對象到數據庫
3. 客戶端跳轉到`admin_property_list`,并帶上參數categoryId

PropertyController:
```
@RequestMapping("admin_property_update")
public String update(Property p) {
propertyService.update(p);
return "redirect:admin_property_list?categoryId=" + p.getCategoryId();
}
```
## 步驟 13 : 刪除功能講解
1. 在PropertyController的delete方法中獲取id
2. 根據id獲取Property對象
3. 借助propertyService刪除這個對象對應的數據
4. 客戶端跳轉到`admin_property_list`,并帶上參數categoryId

```
@RequestMapping("admin_property_delete")
public String delete(int id) {
Property p = propertyService.get(id);
propertyService.delete(id);
return "redirect:admin_property_list?categoryId=" + p.getCategoryId();
}
```
## 步驟 14 : 其他-面包屑導航
在屬性管理頁面的頁頭,有一個面包屑導航
第一個連接跳轉到`admin_category_list`
第二個連接跳轉到`admin_property_list?categoryId=`
``
樣式用的是Bootstrap面包屑導航

```
<ol class="breadcrumb">
<li><a href="admin_category_list">所有分類</a></li>
<li><a href="admin_property_list?categoryId=${c.id}">${c.name}-屬性</a></li>
<li class="active">屬性管理</li>
</ol>
```
## 步驟 15 : `其他-分頁`
這里的分頁比起分類管理中的分頁多了一個參數categoryId
1. 在`PropertyController.list()` 方法中,把`&categoryId=` 參數設置到在page對象的param屬性上
2. 在adminPage.jsp頁面中通過`${page.param}`取出這個參數
## 步驟 16 : 自己做一遍
關于屬性管理這一塊的學習,推薦如下思路:
1. 先拿到本章節對應項目,配置好了跑一遍。
2. 根據接著的講解,把里面的每一塊代碼的邏輯,思路,設計思想搞明白,不明白的及時問老師。
3. 都理清楚之后,把可運行項目刪掉,按照:查詢,增加,刪除,編輯,修改的順序自己從頭到尾做一遍。
只有,能夠獨立的做出來,才叫做把這些知識點的內容轉化為自己的技能。
> 刪除中需要注意,屬性下有沒有對應屬性值,沒有屬性值才能刪除,學員自行添加相應邏輯。
- 項目簡介
- 功能一覽
- 前臺
- 后臺
- 開發流程
- 需求分析-展示
- 首頁
- 產品頁
- 分類頁
- 搜索結果頁
- 購物車查看頁
- 結算頁
- 確認支付頁
- 支付成功頁
- 我的訂單頁
- 確認收貨頁
- 確認收貨成功頁
- 評價頁
- 需求分析-交互
- 分類頁排序
- 立即購買
- 加入購物車
- 調整訂單項數量
- 刪除訂單項
- 生成訂單
- 訂單頁功能
- 確認付款
- 確認收貨
- 提交評價信息
- 登錄
- 注冊
- 退出
- 搜索
- 前臺需求列表
- 需求分析后臺
- 分類管理
- 屬性管理
- 產品管理
- 產品圖片管理
- 產品屬性設置
- 用戶管理
- 訂單管理
- 后臺需求列表
- 表結構設計
- 數據建模
- 表與表之間的關系
- 后臺-分類管理
- 可運行的項目
- 靜態資源
- JSP包含關系
- 查詢
- 分頁
- 增加
- 刪除
- 編輯
- 修改
- 做一遍
- 重構
- 分頁方式
- 分類逆向工程
- 所有逆向工程
- 后臺其他頁面
- 屬性管理實現
- 產品管理實現
- 產品圖片管理實現
- 產品屬性值設置
- 用戶管理實現
- 訂單管理實現
- 前端
- 前臺-首頁
- 可運行的項目
- 靜態資源
- ForeController
- home方法
- home.jsp
- homePage.jsp
- 前臺-無需登錄
- 注冊
- 登錄
- 退出
- 產品頁
- 模態登錄
- 分類頁
- 搜索
- 前臺-需要登錄
- 購物流程
- 立即購買
- 結算頁面
- 加入購物車
- 查看購物車頁面
- 登錄狀態攔截器
- 其他攔截器
- 購物車頁面操作
- 訂單狀態圖
- 生成訂單
- 我的訂單頁
- 我的訂單頁操作
- 評價產品
- 總結