sql语句查询商品的一二三级分类都是一个字段怎么办_畅购商城(三):商品管理...
好好學習,天天向上
本文已收錄至我的Github倉庫「DayDayUP」:github.com/RobodLee/DayDayUP,歡迎Star
小練手
這里有三個小練手的任務,內容比較簡單,就是對一張表的增刪查改,一些簡單的CRUD而已。代碼我就不貼了,簡單記錄一下思路以及遇到的問題,步驟和暢購商城(一):環境搭建中的品牌表操作類似,可以去參考,想要代碼的小伙伴可以去我的Github獲取,或者是去配套資料里面找。
相冊管理
相冊是用于存儲圖片的管理單元,我們通常會將商品的圖片先上傳到相冊中,在添加商品時可以直接在相冊中選擇,獲取相冊中的圖片地址,保存到商品表中。這一小節的任務就是完成對相冊表的增刪查改操作。相冊表的結構如下:
從圖中可以看出,相冊表有編號,相冊名稱,相冊封面,圖片列表四個字段。其中相冊封面中是一張圖片的信息,圖片的內容包括在FastDFS中存儲的路徑,唯一標識符uuid以及圖片的狀態。圖片列表是一組圖片,格式是json數組。
所以要實現相冊管理的功能,第一步「在changgou-service-goods-api工程中創建com.robod.goods.pojo.Album類」,然后「在changgou-service-goods工程中分別創建出AlbumController,AlbumService,AlbumServiceImpl,AlbumMapper」四個文件,最后「在里面寫出相應的代碼」即可。
這里有三個需要注意的是,第一個是AlbumController中用到的Page類是com.github.pagehelper.Page,不是com.robod.entity.Page,不要導錯了。第二個是資料里給的代碼中AlbumController用了Map傳參,這是個不好的習慣,改成數據模型,也就是Album。第三個問題是數據庫中的字段和Album類中的字段不匹配,一個是image_items,另一個是imagItems,要是用@Results注解就有點麻煩,剛好后者是前者的駝峰形式,而且有很多都是這種情況,所以簡單一點的做法就是在changgou-parent/changgou-service/changgou-service-goods/src/main/resources/application.yml中添加如下配置:
mybatis:??configuration:
????map-underscore-to-camel-case:?true?#自動將以下畫線方式命名的數據庫列映射到?Java?對象的駝峰式命名屬性中
當代碼都寫完之后,就來測試一下,
結果就不貼了,測試是成功了,開始下一個功能?
規格參數模板
規格參數模板是用于管理規格參數的單元。規格是例如顏色、手機運行內存等信息,參數是例如系統:安卓(Android)后置攝像頭像素:2000萬及以上 ?熱點:快速充電等信息 。
這一節的任務就是完成對模板表的增刪查改。步驟和上一節一樣,先是「在changgou-service-goods-api工程中創建com.robod.goods.pojo.Template」,然后「在changgou-service-goods工程中分別創建出TemplateController,TemplateService,TemplateServiceImpl,TemplateMapper」四個文件,最后「寫出對應的代碼」即可。
在做這個小任務時遇到了一個問題,我之前不是為了鍛煉寫SQL語句就沒用通用Mapper么,但是這幾個小任務的代碼都是類似的,沒必要每個都寫一遍,所以我就從資料中直接復制粘貼。因為資料中給的代碼是用通用Mapper的,運行的時候就出Bug了。
org.mybatis.spring.MyBatisSystemException:?nested?exception?is?org.apache.ibatis.builder.BuilderException:?Error?invoking?SqlProvider?method?(tk.mybatis.mapper.provider.base.BaseSelectProvider.dynamicSQL).??Cause:?java.lang.InstantiationException:?tk.mybatis.mapper.provider.base.BaseSelectProvider出現這個bug是因為我之前用的@MapperScan注解是org.mybatis.spring.annotation.MapperScan,如果想用通用Mapper的話,就得換成tk.mybatis.spring.annotation.MapperScan,直接換掉就好了。
商品分類
商品分類一共分三級管理,主要作用是在網站首頁中顯示商品導航,以及在管理后臺管理商品時使用(下面的流程分析中會說明這個表的作用)。
步驟還是老樣子,先是「在changgou-service-goods-api中創建com.robod.goods.pojo.Category」,然后「在changgou-service-goods工程中分別創建出CategoryController,CategoryService,CategoryServiceImpl,CategoryMapper」,最后「寫出相應的增刪查改代碼」即可。
----------------|--com.robod
|----controller
|------CategoryController.java
|----Service
|------impl
|--------CategoryServiceImpl.java
|------intf
|--------CategoryService.java
|----Mapper
|------CategoryMapper.java
----------------
SPU和SKU
- SPU(Standard Product Unit):標準化產品單元
是商品信息聚合的最小單位,是一組可復用、易檢索的標準化信息的集合,該集合描述了一個產品的特性。通俗點講,屬性值、特性相同的商品就可以稱為一個SPU。說人話就是「同款商品的公共的不可變的屬性」。比如小米10Pro,不管是哪一部,廠家,品牌,分類這些屬性是所有小米10Pro所共有的而且不可變的,這就是SPU。
- Stock Keeping Unit:最小存貨單位
庫存保有單位即庫存進出計量的單位, 可以是以件、盒、托盤等為單位。SKU是物理上不可分割的最小存貨單元。在使用時要根據不同業態,不同管理模式來處理。在服裝、鞋類商品中使用最多最普遍。用人話說就是「每款商品獨有的屬性」。比如說,一款小米10Pro是珍珠白和8+256的,另一款是星空藍和12+256的,通過這些屬性的不同組合,可以唯一標識不同款的產品,這就是SKU。
- 表結構
商品發布流程分析
1. 三級分類的選擇
一個商城的分類實在是太多了,所以我們需要有三級分類來減少頁面顯示的內容,京東就是三級分類,比如電腦——>電腦配件——>顯示器。在前面的小練手中提到過,分類表中有個字段叫parent_id,當我們選擇了一級分類的時候,就拿著這個它的id去查詢出子分類,然后顯示出來。假如現在選擇了一個分類電腦,它的id是5,那么查詢出電腦整機、電腦配件這幾個分類的parent_id是5,就說明這些是電腦的子分類。二級分類和三級分類的關系也是同樣的。那么一開始是如何顯示一級分類的呢?很簡單,一級分類的parent_id是0,查詢出parent_id是0就說明是一級分類。
2. SPU信息填寫
在這一步中有個商品品牌選項,自然不會去顯示所有的品牌,只會顯示與所選分類相關的品牌。在數據庫中有一張表叫做tb_category_brand,記錄了分類與品牌的對應關系。比如現在選擇了三級分類是手機,它的id是10,從tb_category_brand中查詢出對應的brand_id是134,156,178......,那么就拿著這些brand_id去品牌表中查詢出對應的品牌名稱,顯示在界面上。
3.SKU信息填寫
這個界面的規格和參數是怎么篩選出并展示的呢?在前面小練手部分的規格參數模板部分的表結構可以得出,通過分類id去分類表中查出對應的template_id,再通過查詢到的template_id去規格表和參數表中查詢出對應的數據就OK了。
功能實現
在分析完流程之后,就可以來實現對應的功能了。
1. 根據parent_id查詢所有子分類
流程都已經分析過了,直接上代碼:
/**?*?根據父ID查詢
?*?Controller層?CategoryController.java
?*/
@RequestMapping(value?="/list/{pid}")
public?Result?findByParentId(@PathVariable(value?=?"pid")Integer?pid){
????//根據父節點ID查詢
????List?list?=?categoryService.findByParentId(pid);return?new?Result(true,StatusCode.OK,"查詢成功",list);
}
----------------------------------------------------??//?Service層?CategorySerivceImpl.java@Overridepublic?List?findByParentId(Integer?pid)?{return?categoryMapper.findByParentId(pid);
}
------------------------------------------/***
?*?根據父節點ID查詢
?*?@param?pid:父節點ID
?*?Dao層?CategoryMapper.java
?*/@Select("SELECT?*?FROM?tb_category?WHERE?parent_id?=?#{id}")public?List?findByParentId(Integer?pid);
2. 根據分類id查詢對應的品牌集合
/**?*?根據分類id查詢對應的品牌集合
?*?@param?categoryId
?*?@return
?*?Controller層?BrandController.java
?*/
@GetMapping("/category/{id}")
public?Result>?findByCategory(@PathVariable("id")?int?categoryId)?{
????List?brands?=?brandService.findByCategory(categoryId);return?new?Result<>(true,StatusCode.OK,"查詢成功",brands);
}
---------------------------------------------------------------------------//Service層?BrandSerivceImpl.java??@Overridepublic?List?findByCategory(int?categoryId)?{return?brandMapper.findByCategory(categoryId);
}
---------------------------------------------------------------------------/**
?*?根據分類id查詢對應的品牌集合
?*?@param?categoryId
?*?@return
?*?Dao層?BrandMapper.java
?*/@Select("SELECT?*?FROM?tb_brand?WHERE?id?IN?"?+"(SELECT?brand_id?FROM?tb_category_brand?WHERE?category_id=#{categoryId})")public?List?findByCategory(int?categoryId);
3.根據商品的分類id查詢該分類對應的規格列表
/**?*?根據商品分類的ID?查詢該分類對應的?規格的列表
?*?Controller層?SpecController.java
?*/
@GetMapping("/category/{id}")
public?Result>?findByCategoryId(@PathVariable?Integer?id){
????List?specList?=?specService.findByCategoryId(id);return?new?Result<>(true,StatusCode.OK,"查詢規格的列表成功",specList);
}
------------------------------------------------------------------------//Service層?SpecServiceImpl.java@Overridepublic?List?findByCategoryId(Integer?id)?{//1.先根據商品分類的ID?獲取模板的IDint?templateId?=?categoryMapper.findById(id).getTemplateId();//2.再根據模板的ID?獲取模板對應的規格的列表return?specMapper.findByTemplateId(templateId);
}
-------------------------------------------------------------------------/**
?*?根據模板id查詢規格集合
?*?@param?templateId
?*?@return
?*?Dao層?SpecMapper.java
?*/@Select("select?*?from?tb_spec?where?template_id=#{templateId}")public?List?findByTemplateId(int?templateId);
4. 根據商品的分類id,查詢對應的參數列表
/**?*?根據商品的分類id,查詢對應的參數列表
?*?@param?id
?*?@return
?*?Controller層?ParaController.java
?*/
@GetMapping("/category/{id}")
public?Result>?findParaByCategoryId(@PathVariable(name?=?"id")?Integer?id)?{
????List?paraList?=?paraService.findParaByCategoryId(id);return?new?Result<>(true,?StatusCode.OK,?"參數列表查詢成功",?paraList);
}
--------------------------------------------------------------------------------------------//Service層?PataServiceImpl.java@Overridepublic?List?findParaByCategoryId(Integer?id)?{//1.根據分類的ID?獲取到模板的IDint?templateId?=?categoryMapper.findById(id).getTemplateId();//2.根據模板的ID?獲取參數的列表?返回return?paraMapper.findByTemplateId(templateId);
}
--------------------------------------------------------------------------------------------/**
?*?根據模板的ID,獲取參數的列表
?*?@param?templateId
?*?@return
?*?Dao層?ParaMapper.java
?*/@Select("select?*?from?tb_para?where?template_id=#{templateId}")public?List?findByTemplateId(Integer?templateId);
5. 添加商品功能
一個商品由一個Spu和一組Sku組成,所以「在changgou-service-goods-api工程的com.robod.goods.pojo包下創建一個類Goods作為商品類」。
@Datapublic?class?Goods?implements?Serializable?{
????//SPU
????private?Spu?spu;
????//SKU集合
????private?List?skuList;
}
然后在「SpuController,SpuServiceImpl中寫出相應代碼」即可。
/**?*?添加商品Goods(SPU+SKU)
?*?Controller層?SpuController.java
?*/
@PostMapping("/save")
public?Result?save(@RequestBody?Goods?goods){
????spuService.save(goods);
????return?new?Result(true,StatusCode.OK,"保存商品成功",null);
}
----------------------------------------------------------------
//Service層?SpuService.java
@Override
public?void?save(Goods?goods)?{
????//新增spu
????Spu?spu?=?goods.getSpu();
????spu.setId(idWorker.nextId());
????spuMapper.insertSelective(spu);
????Category?category?=?categoryMapper.findById(spu.getCategory3Id());
????Brand?brand?=?brandMapper.findById(spu.getBrandId());
????//新增sku
????List?skuList?=?goods.getSkuList();
????LocalDateTime?time?=?LocalDateTime.now();for?(Sku?sku?:?skuList)?{
????????StringBuilder?name?=?new?StringBuilder(spu.getName());if?(sku.getSpec()!=null)?{
????????????Map?keyMap?=?JSONObject.parseObject(sku.getSpec(),Map.class);for?(String?spec?:?keyMap.keySet())?{
????????????????name.append(spec);
????????????}
????????}
????????sku.setId(idWorker.nextId());
????????sku.setName(name.toString());
????????sku.setCreateTime(time);
????????sku.setUpdateTime(time);
????????sku.setSpuId(spu.getId());
????????sku.setCategoryId(category.getId());
????????sku.setCategoryName(category.getName());
????????sku.setBrandName(brand.getName());
????????skuMapper.insertSelective(sku);
????}
}
視頻中用的是Date,但是Date現在已經不推薦用了,所以我改成了LocalDateTime。
6. 根據spu_id查詢商品信息
思路就是根據spu_id查詢出Spu和一組Sku,組合成一個Goods。
/**?*?根據點擊到的商品的spu_id獲取商品
?*?@param?id
?*?@return
?*?Controller層?SpuController.java
?*/
@GetMapping("/goods/{id}")
public?Result?findGoodsById(@PathVariable?long?id){
????Goods?goods?=?spuService.findGoodsById(id);
????return?new?Result(true,StatusCode.OK,"查詢goods數據成功",goods);
}
-----------------------------------------------------------------------------//?Service層?SpuServiceImpl.java@Overridepublic?Goods?findGoodsById(long?id)?{
????Goods?goods?=?new?Goods();
????Spu?spu?=?spuMapper.findById(id);
????goods.setSpu(spu);
????List?skuList?=?skuMapper.findBySpuId(spu.getId());
????goods.setSkuList(skuList);return?goods;
}
--------------------------------------------------------------------------------// Dao層?略
7. 修改商品
因為修改商品是在添加商品的界面操作的,等于是重新添加了一遍。所以方法就是根據spu_id把原有的sku刪除,然后重新添加,從而實現修改商品的功能。直接修改SpuServiceImpl中的代碼就可以了:
@Overridepublic?void?save(Goods?goods)?{
????Spu?spu?=?goods.getSpu();
????if?(spu.getId()==null)?{
????????//新增spu
????????spu.setId(idWorker.nextId());
????????spuMapper.insertSelective(spu);
????}?else?{
????????//修改spu并刪除sku
????????spuMapper.updateByPrimaryKeySelective(spu);
????????Sku?sku?=?new?Sku();
????????sku.setSpuId(spu.getId());
????????skuMapper.delete(sku);
????}
????Category?category?=?categoryMapper.findById(spu.getCategory3Id());
????Brand?brand?=?brandMapper.findById(spu.getBrandId());
????//新增sku
????List?skuList?=?goods.getSkuList();
????LocalDateTime?time?=?LocalDateTime.now();for?(Sku?sku?:?skuList)?{
????????StringBuilder?name?=?new?StringBuilder(spu.getName());if?(sku.getSpec()!=null)?{
????????????Map?keyMap?=?JSONObject.parseObject(sku.getSpec(),Map.class);for?(String?spec?:?keyMap.keySet())?{
????????????????name.append(spec);
????????????}
????????}
????????sku.setId(idWorker.nextId());
????????sku.setName(name.toString());
????????sku.setCreateTime(time);
????????sku.setUpdateTime(time);
????????sku.setSpuId(spu.getId());
????????sku.setCategoryId(category.getId());
????????sku.setCategoryName(category.getName());
????????sku.setBrandName(brand.getName());
????????skuMapper.insertSelective(sku);
????}
}
8. ?商品審核
商品審核有兩點,先判斷是否被刪除,被刪除就不用審核了,然后就將status和is_marketable置為1表示已審核并上架。代碼很簡單,一條sql就搞定了。
/**?*?審核商品?自動上架
?*?@param?id??spu的ID
?*?@return
?*?Controller層?SpuController.java
?*/
@PutMapping("/audit/{id}")
public?Result?auditSpu(@PathVariable(name="id")long?id){
????spuService.auditSpu(id);
????return?new?Result(true,StatusCode.OK,"審核通過");
}
------------------------------------------------------------------
//Service層?SpuServiceImpl.java
@Override
public?void?auditSpu(long?id)?{
????if?(spuMapper.audit(id)?==?0)?{
????????throw?new?RuntimeException("操作失敗");
????}
}
------------------------------------------------------------------
/**
?*?審核商品
?*?@param?id
?*?@return
?*?Dao層?SpuMapper.java
?*/
@Update("update?tb_spu?set?status=1,is_marketable=1?where?is_delete=0?and?id?=?#{id}")
int?audit(long?id);
Dao層返回一個數,表示操作的行數,如果等于0則說明沒有數據被修改,情況可能有三種,商品已經被審核過了,或者是已經被刪除了,再或者是商品壓根就不存在??傊褪沁@個操作失敗了,直接拋一個異常”審核失敗“。
9. 上架/下架 商品
/**?*?上架商品
?*?@param?id
?*?@return
?*?Dao層?SpuMapper.java
?*/
@Update("update?tb_spu?set?is_marketable=1?where?id=#{id}?and?is_delete=0?and?is_marketable=0?and?status=1")
int?putSpu(long?id);
和上面一個例子是一樣的,要上架一個商品,必須符合未被刪除,未被上架,通過審核三個條件,否則上架失敗。
下架則要滿足未被刪除,已經上架,通過審核三個條件。
/**?*?商品下架
?*?@param?id
?*?@return
?*?Dao層?SpuMapper.java
?*/
@Update("update?tb_spu?set?is_marketable=0?where?id=#{id}?and?is_delete=0?and?is_marketable=1?and?status=1")
public?int?pullSpu(long?id);
10. 批量 上架/下架 商品
本以為這只是一個很簡單的功能,結果遇到了一堆坑。先把批量上架的代碼貼出來吧,批量下架就不說了,都是一樣的。
//Service層?SpuServiceImpl.java@Override
public?int?putMany(long[]?ids)?{
????int?num?=?spuMapper.putMany(ids);
????if?(num?==?0)?{
????????throw?new?RuntimeException("上架失敗");
????}
????return?num;
}
---------------------------------------------------------------------
//Dao層
@UpdateProvider(type?=?SpuMapperProvider.class,method?=?"putMany")
int?putMany(@Param("ids")?long[]?ids);
class?SpuMapperProvider?{
????public?String?putMany(long[]?ids)?{
????????String?s?=?Arrays.toString(ids);
????????s?=?s.substring(1,?s.length()?-?1);
????????return?"update?tb_spu?set?is_marketable=1?where?id?in("?+?s
????????????????+?")?and?is_delete=0?and?is_marketable=0?and?status=1";
????}
}
有的小伙伴看到這里可能就要問了:你為什么不直接把ids作為占位符,然后從Service層傳過去呢?我也是到昨天才知道的,把數組或者集合傳到Dao層后拼接成的sql語句是這樣的?
很明顯不是我想要的,那有的小伙伴可能又要問了:那你既然在SpuMapperProvider里面拼接sql,為什么不在Service層直接把ids轉換成String然后傳到Dao層的占位符里呢?如果是這樣的話,那么拼接后的sql又變成了這樣?
而正確的sql語句應該是這樣的?
update?tb_spu?set?is_marketable=1?WHERE?id?in(1281162712482709504,?10000001516600)?and?is_delete=0?and?is_marketable=0?and?status=1;Mybatis知道你傳的是String,所以自動幫你加了兩個引號。要不是有MyBatis Log Plugin這個強大的插件把拼接好的sql語句打印了出來,我都不知道錯在哪兒。其實這都不是最坑的,這好歹還能看到問題在哪兒。我昨天在調試的時候打了幾個斷點,Dao層也打了,然后調試功能死活用不了,不是斷點進不去,就是postman那邊一直說連接被拒絕,搞了老半天,還以為是端口被占了,重啟一下也不行,最后還是一氣之下把所有斷點全刪了才發現問題所在,Dao打斷點調試就用不了,真的是。。。哎!
小結
商品管理功能就寫完了,這篇文章先是介紹了幾個小練手的任務,然后介紹了商品發布的流程,最后實現了和商品管理有關的一系列功能。如果我的文章對你有些幫助,不要忘了點贊,收藏,轉發,關注。要是有什么好的意見歡迎在下方留言。讓我們下期再見!
微信公眾號總結
以上是生活随笔為你收集整理的sql语句查询商品的一二三级分类都是一个字段怎么办_畅购商城(三):商品管理...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python corr画图_用一张很丑的
- 下一篇: python中的gui界面编程_pyth