最蠢的代码
/**
* 插入商品规格信息
*/
private void insertSpeInfo(GoodsDetailVo model, Goods commonFields) {
Long goodId = commonFields.getId();
//为商品规格基本信息添加所属商品ID,填充公共字段
List<GoodsSpecificationVo> goodsSpecificationVos = model.getGoodsSpecificationVos()
.stream().peek(goodsSpecificationVo -> {
goodsSpecificationVo.setGoodsId(goodId);
fillCommonFields(commonFields, goodsSpecificationVo);
}).collect(Collectors.toList());
//spcID集合
List<Long> spcIds = new ArrayList<>();
//规格值对象集合
List<GoodsSpecValues> goodsSpecValues = new ArrayList<>();
//取出规格名与对应的规格值集合
for (GoodsSpecificationVo goodsSpecificationVo : goodsSpecificationVos) {
//保存规格名
GoodsSpecification goodsSpecification = BeanUtil.copyProperties(goodsSpecificationVo, GoodsSpecification.class);
goodsSpecificationService.save(goodsSpecification);
//获取规格名ID
Long spcID = goodsSpecification.getId();
spcIds.add(spcID);
List<String> specValue = goodsSpecificationVo.getSpecValue();
//保存规格值
for (String sv : specValue) {
GoodsSpecValues values = new GoodsSpecValues(spcID, sv);
fillCommonFields(commonFields, values);
goodsSpecValues.add(values);
}
goodsSpecValuesService.saveBatch(goodsSpecValues);
goodsSpecValues.clear();
}
String strIds = listToString(spcIds);
//拿到规格值和对应ID
Map<String, Long> spcValIds = goodsSpecValuesService
.list(Wrappers.<GoodsSpecValues>lambdaQuery().in(GoodsSpecValues::getSpecId, spcIds))
.stream().collect(Collectors.toMap(GoodsSpecValues::getSpecValue, GoodsSpecValues::getId));
//获取商品sku集合
List<GoodsSku> goodsSkus = model.getGoodsSkuVos().stream()
.map(goodsSkuVo -> {
GoodsSku goodsSku = BeanUtil.copyProperties(goodsSkuVo, GoodsSku.class);
fillCommonFields(commonFields, goodsSku);
goodsSku.setGoodsId(goodId);
//设置规格名组合ID集合
goodsSku.setGoodsSpec(strIds);
//设置规格值组合ID集合
List<Long> spcValCombinationIds = goodsSkuVo.getGoodsSpecValues().stream()
.map(spcValIds::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
goodsSku.setGoodsSpecValue(listToString(spcValCombinationIds));
return goodsSku;
})
.collect(Collectors.toList());
// 写入商品总库存信息
model.getGood().setQuantity(goodsSkus.stream().mapToInt(GoodsSku::getQuantity).sum());
updateById(model.getGood());
//保存sku
goodsSkuService.saveBatch(goodsSkus);
}
先大概解析一下这段代码的作用
它接受一个GoodsDetailVo
对象和一个Goods
对象作为参数,然后从GoodsDetailVo
中提取商品规格信息并保存到数据库中。
具体而言,该代码执行以下主要步骤:
- 从
GoodsDetailVo
对象中获取商品规格信息,包括规格名、规格值、SKU(库存单位)等。 - 为商品规格基本信息添加所属商品ID和填充公共字段(例如创建时间、更新时间等)。
- 保存规格名到数据库,并获取规格名的ID。
- 保存规格值到数据库,并使用规格名的ID关联规格值。
- 将规格值和对应的ID存储在
spcValIds
映射中,用于后续的关联。 - 处理商品SKU信息,设置商品ID、规格名组合ID集合、规格值组合ID集合等。
- 计算并更新商品总库存信息。
- 保存商品SKU信息到数据库。
存在的主要问题
-
在循环中使用了数据库操作:在循环内部进行数据库的保存操作,会导致频繁的数据库访问,影响性能。
for (GoodsSpecificationVo goodsSpecificationVo : goodsSpecificationVos) { //保存规格名 GoodsSpecification goodsSpecification = BeanUtil.copyProperties(goodsSpecificationVo, GoodsSpecification.class); goodsSpecificationService.save(goodsSpecification); //获取规格名ID Long spcID = goodsSpecification.getId(); spcIds.add(spcID); List<String> specValue = goodsSpecificationVo.getSpecValue(); //保存规格值 for (String sv : specValue) { GoodsSpecValues values = new GoodsSpecValues(spcID, sv); fillCommonFields(commonFields, values); goodsSpecValues.add(values); } goodsSpecValuesService.saveBatch(goodsSpecValues); goodsSpecValues.clear(); }
- 在这里,循环调用
goodsSpecificationService.save(goodsSpecification);
依次逐个保存规格名,之后再将获取到规格名ID逐个插入到规格值中,即这段代码GoodsSpecValues values = new GoodsSpecValues(spcID, sv);
- 之后又犯了同样的错误,又一次在循环中来了数据库IO操作
goodsSpecValuesService.saveBatch(goodsSpecValues);
- 在这里,循环调用
-
重复清空集合:在保存规格值的部分,使用了一个清空集合的操作
goodsSpecValues.clear()
,但该操作在每次循环中执行,并不需要如此频繁地清空集合。可以将清空集合的操作移到循环外部,在循环结束后进行一次清空即可。
goodsSpecValuesService.saveBatch(goodsSpecValues);
goodsSpecValues.clear();
- 方法调用顺序问题:在更新商品总库存信息的部分,调用了
updateById(model.getGood())
来更新商品信息,但是该方法的调用在保存SKU之前,可能导致库存信息不准确。
// 写入商品总库存信息
model.getGood().setQuantity(goodsSkus.stream().mapToInt(GoodsSku::getQuantity).sum());
updateById(model.getGood());
//保存sku
goodsSkuService.saveBatch(goodsSkus);
-
没必要的数据库查询:
//拿到规格值和对应ID Map<String, Long> spcValIds = goodsSpecValuesService .list(Wrappers.<GoodsSpecValues>lambdaQuery().in(GoodsSpecValues::getSpecId, spcIds)) .stream().collect(Collectors.toMap(GoodsSpecValues::getSpecValue, GoodsSpecValues::getId)); //设置规格值组合ID集合 List<Long> spcValCombinationIds = goodsSkuVo.getGoodsSpecValues().stream() .map(spcValIds::get) .filter(Objects::nonNull) .collect(Collectors.toList()); goodsSku.setGoodsSpecValue(listToString(spcValCombinationIds)); public String listToString(List<Long> ids) { return ids.stream() .map(String::valueOf) .collect(Collectors.joining(",")); }
- 其实这次数据库查询是完全不必要的,应该直接从已经保存过后的
goodsSpecValues
局部变量中去筛选数据即可,不过此时的goodsSpecValues
也不是完全的,原因就在于第一个问题中我采用在循环中保存规格值 - 应该在构造这个
spcValIds
集合的时候就直接将value
值转为String
类型
- 其实这次数据库查询是完全不必要的,应该直接从已经保存过后的
-
代码可读性:方法代码逻辑过多,应该对不同表不同数据的插入做拆分,抽离逻辑,抽离成单独的小方法,之后再拼接在一起增强可读性
优化后的代码
这是优化过后的最终版本,中间还有两次优化就不作展示了
private void insertSpeInfo(GoodsDetailVo model, Goods commonFields) {
Long goodId = commonFields.getId();
//为商品规格基本信息添加所属商品ID,填充公共字段,获取Vo对象
List<GoodsSpecificationVo> goodsSpecificationVos = model.getGoodsSpecificationVos()
.stream().peek(goodsSpecificationVo -> {
goodsSpecificationVo.setGoodsId(goodId);
fillCommonFields(commonFields, goodsSpecificationVo);
}).collect(Collectors.toList());
//保存规格名
List<GoodsSpecification> goodsSpecifications = insertGoodsSpecification(goodsSpecificationVos);
//规格名ID集合
List<Long> spcIds = goodsSpecifications.stream().map(GoodsSpecification::getId).collect(Collectors.toList());
//保存规格值
List<GoodsSpecValues> goodsSpecValues = insertGoodsSpecValue(commonFields, goodsSpecificationVos, spcIds);
// 保存sku
List<GoodsSkuVo> goodsSkuVos = model.getGoodsSkuVos()
.stream().peek(goodsSkuVo -> {
fillCommonFields(commonFields, goodsSkuVo);
goodsSkuVo.setGoodsId(goodId);
})
.collect(Collectors.toList());
String strIds = listToString(spcIds);
List<GoodsSku> goodsSkus = insertGoodsSku(strIds, goodsSpecValues, goodsSkuVos);
// 写入商品总库存信息
insertTotalQuantity(model, goodsSkus);
}
//保存商品的库存信息
private void insertTotalQuantity(GoodsDetailVo model, List<GoodsSku> goodsSkus) {
//计算总库存
int totalQuantity = goodsSkus.stream().mapToInt(GoodsSku::getQuantity).sum();
Goods good = model.getGood();
good.setQuantity(totalQuantity);
//更新商品信息
update(good);
}
//保存商品的sku信息
private List<GoodsSku> insertGoodsSku(String speIds, List<GoodsSpecValues> goodsSpecValues,
List<GoodsSkuVo> goodsSkuVos) {
// 拿到规格值和对应ID
Map<String, String> spcValIds = goodsSpecValues.stream()
.collect(Collectors.toMap(GoodsSpecValues::getSpecValue,
goodsSpecValue -> String.valueOf(goodsSpecValue.getId())));
List<GoodsSku> goodsSkus = goodsSkuVos.stream()
.map(goodsSkuVo -> {
GoodsSku goodsSku = BeanUtil.copyProperties(goodsSkuVo, GoodsSku.class);
// 设置规格名组合ID集合
goodsSku.setGoodsSpec(speIds);
// 设置规格值组合ID集合
goodsSku.setGoodsSpecValue(
goodsSkuVo.getGoodsSpecValues().stream()
.map(spcValIds::get)
.collect(Collectors.joining(","))
);
return goodsSku;
})
.collect(Collectors.toList());
//保存sku
goodsSkuService.saveBatch(goodsSkus);
return goodsSkus;
}
//保存商品的规格值信息
private List<GoodsSpecValues> insertGoodsSpecValue
(Goods commonFields, List<GoodsSpecificationVo> goodsSpecificationVos, List<Long> spcIds) {
//规格值列表
List<GoodsSpecValues> goodsSpecValues = new ArrayList<>();
int i = 0;
for (GoodsSpecificationVo goodsSpecificationVo : goodsSpecificationVos) {
Long spcID = spcIds.get(i++);
List<String> specValue = goodsSpecificationVo.getSpecValue();
List<GoodsSpecValues> specValuesList = specValue.stream().map(sv -> {
GoodsSpecValues values = new GoodsSpecValues(spcID, sv);
fillCommonFields(commonFields, values);
return values;
}).collect(Collectors.toList());
goodsSpecValues.addAll(specValuesList);
}
goodsSpecValuesService.saveBatch(goodsSpecValues);
return goodsSpecValues;
}
//保存商品的规格名信息
private List<GoodsSpecification> insertGoodsSpecification
(List<GoodsSpecificationVo> goodsSpecificationVos) {
List<GoodsSpecification> goodsSpecifications = BeanUtil.copyToList(goodsSpecificationVos, GoodsSpecification.class);
goodsSpecificationService.saveBatch(goodsSpecifications);
return goodsSpecifications;
}
以下是对代码进行优化的几个点
-
增强可读性:对方法的整体做了拆分,对于不同的数据的插入拆分成了不同的独立小方法,增强了可读性
-
减少数据库访问次数:将数据库操作尽量集中在一起,减少频繁的数据库访问。
//保存商品的规格值信息
private List<GoodsSpecValues> insertGoodsSpecValue
(Goods commonFields, List<GoodsSpecificationVo> goodsSpecificationVos, List<Long> spcIds) {
//规格值列表
List<GoodsSpecValues> goodsSpecValues = new ArrayList<>(); //定义一个局部变量用来存储规格值
int i = 0;
for (GoodsSpecificationVo goodsSpecificationVo : goodsSpecificationVos) {
Long spcID = spcIds.get(i++);
List<String> specValue = goodsSpecificationVo.getSpecValue();
List<GoodsSpecValues> specValuesList = specValue.stream().map(sv -> {
GoodsSpecValues values = new GoodsSpecValues(spcID, sv);
fillCommonFields(commonFields, values);
return values;
}).collect(Collectors.toList());
goodsSpecValues.addAll(specValuesList);
}
goodsSpecValuesService.saveBatch(goodsSpecValues);//批量一次性保存规格值
return goodsSpecValues;
}
//保存商品的规格名信息
private List<GoodsSpecification> insertGoodsSpecification
(List<GoodsSpecificationVo> goodsSpecificationVos) {
List<GoodsSpecification> goodsSpecifications =
BeanUtil.copyToList(goodsSpecificationVos,GoodsSpecification.class);
goodsSpecificationService.saveBatch(goodsSpecifications); //批量一次性保存规格名信息
return goodsSpecifications;
}
- 优化规格值和对应ID的获取方式:使用已批量保存的规格之集合获取规格值和对应ID的映射关系。
private List<GoodsSku> insertGoodsSku(String speIds, List<GoodsSpecValues> goodsSpecValues,
List<GoodsSkuVo> goodsSkuVos) {
// 拿到规格值和对应ID
Map<String, String> spcValIds = goodsSpecValues.stream() //减少了不必要的数据库查询
.collect(Collectors.toMap(GoodsSpecValues::getSpecValue,
goodsSpecValue -> String.valueOf(goodsSpecValue.getId()))); //直接将value值转为string类型
List<GoodsSku> goodsSkus = goodsSkuVos.stream()
.map(goodsSkuVo -> {
GoodsSku goodsSku = BeanUtil.copyProperties(goodsSkuVo, GoodsSku.class);
// 设置规格名组合ID集合
goodsSku.setGoodsSpec(speIds);
// 设置规格值组合ID集合
goodsSku.setGoodsSpecValue(
goodsSkuVo.getGoodsSpecValues().stream()
.map(spcValIds::get)
.collect(Collectors.joining(",")) //减少了方法的调用
);
return goodsSku;
})
.collect(Collectors.toList());
//保存sku
goodsSkuService.saveBatch(goodsSkus);
return goodsSkus;
}
- 更新商品总库存信息:调整更新商品信息的位置,确保在保存SKU之后更新商品总库存信息。
// 保存sku
insertGoodsSku(strIds, goodsSpecValues, goodsSkuVos);
// 写入商品总库存信息
insertTotalQuantity(model, commonFields);