使用SSM框架實(shí)現(xiàn)仿天貓主頁

打開天貓首頁苍柏,可以看到基本分為3大塊:

  1. 商品分頁列表+主推商品廣告
  2. 知名品牌區(qū)域
  3. “天貓超市”节视、“居家生活”等按主題分塊的商品區(qū)

這3大塊可以分別用不同的模塊處理:

  1. 分類管理模塊:建立不同分類以及用分類劃分商品澜搅;主推商品模塊鳄橘。
  2. 品牌管理模塊:返回品牌活動和品牌logo
  3. 主題模塊:返回主題列表以及主題內(nèi)部的商品

分類管理

首先建立一個分類管理的后臺頁面折柠,用來添加讳苦、刪除和修改分類带膜。


仿 分類管理.png

為了返回這個頁面,需要:

1.在數(shù)據(jù)庫建表category

CREATE TABLE `category` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`id`)
)

2.有了數(shù)據(jù)庫表鸳谜,再配合mybatis-generator插件就可以生成對應(yīng)的pojo膝藕、mapper和mapper的xml文件了。有了這些基本的數(shù)據(jù)操作有了咐扭,不過還需要做一些修改芭挽。用法參考IDEA中mybatis-generator插件的使用
3.需要一個Controller來處理當(dāng)前的頁面的業(yè)務(wù)滑废,所以建立一個CategoryController,并提供分裂列表獲取的方法:

package com.shiwei.tmall.controller;

import ***(略)

@Controller
@RequestMapping("")
public class CategoryController {

    @Autowired
    CategoryService categoryService;

    @RequestMapping("/admin_category_list")
    public String list(Model model, Page page){
        page.setCount(30);

        PageHelper.offsetPage(page.getStart(), page.getCount());

        List<Category> categories = categoryService.list();
        int total = (int) new PageInfo<>(categories).getTotal();


        page.setTotal(total);
        model.addAttribute("cs", categories);
        model.addAttribute("page", page);

        return "admin/listCategory";
    }
}

這里需要說明:

  • 使用了一個CategoryService對象,因?yàn)闉榱思軜?gòu)清晰袜爪,加入了service層蠕趁。每一個service對應(yīng)一個具體的業(yè)務(wù),controller通過組合調(diào)用不同的service來完成任務(wù)辛馆。在service層選擇連接不同的DAO層俺陋,可以是數(shù)據(jù)庫也可以是Redis,這樣可以是項(xiàng)目更靈活昙篙。
  • @Autowired是為了能夠自動注入categoryService腊状。自動注入的好處是解耦,spring容器會根據(jù)類型自動給categoryService賦值瓢对,如果要替換寿酌,只需要替換容器中的bean就可以了胰苏,而不需要動controller的代碼硕蛹。
  • 這里使用了PageHelper插件,它是通過MyBatis的插件功能硕并,修改了select語句的sql法焰,添加了limit屬性從而實(shí)現(xiàn)分頁。

4.創(chuàng)建CategoryService以及實(shí)現(xiàn)類CategoryServiceImpl:

public interface CategoryService {
    List<Category> list();

    void add(Category category);
    void delete(Integer id);
    Category get(Integer id);
    void update(Category category);
}
@Service
public class CategoryServiceImpl implements CategoryService {

    private CategoryMapper categoryMapper = null;

    @Autowired
    public CategoryServiceImpl(CategoryMapper categoryMapper){
        this.categoryMapper = categoryMapper;
    }

    @Override
    public List<Category> list() {
        CategoryExample example =new CategoryExample();
        example.setOrderByClause("id");
        return categoryMapper.selectByExample(example);
    }

    @Override
    public void add(Category category) {
        categoryMapper.insert(category);
    }

    @Override
    public void delete(Integer id) {
        categoryMapper.deleteByPrimaryKey(id);
    }

    @Override
    public Category get(Integer id) {
        return categoryMapper.selectByPrimaryKey(id);
    }

    @Override
    public void update(Category category) {
        categoryMapper.updateByPrimaryKeySelective(category);
    }
}

