第四章
规格管理、模板管理、分类管理以及商品发布
优就业.JAVA教研室
学习目标
目标一:规格管理
目标二:模板管理
目标三:商品分类管理
目标四:了解电商概念SPU 和SKU
目标五:新增商品、修改商品
目标六:删除商品
目标七:审核商品
目标八:注解式事务
一 代码生成器
1 代码生成
我们接下来使用《优就业JAVA代码生成器1.0》来完成代码的编写。生成后将代码拷贝到工程中。具体步骤如下:
(1)导入(资源\配套软件\dongyimaiMakeCode)代码生成器工程到开发工具。
(2)修改数据库连接配置:打开src/main/resources/properties/db.properties
#pojo实体类所在包
pojoPackage=com.offcn.sellergoods.pojo
#Dao接口所在包
mapperPackage=com.offcn.sellergoods.dao
#service服务接口所在包
serviceInterfacePackage=com.offcn.sellergoods.service
#service服务实现类所在包
serviceInterfaceImplPackage=com.offcn.sellergoods.service.impl
#controller控制器所在包
controllerPackage=com.offcn.sellergoods.controller
#feignFeign调用接口所在包
feignPackage=com.offcn.sellergoods.feign
#是否开启swagger注解
enableSwagger=true
#swagger-ui所在目录
swaggeruipath=com.offcn.swagger
#服务名,提供feign调用
serviceName=sellergoods
#数据库地址、账号密码
url=jdbc:mysql://192.168.188.1:3306/dongyimai-db
uname=root
pwd=root
driver=com.mysql.jdbc.Driver
(3)运行代码生成主程序:CodeApplication及可生成代码。
(4)提示成功后,到生成路径去找生成的代码,并拷贝到我们的工程中。
Pojo、Dao、Service、ServiceImpl、Controller都生成到了 /src/main/java目录下
2 代码拷贝
将商品相关代码拷贝到工程
(1)拷贝实体类
(2)拷贝Dao层接口
(3)拷贝服务接口
(4)拷贝服务实现类
(5)拷贝控制器
二 Swagger2的使用
1 配置Swagger文档
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。
1) 在dongyimai-parent工程中pom.xml引入Swagger2和swagger ui
<!-- springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2) 在模块中编写配置类(@EnableSwagger2 开启swagger2自动生成api文档功能)
@Configuration
@EnableSwagger2
public class SwaggerConfig {
//配置文档属性
private ApiInfo getApiInfo(){
return new ApiInfoBuilder().title("商品微服务接口文档")
.description("给前端妹子看的")
.version("1.0")
.termsOfServiceUrl("http://www.ujiuye.com")
.build();
}
//创建文档配置对象
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(getApiInfo())
.groupName("group1")
.select()
.build();
}
}
3)访问文档
http://localhost:9001/swagger-ui.html
试着操作一下品牌管理的内容~
三 规格管理
1 需求分析
规格管理是用于管理规格选项的单元。规格是例如颜色、手机运行内存等信息,选项是例如系统:安卓(Android)后置摄像头像素:2000万及以上 热点:快速充电等信息 。
前端交互方式见管理后台的静态原型
2 表结构分析
tb_specification 规格表
字段 | 类型 | 长度 | 含义 |
---|---|---|---|
id | Bigint | 主键 | |
spec_name | Varchar | 255 | 规格名称 |
tb_specification_option 规格选项表
字段 | 类型 | 长度 | 含义 |
---|---|---|---|
id | Bigint | 主键 | |
option_name | Varchar | 200 | 规格选项名称 |
spec_id | Bigint | 30 | 规格ID |
orders | Int | 11 | 排序 |
3 代码实现
3.1 新增规格
实现思路:我们将规格和规格选项数据合并成一个对象来传递,这时我们需要用一个对象将这两个对象组合起来。在业务逻辑中,得到组合对象中的规格和规格选项列表,插入规格返回规格ID,然后循环插入规格选项。
(1)在dongyimai-sellergoods-service-api 建立com.offcn.sellergoods.group包,包下建立SpecEntity类
@ApiModel(description = "规格复合实体类",value = "SpecEntity")
public class SpecEntity implements Serializable {
@ApiModelProperty(value = "规格对象",required = false)
private Specification specification;
@ApiModelProperty(value = "规格选项对象",required = false)
private List<SpecificationOption> specificationOptionList;
public Specification getSpecification() {
return specification;
}
public void setSpecification(Specification specification) {
this.specification = specification;
}
public List<SpecificationOption> getSpecificationOptionList() {
return specificationOptionList;
}
public void setSpecificationOptionList(List<SpecificationOption> specificationOptionList) {
this.specificationOptionList = specificationOptionList;
}
}
(2)业务层
修改com.offcn.sellergoods.service.SpecificationService,新增方法
/***
* 新增Specification
* @param specEntity
*/
void add(SpecEntity specEntity);
修改com.offcn.sellergoods.service.impl.SpecificationServiceImpl,新增增加规格方法代码如下:
/**
* 增加Specification
* @param specEntity
*/
@Override
public void add(SpecEntity specEntity){
//1.保存规格名称
this.save(specEntity.getSpecification());
//2.得到规格名称ID
if (null != specEntity.getSpecificationOptionList() && specEntity.getSpecificationOptionList().size() > 0) {
for (SpecificationOption specificationOption : specEntity.getSpecificationOptionList()) {
//3.向规格选项中设置规格ID
specificationOption.setSpecId(specEntity.getSpecification().getId());
//4.保存规格选项
specificationOptionMapper.insert(specificationOption);
}
}
}
(3)控制层
修改SpecificationController新增方法
/***
* 新增Specification数据
* @param specEntity
* @return
*/
@ApiOperation(value = "Specification添加",notes = "添加Specification方法详情",tags = {"SpecificationController"})
@PostMapping
public Result add(@RequestBody @ApiParam(name = "SpecEntity复合实体",value = "传入JSON数据",required = true) SpecEntity specEntity){
//调用SpecificationService实现添加Specification
specificationService.add(specEntity);
return new Result(true,StatusCode.OK,"添加成功");
}
测试数据
{
"specification": {
"specName": "测试规格01"
},
"specificationOptionList": [
{
"optionName": "16G",
"orders": "1"
},
{
"optionName": "21G",
"orders": "2"
},
{
"optionName": "33G",
"orders": "3"
}
]
}
3.2 根据ID查询规格
(1)业务层
修改com.offcn.sellergoods.service.SpecificationService,查询规格方法,代码如下:
SpecEntity findById(Long id);
(2)修改com.offcn.sellergoods.service.impl.SpecificationServiceImpl,查询规格方法,代码如下:
public SpecEntity findById(Long id) {
Specification specification = specificationMapper.selectById(id);
QueryWrapper<SpecificationOption> queryWrapper = new QueryWrapper();
queryWrapper.eq("spec_id",id);
List<SpecificationOption> specificationOptionList = specificationOptionMapper.selectList(queryWrapper);
SpecEntity specEntity = new SpecEntity();
specEntity.setSpecification(specification);
specEntity.setSpecificationOptionList(specificationOptionList);
return specEntity;
}
(3)控制层
SpecificationController修改方法
@ApiOperation(value = "Specification根据ID查询",notes = "根据ID查询Specification方法详情",tags = {"SpecificationController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@GetMapping("/{id}")
public Result<SpecEntity> findById(@PathVariable Long id){
//调用SpecificationService实现根据主键查询Specification
SpecEntity specification = specificationService.findById(id);
return new Result<SpecEntity>(true,StatusCode.OK,"查询成功",specification);
}
3.3 修改规格
(1)业务层
修改com.offcn.sellergoods.service.SpecificationService,修改规格方法,代码如下:
/***
* 修改Specification数据
* @param specEntity
*/
void update(SpecEntity specEntity);
修改com.offcn.sellergoods.service.impl.SpecificationServiceImpl,修改品牌方法,代码如下:
/**
* 修改Specification
* @param specEntity
*/
@Override
public void update(SpecEntity specEntity){
//1.修改规格名称对象
this.updateById(specEntity.getSpecification());
//2.根据ID删除规格选项集合
QueryWrapper<SpecificationOption> queryWrapper = new QueryWrapper<SpecificationOption>();
queryWrapper.eq("spec_id", specEntity.getSpecification().getId());
//执行删除
specificationOptionMapper.delete(queryWrapper);
//3.重新插入规格选项
if (!CollectionUtils.isEmpty(specEntity.getSpecificationOptionList())) {
for (SpecificationOption specificationOption : specEntity.getSpecificationOptionList()) {
//先设置规格名称的ID
specificationOption.setSpecId(specEntity.getSpecification().getId());
specificationOptionMapper.insert(specificationOption);
}
}
}
(2)控制层
SpecificationController修改方法
/***
* 修改Specification数据
* @param specEntity
* @param id
* @return
*/
@ApiOperation(value = "Specification根据ID修改",notes = "根据ID修改Specification方法详情",tags = {"SpecificationController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@PutMapping(value="/{id}")
public Result update(@RequestBody @ApiParam(name = "Specification对象",value = "传入JSON数据",required = false) SpecEntity specEntity,@PathVariable Long id){
//设置主键值
specEntity.getSpecification().setId(id);
specificationService.update(specEntity);
return new Result(true,StatusCode.OK,"修改成功");
}
测试数据
{
"specification": {
"specName": "测试规格01_update"
},
"specificationOptionList": [
{
"optionName": "21G_update",
"orders": "2"
},
{
"optionName": "33G",
"orders": "3"
},
{
"optionName": "48G",
"orders": "4"
}
]
}
3.4 删除规格
(1)业务层
修改com.offcn.sellergoods.service.SpecificationServiceImpl,删除规格方法,代码如下:
/**
* 删除
* @param id
*/
@Override
public void delete(Long id){
//1.删除规格名称对象
this.removeById(id);
//2.关联删除规格选项集合
QueryWrapper<SpecificationOption> queryWrapper = new QueryWrapper();
queryWrapper.eq("spec_id", id);
//执行删除
specificationOptionMapper.delete(queryWrapper);
}
(2)控制层
SpecificationController删除方法
/***
* 根据ID删除品牌数据
* @param id
* @return
*/
@ApiOperation(value = "Specification根据ID删除",notes = "根据ID删除Specification方法详情",tags = {"SpecificationController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@DeleteMapping(value = "/{id}" )
public Result delete(@PathVariable Long id){
//调用SpecificationService实现根据主键删除
specificationService.delete(id);
return new Result(true,StatusCode.OK,"删除成功");
}
四 模板管理
**1 **需求分析
首先我们需要理解模板的作用。模板主要有两个:
1是用于关联品牌与规格
2定义扩充属性
2 表结构分析
tb_type_template 模板表
字段 | 类型 | 长度 | 含义 |
---|---|---|---|
Id | Bigint | 主键 | |
name | Varchar | 80 | 模板名称 |
spec_ids | Varchar | 1000 | 关联规格(json格式) |
brand_ids | Varchar | 1000 | 关联品牌(json格式) |
custom_attribute_items | Varchar | 2000 | 扩展属性(Json格式) |
3.品牌下拉列表
在弹出窗口中有个品牌下拉列表,要求品牌是可以选择多个,需要后台提供数据支持
3.1 品牌下拉列表代码实现
(1)修改dao层 ,在BrandMapper中添加查询
@Select("select id,name as text from tb_brand")
public List<Map> selectOptions();
(2)修改com.offcn.sellergoods.service.BrandService,增加方法定义
/**
* 查询品牌下拉列表
* @return
*/
public List<Map> selectOptions();
(3)修改com.offcn.sellergoods.service.impl.BrandServiceImpl,增加方法
/**
* 查询品牌下拉列表
*
* @return
*/
@Override
public List<Map> selectOptions() {
return brandMapper.selectOptions();
}
(4)修改BrandController.java
@ApiOperation(value = "查询品牌下拉列表",notes = "查询品牌下拉列表",tags = {"BrandController"})
@GetMapping("/selectOptions")
public ResponseEntity<List<Map>> selectOptions(){
return ResponseEntity.ok(brandService.selectOptions());
}
3.2 规格下拉列表代码实现
(1)修改dao层 ,在SpecificationMapper中添加查询
@Select("select id,spec_name as text from tb_specification")
public List<Map> selectOptions();
(2)修改com.offcn.sellergoods.service.SpecificationService,增加方法定义
/**
* 查询规格下拉列表
* @return
*/
public List<Map> selectOptions();
(3)修改com.offcn.sellergoods.service.impl.SpecificationServiceImpl,增加方法
/**
* 查询规格下拉列表
*
* @return
*/
@Override
public List<Map> selectOptions() {
return specificationMapper.selectOptions();
}
(4)修改SpecificationController.java
@ApiOperation(value = "查询规格下拉列表",notes = "查询规格下拉列表",tags = {"SpecificationController"})
@GetMapping("/selectOptions")
public List<Map> selectOptions() {
return specificationService.selectOptions();
}
五 商品分类
1 需求分析
实现三级商品分类列表查询功能
进入页面首先显示所以一级分类,效果如下:
点击列表行的查询下级按钮,进入下级分类列表,同时更新面包屑导航
2 表结构分析
tb_item_cat 商品分类表
字段 | 类型 | 长度 | 含义 |
---|---|---|---|
id | Bigint | 主键 | |
parent_id | Bigint | 上级ID | |
name | varchar | 分类名称 | |
type_id | Bigint | 模板ID |
3 查询下级代码实现
(1)修改com.offcn.sellergoods.service.ItemCatService接口,新增方法定义
/**
* 根据父级ID查询分类列表
* @param parentId
* @return
*/
public List<ItemCat> findByParentId(Long parentId);
(2)修改com.offcn.sellergoods.service.impl.ItemCatServiceImpl ,实现方法
/**
* 根据父级ID查询分类列表
*
* @param parentId
* @return
*/
public List<ItemCat> findByParentId(Long parentId) {
ItemCat itemCat = new ItemCat();
itemCat.setParentId(parentId);
QueryWrapper<ItemCat> queryWrapper = this.createQueryWrapper(itemCat);
return this.list(queryWrapper);
}
(3)修改ItemCatController.java
@ApiOperation(value = "根据父级ID查询ItemCat",notes = "根据父级ID查询ItemCat",tags = {"ItemCatController"})
@GetMapping("/findByParentId")
public Result<List<ItemCat>> findByParentId(Long parentId) {
List<ItemCat> list = itemCatService.findByParentId(parentId);
return new Result<List<ItemCat>>(true, StatusCode.OK,"查询成功",list) ;
}
4 新增商品分类(学员实现)
4.1新增商品分类–实现模板下拉列表(学员实现)
5 修改商品分类(学员实现)
6 删除商品分类(学员实现)
六 电商概念及表结构分析
1. SPU与SKU概念
SPU = Standard Product Unit (标准产品单位)
-
概念 : SPU 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
-
通俗点讲,属性值、特性相同的货品就可以称为一个 SPU
==同款商品的公共属性抽取==
例如: Iphone12就是一个SPU,与商家,与颜色、款式、套餐都无关
SKU = stock keeping unit( 库存量单位)
-
SKU 即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
-
SKU 是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。
-
在服装、鞋类商品中使用最多最普遍。
==某个库存单位的商品独有属性(某个商品的独有属性)==
例如:Iphone12 黑色 128G 全网通 就是一个 SKU
2 表结构分析
Tb_goods 商品表
Tb_goods_desc 商品扩展信息表
Tb_item SKU表
七 新增和修改商品
1 需求分析
实现商品的新增与修改功能。
(1)先选择添加的商品所属分类,选择关联品牌,并填写相关信息
(2)上传图片以及填写关联扩展属性的信息
(3)填写SKU信息
2 实现思路
前端传递给后端的数据格式 是一个spu对象和sku列表组成的对象,如下图:
上图JSON数据如下:
{
"goods": {
"brandId": 1,
"caption": "测试商品01副标题",
"category1Id": 558,
"category2Id": 559,
"category3Id": 560,
"goodsName": "测试商品01",
"isEnableSpec": "1",
"price": "1000",
"typeTemplateId": 35
},
"goodsDesc": {
"customAttributeItems": "[{"text":"内存大小","value":"16G"},{"text":"颜色","value":"绿色"}]",
"introduction": "<span style="color:#666666;font-family:tahoma, arial, "font-size:14px;background-color:#FFFFFF;">测试描述</span><br />",
"itemImages": "[{"color":"红色","url":"http://192.168.188.146/group1/M00/00/00/wKi8kly5gXGAZ9X_AAQQPbR_emI045.png"},{"color":"绿色","url":"http://192.168.188.146/group1/M00/00/00/wKi8kly5gXmANvndAAI1bFuIUJE180.jpg"}]",
"packageList": "测试包装",
"saleService": "测试售后服务",
"specificationItems": "[{"attributeValue":["移动3G","移动4G"],"attributeName":"网络"},{"attributeValue":["16G","32G"],"attributeName":"机身内存"}]"
},
"itemList": [
{
"isDefault": "1",
"num": 9999,
"price": "1000",
"spec": "{"机身内存":"16G","网络":"移动3G"}",
"status": "1",
},
{
"isDefault": "0",
"num": 9999,
"price": "2000",
"spec": "{"机身内存":"16G","网络":"移动4G"}",
"status": "1",
},
{
"isDefault": "0",
"num": 9999,
"price": "2001",
"spec": "{"机身内存":"24G","网络":"移动3G"}",
"status": "1",
},
{
"isDefault": "0",
"num": 9999,
"price": "2002",
"spec": "{"机身内存":"24G","网络":"移动4G"}",
"status": "1",
}
]
}
3 查询分类级联数据
3.1 需求分析
在实现商品增加之前,需要先选择对应的分类,选择分类的时候,首选选择一级分类,然后根据选中的分类,将选中的分类作为查询的父ID,再查询对应的子分类集合。这块在昨天的代码中已经有一个根据父节点ID查询分类信息的方法,参考findByPrantId方法,首先查询顶级分类,也就是pid=0,然后根据用户选择的分类,将选择的分类作为pid查询子分类。
3.2 代码实现
(1)Dao实现,代码如下:
public interface ItemCatMapper extends BaseMapper<ItemCat> {
}
(2)Service层,代码如下:
/**
* 根据父级ID查询分类列表
* @param parentId
* @return
*/
public List<ItemCat> findByParentId(Long parentId);
服务实现,代码如下:
/**
* 根据父级ID查询分类列表
*
* @param parentId
* @return
*/
public List<ItemCat> findByParentId(Long parentId) {
ItemCat itemCat = new ItemCat();
itemCat.setParentId(parentId);
QueryWrapper<ItemCat> queryWrapper = this.createQueryWrapper(itemCat);
return this.list(queryWrapper);
}
(3)Controller层,代码如下:
@ApiOperation(value = "根据父级ID查询ItemCat",notes = "根据父级ID查询ItemCat",tags = {"ItemCatController"})
@GetMapping("/findByParentId")
public Result<List<ItemCat>> findByParentId(Long parentId) {
List<ItemCat> list = itemCatService.findByParentId(parentId);
return new Result<List<ItemCat>>(true, StatusCode.OK,"查询成功",list) ;
}
4 查询分类下品牌数据
4.1 分析
用户每次选择了分类之后,可以根据用户选择的分类到tb_item_cat
表中根据模板ID关联到tb_type_template
表中查询品牌,再将品牌集合数据放到前端来展示即可实现上述功能。
4.2 代码实现
(1)Dao实现
com.offcn.sellergoods.dao.TypeTemplateMapper
,代码如下:
public interface TypeTemplateMapper extends BaseMapper<TypeTemplate> {
}
(2)Service层,代码如下:
/**
* 根据ID查询TypeTemplate
* @param id
* @return
*/
TypeTemplate findById(Long id);
服务实现,代码如下:
/**
* 根据ID查询TypeTemplate
* @param id
* @return
*/
@Override
public TypeTemplate findById(Long id){
return this.getById(id);
}
(3)Controller层,代码如下:
/***
* 根据ID查询TypeTemplate数据
* @param id
* @return
*/
@ApiOperation(value = "TypeTemplate根据ID查询",notes = "根据ID查询TypeTemplate方法详情",tags = {"TypeTemplateController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@GetMapping("/{id}")
public Result<TypeTemplate> findById(@PathVariable Long id){
//调用TypeTemplateService实现根据主键查询TypeTemplate
TypeTemplate typeTemplate = typeTemplateService.findById(id);
return new Result<TypeTemplate>(true,StatusCode.OK,"查询成功",typeTemplate);
}
5 规格查询
5.1 分析
用户选择分类后,需要根据所选分类对应的模板ID查询对应的规格以及规格选项,需要单独编写方法查询出规格选项。
上图JSON数据如下:
{
"options": [
{
"id": 114,
"optionName": "移动2G",
"specId": 27,
"orders": 7
},
{
"id": 115,
"optionName": "联通2G",
"specId": 27,
"orders": 8
},
{
"id": 116,
"optionName": "电信2G",
"specId": 27,
"orders": 9
}
],
"id": 27,
"text": "网络"
},
{
"options": [
{
"id": 118,
"optionName": "16G",
"specId": 32,
"orders": 1
},
{
"id": 119,
"optionName": "32G",
"specId": 32,
"orders": 2
},
{
"id": 120,
"optionName": "64G",
"specId": 32,
"orders": 3
},
{
"id": 121,
"optionName": "128G",
"specId": 32,
"orders": 4
}
],
"id": 32,
"text": "机身内存"
}
5.2 代码实现
(1)Service层
修改com.offcn.sellergoods.service.TypeTemplateService
添加根据分类ID查询规格列表,代码如下:
/**
* 根据模板ID查询规格列表
* @param typeId
* @return
*/
public List<Map> findSpecList(Long typeId);
(2)服务实现,修改com.offcn.sellergoods.service.impl.TypeTemplateService
添加上面方法的实现,代码如下:
@Autowired
private SpecificationOptionMapper specificationOptionMapper;
/**
* 根据模板ID查询规格列表
*
* @param typeId
* @return
*/
public List<Map> findSpecList(Long typeId) {
//1.根据模板ID查询模板对象
TypeTemplate typeTemplate = this.findById(typeId);
//2.将规格名称JSON结构的字符串转换成JSON对象 [{"id":27,"text":"网络"},{"id":32,"text":"机身内存"}]
List<Map> specList = JSON.parseArray(typeTemplate.getSpecIds(), Map.class);
if (!CollectionUtils.isEmpty(specList)) {
for (Map map : specList) {
Long specId = new Long((Integer) map.get("id")); //Map集合中取得数值类型的值默认是整型
//3.根据规格ID查询规格选项集合
QueryWrapper<SpecificationOption> queryWrapper = new QueryWrapper();
queryWrapper.eq("spec_id", specId);
List<SpecificationOption> specificationOptionList = specificationOptionMapper.selectList(queryWrapper);
//4.重新将规格选项集合设置回JSON对象中 {"id":27,"text":"网络","options":[{},{},{}]}
map.put("options", specificationOptionList);
}
}
return specList;
}
(3)Controller层
修改com.offcn.sellergoods.controller.TypeTemplateController
添加根据分类ID查询规格数据,代码如下:
@ApiOperation(value = "查询规格及规格选项信息",notes = "查询规格及规格选项信息",tags = {"TypeTemplateController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@GetMapping("/findSpecList/{id}")
public Result<List<Map>> findSpecList(@PathVariable Long id){
List<Map> list = typeTemplateService.findSpecList(id);
return new Result<List<Map>>(true, StatusCode.OK,"查询成功",list) ;
}
6 SPU+SKU保存
6.1 分析
保存商品数据的时候,需要保存Spu和Sku,一个Spu对应多个Sku,我们可以先构建一个Goods对象,将Spu
和List<Sku>
组合到一起,前端将2者数据提交过来,再实现添加操作。
6.2 代码实现
(1)Pojo改造
修改dongyimai-sellergoods-service-api
工程创建组合实体类,创建com.offcn.sellergoods.group.GoodsEntity,代码如下:
public class GoodsEntity implements Serializable {
private Goods goods; //商品信息 SPU
private GoodsDesc goodsDesc; //商品扩展信息
private List<Item> itemList; //商品详情 SKU
//..get..set..toString
}
因为扩展信息是使用SPU的主键,所以需要修改扩展信息的Pojo的ID生成方式为input类型,代码如下:
@ApiModelProperty(value = "SPU_ID",required = false)
@TableId(type = IdType.INPUT)
@TableField(value = "goods_id")
private Long goodsId;//SPU_ID
(2) 业务层
修改com.offcn.sellergoods.service.GoodsService接口,添加保存Goods方法,代码如下:
/***
* 新增Goods
* @param goodsEntity
*/
void add(GoodsEntity goodsEntity);
修改com.offcn.sellergoods.service.impl.GoodsServiceImpl类,添加保存Goods的方法实现,代码如下:
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsDescMapper goodsDescMapper;
@Autowired
private ItemMapper itemMapper;
@Autowired
private ItemCatMapper itemCatMapper;
@Autowired
private BrandMapper brandMapper;
/**
* 增加Goods
*
* @param goods
*/
@Override
public void add(GoodsEntity goodsEntity) {
goodsEntity.getGoods().setAuditStatus("0"); //审核状态 未审核
//1.保存SPU 商品信息对象
goodsMapper.insert(goodsEntity.getGoods());
//2.获取商品信息对象主键ID ,向商品扩展信息对象中设置主键
goodsEntity.getGoodsDesc().setGoodsId(goodsEntity.getGoods().getId());
//3.保存商品扩展信息
goodsDescMapper.insert(goodsEntity.getGoodsDesc());
//4.保存SKU 商品详情信息
if ("1".equals(goodsEntity.getGoods().getIsEnableSpec())) {
//5.保存SKU 商品详情信息
if (!CollectionUtils.isEmpty(goodsEntity.getItemList())) {
for (Item item : goodsEntity.getItemList()) {
String title = goodsEntity.getGoods().getGoodsName();
//设置SKU的名称 商品名+规格选项
Map<String, String> specMap = JSON.parseObject(item.getSpec(), Map.class); //取得SKU的规格选项,并做JSON类型转换
for (String key : specMap.keySet()) {
title += specMap.get(key) + " ";
}
item.setTitle(title); //SKU名称
item.setCategoryId(goodsEntity.getGoods().getCategory3Id()); //商品分类 三级
item.setCreateTime(new Date()); //创建时间
item.setUpdateTime(new Date()); //更新时间
item.setGoodsId(goodsEntity.getGoods().getId()); //SPU ID
item.setSellerId(goodsEntity.getGoods().getSellerId()); //商家ID
//查询分类对象
ItemCat itemCat = itemCatMapper.selectById(goodsEntity.getGoods().getCategory3Id());
item.setCategory(itemCat.getName()); //分类名称
//查询品牌对象
Brand tbBrand = brandMapper.selectById(goodsEntity.getGoods().getBrandId());
item.setBrand(tbBrand.getName()); //品牌名称
List<Map> imageList = JSON.parseArray(goodsEntity.getGoodsDesc().getItemImages(), Map.class);
if (imageList.size() > 0) {
item.setImage((String) imageList.get(0).get("url")); //商品图片
}
//保存SKU信息
itemMapper.insert(item);
}
}
} else {
//不启用规格 SKU信息为默认值
Item item = new Item();
item.setTitle(goodsEntity.getGoods().getGoodsName()); //商品名称
item.setPrice(goodsEntity.getGoods().getPrice()); //默认使用SPU的价格
item.setNum(9999);
item.setStatus("1"); //是否启用
item.setIsDefault("1"); //是否默认
item.setSpec("{}"); //没有选择规格,则放置空JSON结构
this.setItemValue(goodsEntity, item);
itemMapper.insert(item);
}
}
(3)Controller层
修改com.offcn.sellergoods.controller.GoodsController,增加保存Goods方法,代码如下:
/***
* 新增Goods数据
* @param goods
* @return
*/
@ApiOperation(value = "Goods添加",notes = "添加Goods方法详情",tags = {"GoodsController"})
@PostMapping
public Result add(@RequestBody @ApiParam(name = "Goods复合实体对象",value = "传入JSON数据",required = true) GoodsEntity goodsEntity){
//调用GoodsService实现添加Goods
goodsService.add(goodsEntity);
return new Result(true,StatusCode.OK,"添加成功");
}
测试数据
{
"goods": {
"brandId": 1,
"caption": "测试商品01副标题",
"category1Id": 558,
"category2Id": 559,
"category3Id": 560,
"goodsName": "测试商品01",
"isEnableSpec": "1",
"price": "1000",
"typeTemplateId": 35
},
"goodsDesc": {
"customAttributeItems": "[{\"text\":\"内存大小\",\"value\":\"16G\"},{\"text\":\"颜色\",\"value\":\"绿色\"}]",
"introduction": "<span style=\"color:#666666;font-family:tahoma, arial, "font-size:14px;background-color:#FFFFFF;\">测试描述</span><br />",
"itemImages": "[{\"color\":\"红色\",\"url\":\"http://192.168.188.146/group1/M00/00/00/wKi8kly5gXGAZ9X_AAQQPbR_emI045.png\"},{\"color\":\"绿色\",\"url\":\"http://192.168.188.146/group1/M00/00/00/wKi8kly5gXmANvndAAI1bFuIUJE180.jpg\"}]",
"packageList": "测试包装",
"saleService": "测试售后服务",
"specificationItems": "[{\"attributeValue\":[\"移动3G\",\"移动4G\"],\"attributeName\":\"网络\"},{\"attributeValue\":[\"16G\",\"32G\"],\"attributeName\":\"机身内存\"}]"
},
"itemList": [
{
"isDefault": "1",
"num": 9999,
"price": "1000",
"spec": "{\"机身内存\":\"16G\",\"网络\":\"移动3G\"}",
"status": "1"
},
{
"isDefault": "0",
"num": 9999,
"price": "2000",
"spec": "{\"机身内存\":\"16G\",\"网络\":\"移动4G\"}",
"status": "1"
},
{
"isDefault": "0",
"num": 9999,
"price": "2001",
"spec": "{\"机身内存\":\"24G\",\"网络\":\"移动3G\"}",
"status": "1"
},
{
"isDefault": "0",
"num": 9999,
"price": "2002",
"spec": "{\"机身内存\":\"24G\",\"网络\":\"移动4G\"}",
"status": "1"
}
]
}
7 根据ID查询商品
7.1 需求分析
需求:根据id 查询SPU和SKU列表 ,显示效果如下:
"goods": {
"id": 149187842867985,
"sellerId": "1",
"goodsName": "测试商品01",
"defaultItemId": null,
"auditStatus": "1",
"isMarketable": null,
"brandId": 1,
"caption": "测试商品01",
"category1Id": 558,
"category2Id": 559,
"category3Id": 560,
"smallPic": null,
"price": "2000.00",
"typeTemplateId": 35,
"isEnableSpec": "1",
"isDelete": "1"
},
"goodsDesc": {
"goodsId": 149187842867985,
"introduction": "<span style=\"color:#666666;font-family:tahoma, arial, "font-size:14px;background-color:#FFFFFF;\">读者评论</span><br />",
"specificationItems": "[{\"attributeValue\":[\"移动3G\",\"移动4G\"],\"attributeName\":\"网络\"},{\"attributeValue\":[\"16G\",\"32G\"],\"attributeName\":\"机身内存\"}]",
"customAttributeItems": "[{\"text\":\"内存大小\",\"value\":\"16G\"},{\"text\":\"颜色\",\"value\":\"绿色\"}]",
"itemImages": "[{\"color\":\"红色\",\"url\":\"http://192.168.188.146/group1/M00/00/00/wKi8kly5gXGAZ9X_AAQQPbR_emI045.png\"},{\"color\":\"绿色\",\"url\":\"http://192.168.188.146/group1/M00/00/00/wKi8kly5gXmANvndAAI1bFuIUJE180.jpg\"}]",
"packageList": "测试包装",
"saleService": "测试售后服务"
},
"itemList": [
{
"id": 1369283,
"title": "测试商品01移动3G 16G ",
"sellPoint": null,
"price": "2001.00",
"stockCount": null,
"num": 9999,
"barcode": null,
"image": "http://192.168.188.146/group1/M00/00/00/wKi8kly5gXGAZ9X_AAQQPbR_emI045.png",
"categoryId": 560,
"status": "1",
"createTime": "2021-02-08T12:17:45.000+0000",
"updateTime": "2021-02-08T12:17:45.000+0000",
"itemSn": null,
"costPirce": null,
"marketPrice": null,
"isDefault": "1",
"goodsId": 149187842867985,
"sellerId": "1",
"cartThumbnail": null,
"category": "手机",
"brand": "联想",
"spec": "{\"机身内存\":\"16G\",\"网络\":\"移动3G\"}",
"seller": null
},
{
"id": 1369284,
"title": "测试商品01移动4G 16G ",
"sellPoint": null,
"price": "2001.00",
"stockCount": null,
"num": 9999,
"barcode": null,
"image": "http://192.168.188.146/group1/M00/00/00/wKi8kly5gXGAZ9X_AAQQPbR_emI045.png",
"categoryId": 560,
"status": "1",
"createTime": "2021-02-08T12:17:45.000+0000",
"updateTime": "2021-02-08T12:17:45.000+0000",
"itemSn": null,
"costPirce": null,
"marketPrice": null,
"isDefault": "1",
"goodsId": 149187842867985,
"sellerId": "1",
"cartThumbnail": null,
"category": "手机",
"brand": "联想",
"spec": "{\"机身内存\":\"16G\",\"网络\":\"移动4G\"}",
"seller": null
}]
7.2 代码实现
(1)业务层
修改dongyimai-sellergoods-service
工程,修改com.offcn.sellergoods.service.GoodsService接口,添加根据ID查找方法findById代码如下:
/**
* 根据ID查询Goods
* @param id
* @return
*/
GoodsEntity findById(Long id);
修改dongyimai-sellergoods-service
工程,修改com.offcn.sellergoods.service.impl.GoodsServiceImpl类,修改根据ID查找findById方法,代码如下:
/**
* 根据ID查询Goods
*
* @param id
* @return
*/
@Override
public GoodsEntity findById(Long id) {
//1.根据ID查询SPU信息
Goods goods = goodsMapper.selectById(id);
//2.根据ID查询商品扩展信息
GoodsDesc goodsDesc = goodsDescMapper.selectById(id);
//3.根据ID查询SKU列表
QueryWrapper<Item> queryWrapper = new QueryWrapper();
queryWrapper.eq("goods_id", id);
List<Item> itemList = itemMapper.selectList(queryWrapper);
//4.设置复合实体对象
GoodsEntity goodsEntity = new GoodsEntity();
goodsEntity.setGoods(goods);
goodsEntity.setGoodsDesc(goodsDesc);
goodsEntity.setItemList(itemList);
return goodsEntity;
}
(2)Controller层
修改com.offcn.sellergoods.controller.GoodsController,修改findById方法,代码如下:
/***
* 根据ID查询Goods数据
* @param id
* @return
*/
@ApiOperation(value = "Goods根据ID查询",notes = "根据ID查询Goods方法详情",tags = {"GoodsController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@GetMapping("/{id}")
public Result<GoodsEntity> findById(@PathVariable Long id){
//调用GoodsService实现根据主键查询Goods
GoodsEntity goodsEntity = goodsService.findById(id);
return new Result<GoodsEntity>(true,StatusCode.OK,"查询成功",goodsEntity);
}
8 保存修改
(1)业务层
修改dongyimai-sellergoods-service
的GoodsService的update方法,代码如下:
/***
* 修改Goods数据
* @param goods
*/
void update(GoodsEntity goodsEntity);
修改dongyimai-sellergoods-service
的GoodsServiceImpl ,将SKU列表插入的代码提取出来,封装到私有方法中,代码如下:
private void setItemValue(GoodsEntity goodsEntity, Item item) {
item.setCategoryId(goodsEntity.getGoods().getCategory3Id()); //商品分类 三级
item.setCreateTime(new Date()); //创建时间
item.setUpdateTime(new Date()); //更新时间
item.setGoodsId(goodsEntity.getGoods().getId()); //SPU ID
item.setSellerId(goodsEntity.getGoods().getSellerId()); //商家ID
//查询分类对象
ItemCat itemCat = itemCatMapper.selectById(goodsEntity.getGoods().getCategory3Id());
item.setCategory(itemCat.getName()); //分类名称
//查询品牌对象
Brand tbBrand = brandMapper.selectById(goodsEntity.getGoods().getBrandId());
item.setBrand(tbBrand.getName()); //品牌名称
List<Map> imageList = JSON.parseArray(goodsEntity.getGoodsDesc().getItemImages(), Map.class);
if (imageList.size() > 0) {
item.setImage((String) imageList.get(0).get("url")); //商品图片
}
}
//保存SKU信息
private void saveItemList(GoodsEntity goodsEntity) {
if ("1".equals(goodsEntity.getGoods().getIsEnableSpec())) {
//5.保存SKU 商品详情信息
if (!CollectionUtils.isEmpty(goodsEntity.getItemList())) {
for (Item item : goodsEntity.getItemList()) {
String title = goodsEntity.getGoods().getGoodsName();
//设置SKU的名称 商品名+规格选项
Map<String, String> specMap = JSON.parseObject(item.getSpec(), Map.class); //取得SKU的规格选项,并做JSON类型转换
for (String key : specMap.keySet()) {
title += specMap.get(key) + " ";
}
item.setTitle(title); //SKU名称
this.setItemValue(goodsEntity, item);
//保存SKU信息
itemMapper.insert(item);
}
}
} else {
//不启用规格 SKU信息为默认值
Item item = new Item();
item.setTitle(goodsEntity.getGoods().getGoodsName()); //商品名称
item.setPrice(goodsEntity.getGoods().getPrice()); //默认使用SPU的价格
item.setNum(9999);
item.setStatus("1"); //是否启用
item.setIsDefault("1"); //是否默认
item.setSpec("{}"); //没有选择规格,则放置空JSON结构
this.setItemValue(goodsEntity, item);
itemMapper.insert(item);
}
}
在add方法中调用 此方法,修改如下:
/**
* 增加Goods
* @param goods
*/
@Override
public void add(GoodsEntity goodsEntity){
goodsEntity.getGoods().setAuditStatus("0"); //审核状态 未审核
//1.保存SPU 商品信息对象
goodsMapper.insert(goodsEntity.getGoods());
//2.获取商品信息对象主键ID ,向商品扩展信息对象中设置主键
goodsEntity.getGoodsDesc().setGoodsId(goodsEntity.getGoods().getId());
//3.保存商品扩展信息
goodsDescMapper.insert(goodsEntity.getGoodsDesc());
//4.保存商品SKU信息
this.saveItemList(goodsEntity);
}
怎么样,是不是比原来更加清爽了呢?
接下来,我们修改update方法,实现修改
/**
* 修改Goods
* @param goods
*/
@Override
public void update(GoodsEntity goodsEntity){
//将审核状态重新设置为 未审核
goodsEntity.getGoods().setAuditStatus("0");
//1.修改SPU的信息
goodsMapper.updateById(goodsEntity.getGoods());
//2.修改商品扩展信息
goodsDescMapper.updateById(goodsEntity.getGoodsDesc());
//3.先根据商品ID删除SKU信息
QueryWrapper<Item> queryWrapper = new QueryWrapper();
queryWrapper.eq("goods_id", goodsEntity.getGoods().getId());
itemMapper.delete(queryWrapper);
//4.重新添加SKU信息
this.saveItemList(goodsEntity);
}
(2)控制层
修改com.offcn.sellergoods.controller.GoodsController,修改update方法,代码如下:
/***
* 修改Goods数据
* @param goods
* @param id
* @return
*/
@ApiOperation(value = "Goods根据ID修改",notes = "根据ID修改Goods方法详情",tags = {"GoodsController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID", required = true, dataType = "Long")
@PutMapping(value="/{id}")
public Result update(@RequestBody @ApiParam(name = "Goods复合实体对象",value = "传入JSON数据",required = false) GoodsEntity goodsEntity,@PathVariable Long id){
//设置主键值
goodsEntity.getGoods().setId(id);
//调用GoodsService实现修改Goods
goodsService.update(goodsEntity);
return new Result(true,StatusCode.OK,"修改成功");
}
八 商品审核与上下架(学员实现)
1 需求分析
商品新增后,审核状态为0(未审核),默认为下架状态。
审核商品,需要校验是否是被删除的商品,如果未删除则修改审核状态为1,并自动上架
下架商品,需要校验是否是被删除的商品,如果未删除则修改上架状态为0
上架商品,需要审核通过的商品
2 实现思路
(1)按照ID查询SPU信息
(2)判断修改审核、上架和下架状态
(3)保存SPU
3 代码实现
3.1 商品审核
实现审核通过,自动上架。
(1)业务层
修改dongyimai-sellergoods-service
工程的com.offcn.sellergoods.service.GoodsService接口,添加审核方法,代码如下:
/***
* 商品审核
* @param goodsId
*/
void audit(Long goodsId);
修改dongyimai-sellergoods-service
工程的com.offcn.sellergoods.service.impl.GoodsServiceImpl类,添加audit方法,代码如下:
/***
* 商品审核
* @param goodsId
*/
@Override
public void audit(Long goodsId) {
//查询商品
Goods goods = goodsMapper.selectById(goodsId);
//判断商品是否已经删除
if(goods.getIsDelete().equals("1")){
throw new RuntimeException("该商品已经删除!");
}
//实现上架和审核
goods.setAuditStatus("1"); //审核通过
goods.setIsMarketable("1"); //上架
goodsMapper.updateById(goods);
}
(2)控制层
修改com.offcn.sellergoods.controller.GoodsController,新增audit方法,代码如下:
/**
* 审核
* @param id
* @return
*/
@PutMapping("/audit/{id}")
public Result audit(@PathVariable Long id){
goodsService.audit(id);
return new Result(true,StatusCode.OK,"审核成功");
}
3.2 下架商品
(1)业务层
修改com.offcn.sellergoods.service.GoodsService接口,添加pull方法,用于商品下架,代码如下:
/***
* 商品下架
* @param goodsId
*/
void pull(Long goodsId);
修改com.offcn.sellergoods.impl.GoodsServiceImpl,添加如下方法:
/**
* 商品下架
* @param goodsId
*/
@Override
public void pull(Long goodsId) {
Goods goods = goodsMapper.selectById(goodsId);
if(goods.getIsDelete().equals("1")){
throw new RuntimeException("此商品已删除!");
}
goods.setIsMarketable("0");//下架状态
goodsMapper.updateById(goods);
}
(2)控制层
修改com.offcn.sellergoods.controller.GoodsController,添加pull方法,代码如下:
/**
* 下架
* @param id
* @return
*/
@PutMapping("/pull/{id}")
public Result pull(@PathVariable Long id){
goodsService.pull(id);
return new Result(true,StatusCode.OK,"下架成功");
}
3.3 上架商品
必须是通过审核的商品才能上架
(1)业务层
修改com.offcn.sellergoods.service.GoodsService,添加put方法,代码如下:
/***
* 商品上架
* @param goodsId
*/
void put(Long goodsId);
修改com.offcn.sellergoods.service.impl.GoodsServiceImpl,添加put方法实现,代码如下:
/***
* 商品上架
* @param goodsId
*/
@Override
public void put(Long goodsId) {
Goods goods = goodsMapper.selectById(goodsId);
//检查是否删除的商品
if(goods.getIsDelete().equals("1")){
throw new RuntimeException("此商品已删除!");
}
if(!goods.getAuditStatus().equals("1")){
throw new RuntimeException("未通过审核的商品不能!");
}
//上架状态
goods.setIsMarketable("1");
goodsMapper.updateById(goods);
}
(2)控制层
修改com.offcn.sellergoods.controller.GoodsController,添加put方法代码如下:
/**
* 商品上架
* @param id
* @return
*/
@PutMapping("/put/{id}")
public Result put(@PathVariable Long id){
goodsService.put(id);
return new Result(true,StatusCode.OK,"上架成功");
}
3.4 批量上架
前端传递一组商品ID,后端进行批量上下架处理
(1)业务层
修改com.offcn.sellergoods.service.GoodsService接口,代码如下:
int putMany(Long[] ids);
修改com.offcn.sellergoods.service.impl.GoodsServiceImpl,添加批量上架方法实现,代码如下:
/***
* 批量上架
* @param ids:需要上架的商品ID集合
* @return
*/
@Override
public int putMany(Long[] ids) {
Goods goods=new Goods();
goods.setIsMarketable("1");//上架
//批量修改
QueryWrapper<Goods> queryWrapper = new QueryWrapper();
queryWrapper.in("id", Arrays.asList(ids));
//下架
queryWrapper.eq("is_marketable","0");
//审核通过的
queryWrapper.eq("audit_status","1");
//非删除的
queryWrapper.eq("is_delete","0");
return goodsMapper.update(goods, queryWrapper);
}
(2)控制层
修改com.offcn.sellergoods.controller.GoodsController,添加批量上架方法,代码如下:
/**
* 批量上架
* @param ids
* @return
*/
@PutMapping("/put/many")
public Result putMany(@RequestBody Long[] ids){
int count = goodsService.putMany(ids);
return new Result(true,StatusCode.OK,"上架"+count+"个商品");
}
3.5 批量下架
学员实现
九 删除与还原商品
1 需求分析
请看管理后台的静态原型
我们为商品管理提供商品删除功能,用户选中部分商品,点击删除按钮即可实现商品删除。注意,这里的删除并非是物理删除,而是修改tb_goods表的is_delete字段为1 ,我们可以称之为“逻辑删除”
2 实现思路
逻辑删除商品,修改spu表is_delete字段为1
3 代码实现
3.1 逻辑删除商品
(1)业务层
修改com.offcn.sellergoods.service.impl.GoodsServiceImpl接口,修改delete方法,代码如下:
/**
* 删除
*
* @param id
*/
@Override
public void delete(Long id) {
// 逻辑删除
Goods goods = goodsMapper.selectById(id);
//检查是否下架的商品
if (!goods.getIsMarketable().equals("0")) {
throw new RuntimeException("必须先下架再删除!");
}
goods.setIsDelete("1");
//未审核
goods.setAuditStatus("0");
goodsMapper.updateById(goods);
}
(2)控制层
修改com.offcn.sellergoods.controller.GoodsController,添加delete方法,如下:
/***
* 根据ID删除品牌数据
* @param id
* @return
*/
@ApiOperation(value = "Goods根据ID删除",notes = "根据ID删除Goods方法详情",tags = {"GoodsController"})
@ApiImplicitParam(paramType = "path", name = "id", value = "主键ID集合", required = true, dataType = "Long")
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id){
//调用GoodsService实现根据主键删除
goodsService.delete(id);
return new Result(true,StatusCode.OK,"删除成功");
}
十 注解式事务配置
1 事务异常测试
我们修改dongyimai-sellergoods-service工程GoodsServiceImpl.java的add方法
/**
* 增加Goods
*
* @param goods
*/
@Override
public void add(GoodsEntity goodsEntity) {
goodsEntity.getGoods().setAuditStatus("0"); //审核状态 未审核
//1.保存SPU 商品信息对象
goodsMapper.insert(goodsEntity.getGoods());
//2.获取商品信息对象主键ID ,向商品扩展信息对象中设置主键
goodsEntity.getGoodsDesc().setGoodsId(goodsEntity.getGoods().getId());
//3.保存商品扩展信息
goodsDescMapper.insert(goodsEntity.getGoodsDesc());
int i = 1 / 0;
//4.保存商品SKU信息
this.saveItemList(goodsEntity);
}
在插入商品表和扩展表后,人为制造一个异常。我们运行程序,新增商品数据,观察运行结果。
通过观察,我们发现,程序发生异常 ,商品表仍然会存储记录,这是不符合我们要求的。这是因为我们目前的系统还没有配置事务。
2 注解式事务解决方案
在项目pom.xml
文件中,因为引入了spring-boot-starter-jdbc,会注入一个DataSourceTransactionManager
的 bean,提供了事务支持
@Transactional
这个注解可以放在类上,也可以放在方法上;如果是标注在类上,则这个类的所有公共方法,都支持事务;
如果类和方法上都有,则方法上的注解相关配置,覆盖类上的注解
@Service
@Transactional
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements GoodsService {
//此处略
}
经过测试,我们发现,系统发生异常,商品表不会新增记录,事务配置成功。
删除掉测试代码int x=1/0
我们需要将所有涉及多表操作的服务类添加事务注解,例如SpecificationServiceImpl类