需要說明幾點(diǎn):

  • 為什么采用接口和實(shí)現(xiàn)類的模式而不是直接使用一個類倔毙?個人理解是為了以后更好的擴(kuò)展埃仪,當(dāng)有多個不同的邏輯實(shí)現(xiàn)并存時(shí),可以更方便的替換實(shí)現(xiàn)陕赃÷羊龋總的思想還是源于實(shí)現(xiàn)和聲明的分離,更深入的還需要以后更多項(xiàng)目的認(rèn)識么库。這里的討論也不錯
  • 使用MyBatis插件自動生成的mapper會額外增加一個example類傻丝,是用于查詢的。這個的一個好處是可以一定程度防止SQL注入诉儒,因?yàn)镾QL注入是通過把sql語句偽裝成參數(shù)傳入葡缰,從而修改了sql語句的意思。比如select * from user where id = 197837 and 1=1,這里的197837 and 1=1是傳入的參數(shù)忱反,但是卻會查出所有表數(shù)據(jù)泛释,但是使用example查詢時(shí)是參數(shù)綁定的方式,實(shí)際編譯的sql是select * from user where id = ?,這樣sql的語義至少不會因?yàn)閭鲄⒍恍薷摹?/li>

這樣分類信息就查詢出來了温算,下面就是對分類的修改怜校。

分類新增和編輯
仿 分類編輯.png

對應(yīng)的Controller代碼:

//保存分類圖片
private void saveCategoryImage(String homePath, MultipartFile imgFile, Integer categoryId) throws IOException{
    //判空處理
    if (imgFile == null || imgFile.isEmpty()){
        return;
    }
    File  imageFolder= new File(homePath+"/img/category");
    File file = new File(imageFolder,categoryId+".jpg");
    if(!file.getParentFile().exists()){
        file.getParentFile().mkdirs();
    }

    imgFile.transferTo(file);
}

@RequestMapping("/admin_category_add")
public String add(Category category, HttpSession session, MultipartFile imgFile) throws IOException {
    if (category.getName() == null){
        return "";
    }

    categoryService.add(category);
    saveCategoryImage(session.getServletContext().getRealPath("/"), imgFile, category.getId());

    return "redirect:/admin_category_list";
}

@RequestMapping("admin_category_update")
public String update(Category category, HttpSession session, MultipartFile imgFile, @Param("id") Integer id) throws IOException{
    categoryService.update(category);
    saveCategoryImage(session.getServletContext().getRealPath("/"), imgFile, category.getId());

    return "redirect:admin_category_list";
}

插入category數(shù)據(jù)很簡單,service和mapper都準(zhǔn)備好了注竿,這里需要注意的是上傳圖片的功能茄茁。上傳圖片使用HTTP POST的multipart/form-data類型宇智,這個前端都有相應(yīng)的框架支持,后端使用MultipartFile類型接受胰丁,它源于org.springframework.web.multipart包随橘,springMVC會處理從上傳文件到這個類型的轉(zhuǎn)化,前提是需要開啟轉(zhuǎn)化器的支持:

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>

拿到圖片數(shù)據(jù)后锦庸,寫入本地机蔗,這里使用了session.getServletContext().getRealPath("/")來獲取項(xiàng)目的絕對路徑,然后由此確定圖片存儲位置甘萧。

如果圖片功能更復(fù)雜萝嘁,比如還需要生成對應(yīng)的各種尺寸縮略圖、圖片是否違法違規(guī)的核查等扬卷,就需要一個圖片處理的service和單獨(dú)的處理模塊來支持了牙言。

產(chǎn)品和分類關(guān)聯(lián)

分類的作用是用來將產(chǎn)品分組,需要把產(chǎn)品歸類到一個個分類里怪得,所以需要:

  1. 一個產(chǎn)品的模型咱枉,包括數(shù)據(jù)庫表,pojo和對應(yīng)的mapper等
  2. 因?yàn)橐粋€分類里有多個產(chǎn)品徒恋,分類和產(chǎn)品屬于“1對多”的關(guān)系蚕断,所以通過在產(chǎn)品的表里加入外鍵來實(shí)現(xiàn)和分類的關(guān)聯(lián):
 CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `subTitle` varchar(255) DEFAULT NULL,
  `originalPrice` float DEFAULT NULL,
  `promotePrice` float DEFAULT NULL,
  `stock` int(11) DEFAULT NULL,
  `cid` int(11) DEFAULT NULL,
  `createDate` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_product_category` (`cid`),
  CONSTRAINT `fk_product_category` FOREIGN KEY (`cid`) REFERENCES `category` (`id`)
)

這里通過一個外鍵,把字段cid關(guān)聯(lián)到category表的id上了入挣。

  1. 需要一個創(chuàng)建產(chǎn)品并且劃分類別的頁面:
仿 產(chǎn)品編輯.png

產(chǎn)品的業(yè)務(wù)就需要新增一個ProductController,在這里新增一個產(chǎn)品添加和更新的方法:

//新增產(chǎn)品
@RequestMapping("admin_product_add")
public String add(Model model, Product p) {
    p.setCreateDate(new Date());
    productService.add(p);
    return "redirect:admin_product_list?cid="+p.getCid();
}
//更新產(chǎn)品
@RequestMapping("admin_product_update")
public String update(Product p) {
    productService.update(p);
    return "redirect:admin_product_list?cid="+p.getCid();
}

這里的參數(shù)直接就是Product亿乳,因?yàn)槭褂肧pringMVC框架可以自動合成pojo對象,比如這里Product里有字段cid,會自動填充HTTP請求參數(shù)里的同名字段径筏。如果參數(shù)不同名就會失敗葛假,也無法使用@RequestParam來關(guān)聯(lián)參數(shù)。

首頁展示
仿 前端 分類.png

有了分類列表的接口和產(chǎn)品管理的接口滋恬,首頁這部分就可以展示出來聊训。

品牌管理

需要:

  1. 品牌模型,包括數(shù)據(jù)庫表夷恍、pojo和mapper等
  2. 產(chǎn)品需要關(guān)聯(lián)品牌
  3. 品牌活動模塊
  4. 品牌的創(chuàng)建魔眨、編輯等基本操作

創(chuàng)建數(shù)據(jù)庫表:

create table brand(
    id int not null auto_increment,
    name varchar(127),
    primary key(id)
    );

然后使用Mybatis-generator生成對應(yīng)pojo和mapper文件,再就是增加一個BrandController:

@RequestMapping("/")
@Controller
public class BrandController {
    @Autowired
    BrandService brandService;

    @RequestMapping("/brandList")
    public String list(Model model, Page page){
        
        page.setCount(30);
        PageHelper.offsetPage(page.getStart(), page.getCount());
        
        List<Brand> brands = brandService.list();
        page.setTotal((int)new PageInfo<>(brands).getTotal());
        
        model.addAttribute("brands", brands);
        
        return "admin/listBrand";
    }
}

同樣需要建立配套的service和實(shí)現(xiàn)類:

public interface BrandService {
    List<Brand> list();
}
...................................
@Service
public class BrandServiceImpl implements BrandService {

    @Autowired
    BrandMapper brandMapper;

    @Override
    public List<Brand> list() {
        BrandExample example = new BrandExample();
        example.setOrderByClause("name"); //默認(rèn)按名字排序
        return brandMapper.selectByExample(example);
    }
}

注意要加@Controller注解和@Service注解酿雪,否則Spring容器找不到它們就無法做URL映射和依賴注入了遏暴。

增加品牌

現(xiàn)在品牌的數(shù)據(jù)還是空的,需要增加品牌的功能指黎,首先在service里增加方法void addBrand(Brand brand);, BrandServiceImpl里添加實(shí)現(xiàn):

@Override
@Transactional
public void addBrand(Brand brand) {
    brandMapper.insert(brand);
}

再在controller里添加增加的方法:

@RequestMapping("/addBrand")
public String addBrand(HttpSession session, Brand brand, MultipartFile imgFile) throws IOException{

    brandService.addBrand(brand);
    imageSaveService.saveImage(session.getServletContext().getRealPath("/"), ImageSaveService.BRAND_IMAGE_KEY, imgFile, brand.getId());

    return "redirect:/brandList";
}

因?yàn)閳D片存儲功能和分類那一一致朋凉,所以提取這個功能到一個單獨(dú)的service類里了,通過key來識別存儲不同模塊的圖片:

@Service
public class ImageSaveService {

    public static final String CATEGORY_IMAGE_KEY = "category";
    public static final String BRAND_IMAGE_KEY = "brand";

    //保存圖片
    public void saveImage(String homePath, String pathKey, MultipartFile imgFile, Integer itemId) throws IOException {
        //判空處理
        if (imgFile == null || imgFile.isEmpty()){
            return;
        }
        File imageFolder= new File(homePath+"/img/"+pathKey);
        File file = new File(imageFolder,itemId+".jpg");
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }

        imgFile.transferTo(file);
    }
}

后端品牌管理頁面:


仿 品牌管理.png

有了數(shù)據(jù)醋安,再回到主頁杂彭,需要顯示塊狀的品牌列表墓毒,所以提供一個新的接口,只返回一部分并且隨機(jī)的品牌列表:

//BrandController部分
@RequestMapping("/randomHomeBrandList")
@ResponseBody
public List randomHomeBrandList(int count){
    return brandService.randomList(count);
}

//BrandServiceImpl部分
@Override
public List<Brand> randomList(int count) {
    return brandMapper.randomSelect(count);
}
使用@ResponseBody是為了返回json類型亲怠,前端可以通過這個接口單獨(dú)拉取品牌數(shù)據(jù)所计,換一批時(shí)可以局部替換。

關(guān)鍵是mapper.xml里的代碼:

<select id="randomSelect" parameterType="int" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from brand
ORDER BY rand()
LiMIT 0, #{count}
</select>

因?yàn)槭褂昧薽ysql數(shù)據(jù)庫团秽,所以用了一個ORDER BY rand()來進(jìn)行隨機(jī)查詢主胧,如果是其他數(shù)據(jù)庫可以加條件判斷來確定sql.

前端的效果:


品牌列表.jpg
品牌活動
  1. 建立品牌活動表和對應(yīng)的DAO層
  2. 建立后臺編輯頁面
  3. 建立對應(yīng)的controller和service
create table BrandActivity(
    id int not null auto_increment,
    title varchar(20),
    subtitle varchar(50),
    mainBrand int,
    pageLink varchar(255),
    primary key(id),
    constraint fk_ba_b foreign key (mainBrand) references brand(id)
);

創(chuàng)建了一個數(shù)據(jù)庫表,包含標(biāo)題习勤、小標(biāo)題踪栋、圖片,以及一個關(guān)聯(lián)到品牌的外鍵图毕。

@RequestMapping("brandActivityManage")
public String brandActivityManage(Model model, Page page){
    
    PageHelper.offsetPage(page.getStart(), page.getCount());
    List<BrandActivity> brandActivities = brandActivityService.list();


    List<Brand> brands = brandService.list();
    model.addAttribute("brands",brands);
    model.addAttribute("brandActivities",brandActivities);
    for (BrandActivity brandActivity: brandActivities){
        int mbid = brandActivity.getMainBrand();
        for (Brand b: brands){
            if (mbid == b.getId()){
                brandActivity.setMainBrandName(b.getName());
            }
        }
    }

    return "admin/listBrandActivity";
}

這個是返回后臺管理頁面的方法夷都,內(nèi)部查詢了所有的品牌活動。BrandActivity的pojo里除了數(shù)據(jù)庫表對應(yīng)的屬性予颤,還增加了一個mainBrandName,因?yàn)閿?shù)據(jù)庫表里存儲的是id,顯示的時(shí)候需要轉(zhuǎn)換為名稱囤官。

因?yàn)樾枰械钠放谱鲞x擇,所以本來就要查詢出所有品牌列表荣瑟,就直接在這用一個雙層for循環(huán)給mainBrandName賦值了治拿。否則需要用mainBrandId去數(shù)據(jù)庫查到對應(yīng)的Brand數(shù)據(jù)摩泪。

其他的增刪改查的方法都大同小異笆焰,就沒什么好寫的了。

后臺管理頁面效果:


品牌活動管理.png

前臺效果:


品牌活動.png

為了單獨(dú)拉取活動數(shù)據(jù)见坑,增加了一個接口:

@RequestMapping("/brandActivityList")
@ResponseBody
public List<BrandActivity> list(){
    return brandActivityService.list();
}

接口很簡單嚷掠,同樣是用@ResponseBody返回json類型數(shù)據(jù)。前段隨便網(wǎng)上搜了的代碼荞驴,為了掩飾湊合著用不皆,思路就是ajax獲取數(shù)據(jù),事先在jsp文件里留一個div熊楼,比如這個<div class="brand-activities"></div>,然后請求完數(shù)據(jù)往這個div里加入內(nèi)容:

//brand-activities就是要修改的div
$.ajax({
    type: "GET",
    url: "./brandActivityList",
    data: null,
    dataType: "json",
    success: function(data){
        var item = "";
        $(".brand-activities").empty(); //清空
        for(var i = 0 ; i < data.length; i++) {
            item += "<li class='brand-activity-item'> <div class='brand-activity-header'>"+data[i].title+"  "+data[i].subtitle+
                    "<a style='text-align: right;vertical-align: center font-size: 13pt; flex: 1'>更多</a> </div>  <div class='brand-activity-img' style='text-align: center;width: 100%'> <img src='./img/brandActivity/"+data[i].id+
                    ".jpg' style='width: 100%;vertical-align: middle;'> </div> <a class='brand-mask' href="+data[i].pageLink+
                    "> </a> </li>"
        }
        $(".brand-activities").append(item);   // 顯示到里面

    }
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霹娄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鲫骗,更是在濱河造成了極大的恐慌犬耻,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件执泰,死亡現(xiàn)場離奇詭異枕磁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)术吝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門计济,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茸苇,“玉大人,你說我怎么就攤上這事沦寂⊙埽” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵传藏,是天一觀的道長则果。 經(jīng)常有香客問我,道長漩氨,這世上最難降的妖魔是什么西壮? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮叫惊,結(jié)果婚禮上款青,老公的妹妹穿的比我還像新娘。我一直安慰自己霍狰,他們只是感情好抡草,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蔗坯,像睡著了一般康震。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宾濒,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天腿短,我揣著相機(jī)與錄音,去河邊找鬼绘梦。 笑死橘忱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卸奉。 我是一名探鬼主播钝诚,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼榄棵!你這毒婦竟也來了凝颇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤疹鳄,失蹤者是張志新(化名)和其女友劉穎拧略,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尚辑,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辑鲤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杠茬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片月褥。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡弛随,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宁赤,到底是詐尸還是另有隱情舀透,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布决左,位于F島的核電站愕够,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏佛猛。R本人自食惡果不足惜惑芭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望继找。 院中可真熱鬧遂跟,春花似錦、人聲如沸婴渡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽边臼。三九已至哄尔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柠并,已是汗流浹背岭接。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堂鲤,地道東北人亿傅。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像瘟栖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谅阿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言:首先這個項(xiàng)目是基于網(wǎng)站http://www.how2j.com 參考而完成的一個學(xué)習(xí)項(xiàng)目半哟,僅供學(xué)習(xí)參考。 項(xiàng)...
    RichardYee閱讀 584評論 0 0
  • 經(jīng)過上篇文章Spring签餐、Spring MVC與Mybatis整合工程搭建我們便將SSM的環(huán)境搭建了寓涨,接下來我們便...
    codingXiaxw閱讀 22,712評論 1 38
  • 作用: SSM是sping+springMVC+mybatis集成的框架。是標(biāo)準(zhǔn)的MVC模式氯檐,將整個系統(tǒng)劃分為表現(xiàn)...
    夢晝初心閱讀 27,267評論 0 16
  • Mybatis 創(chuàng)建數(shù)據(jù)庫 Mybatis框架原理(掌握) 1戒良、Mybatis 是什么? Mybatis 是一個持...
    printf200閱讀 710評論 0 4
  • 一片秋葉冠摄,幾朵白云鹿足印小徑糯崎,鳥鳴伴風(fēng)聲空枝濯濯几缭,蒼然蕭瑟滿地落葉,燦燦澄澄駐足林間沃呢,聽濤聲陣陣登高望遠(yuǎn)年栓,目送南歸...
    叢子_c03a閱讀 328評論 1 4