乐优商城(八)商品管理

目录

一、SPU和SKU数据结构

1.1 SPU表

1.1.1 表结构

1.1.2 SPU中的规格参数

1.2 SKU表

1.2.1 表结构

1.2.2 KU中的特有规格参数

1.3 图片信息

二、商品查询

2.1 效果预览

2.2 页面实现

2.2.1 页面代码

2.2.2 页面分析

2.2.3 总结

2.3 后台接口

2.3.1 POJO

2.3.2 Controller

2.3.3 Service

2.3.4 Mapper

2.3.5 Category中拓展查询名称的功能

2.3.6 测试


商品管理

一、SPU和SKU数据结构

1.1 SPU表

1.1.1 表结构

tb_spu表

CREATE TABLE `tb_spu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'spu id',
  `title` varchar(255) NOT NULL DEFAULT '' COMMENT '标题',
  `sub_title` varchar(255) DEFAULT '' COMMENT '子标题',
  `cid1` bigint(20) NOT NULL COMMENT '1级类目id',
  `cid2` bigint(20) NOT NULL COMMENT '2级类目id',
  `cid3` bigint(20) NOT NULL COMMENT '3级类目id',
  `brand_id` bigint(20) NOT NULL COMMENT '商品所属品牌id',
  `saleable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否上架,0下架,1上架',
  `valid` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0已删除,1有效',
  `create_time` datetime DEFAULT NULL COMMENT '添加时间',
  `last_update_time` datetime DEFAULT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=208 DEFAULT CHARSET=utf8 COMMENT='spu表,该表描述的是一个抽象的商品,比如 iphone8';

但是这个表中没有商品的描述字段等附加信息

进行垂直拆分,将SPU表中的详情放在另一张表中:tb_spu_detail

CREATE TABLE `tb_spu_detail` (
  `spu_id` bigint(20) NOT NULL,
  `description` text COMMENT '商品描述信息',
  `specifications` varchar(3000) NOT NULL DEFAULT '' COMMENT '全部规格参数数据',
  `spec_template` varchar(1000) NOT NULL COMMENT '特有规格参数及可选值信息,json格式',
  `packing_list` varchar(1000) DEFAULT '' COMMENT '包装清单',
  `after_service` varchar(1000) DEFAULT '' COMMENT '售后服务',
  PRIMARY KEY (`spu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

因为这个表中的数据都比较大,所以为了不影响主表的查询效率,拆分出这张表。但是在这里需要详细介绍一下这两个字段:specifications和spec_template。

1.1.2 SPU中的规格参数

在上一篇博客中规格参数与商品分类绑定,一个分类下的所有SPU具有类似的规格参数。SPU下的SKU可能会有不同的规格参数,因此:

  • SPU中保存全局的规格参数信息。

  • SKU中保存特有规格参数。

以手机为例,品牌、操作系统等肯定是全局属性,内存、颜色等肯定是特有属性。

当确定了一个SPU,比如vivo的:x23

全局属性举例:

品牌:vivo
型号:x21

特有属性举例:

颜色:[冰钻黑,宝石红,极光白,黑金,魅夜紫]
内存:[6G]
机身存储:[64G,128G]

1.1.2.1. specifications字段

此字段保存全部规格参数信息,所以是一个json格式:

整体来看

整体看上去与规格参数表中的数据一样,也是一个数组,并且分组,每组下有多个参数 。

展开来看

可以看到,与规格参数表中的模板相比,最大的区别就是,这里指定了具体的值,因为商品确定了,其参数值肯定也确定了。

特有属性

刚才看到的是全局属性,那么特有属性在这个字段中如何存储呢?

特有属性也是有的,但是,注意看这里是不确定具体值的,因为特有属性只有在SKU中才能确定。这里只是保存了options,所有SKU属性的可选项。

在哪里可以使用specifications字段呢?商品详情页的规格参数信息中:

1.1.2.2. spec_template字段

既然specifications已经包含了所有的规格参数,那么为什么又多出了一个spec_template呢?里面又有哪些内容呢?

来看数据格式:

可以看出,里面只保存了规格参数中的特有属性,而且格式进行了大大的简化,只有属性的key,和待选项。

为什么要冗余保存一份?

因为很多场景下我们只需要查询特有规格属性,如果放在一起,每次查询再去分离比较麻烦。

比如,商品详情页展示可选的规格参数时:

1.2 SKU表

1.2.1 表结构

CREATE TABLE `tb_sku` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'sku id',
  `spu_id` bigint(20) NOT NULL COMMENT 'spu id',
  `title` varchar(255) NOT NULL COMMENT '商品标题',
  `images` varchar(1000) DEFAULT '' COMMENT '商品的图片,多个图片以‘,’分割',
  `price` bigint(15) NOT NULL DEFAULT '0' COMMENT '销售价格,单位为分',
  `indexes` varchar(100) COMMENT '特有规格属性在spu属性模板中的对应下标组合',
  `own_spec` varchar(1000) COMMENT 'sku的特有规格参数,json格式,反序列化时应使用linkedHashMap,保证有序',
  `enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0无效,1有效',
  `create_time` datetime NOT NULL COMMENT '添加时间',
  `last_update_time` datetime NOT NULL COMMENT '最后修改时间',
  PRIMARY KEY (`id`),
  KEY `key_spu_id` (`spu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku表,该表表示具体的商品实体,如黑色的64GB的iphone 8';

还有一张表代表库存:

CREATE TABLE `tb_stock` (
  `sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',
  `seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',
  `seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',
  `stock` int(9) NOT NULL COMMENT '库存数量',
  PRIMARY KEY (`sku_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息';

问题:为什么要将库存独立一张表?

因为库存字段写频率较高,而SKU的其它字段以读为主,因此将两张表分离,读写不会干扰。

特别需要注意的是sku表中的indexes字段和own_spec字段。sku中应该保存特有规格参数的值,就在这两个字段中。

1.2.2 KU中的特有规格参数

1.2.2.1 indexes字段

在SPU表中,已经对特有规格参数及可选项进行了保存,结构如下(样例):

{
    "机身颜色": [
        "香槟金",
        "樱花粉",
        "磨砂黑"
    ],
    "内存": [
        "2GB",
        "3GB"
    ],
    "机身存储": [
        "16GB",
        "32GB"
    ]
}

这些特有属性如果排列组合,会产生12个不同的SKU,而不同的SKU,其属性就是上面备选项中的一个。

比如:

  • 红米4X,香槟金,2GB内存,16GB存储

  • 红米4X,磨砂黑,2GB内存,32GB存储

你会发现,每一个属性值,对应于SPU中options数组的一个选项,如果我们记录下角标,就是这样:

  • 红米4X,0,0,0

  • 红米4X,2,0,1

既然如此,那么就可以将不同角标串联起来,作为SPU下不同SKU的标示。这就是indexes字段。

这个设计在商品详情页会特别有用

当用户点击选中一个特有属性,你就能根据 角标快速定位到sku。

1.2.2.2 own_spec字段

结构:

{"机身颜色":"香槟金","内存":"2GB","机身存储":"16GB"}

保存的是特有属性的键值对。

SPU中保存的是可选项,但不确定具体的值,而SKU中的保存的就是具体的键值对了。

这样,在页面展示规格参数信息时,就可以根据key来获取值,用于显示。

1.3 图片信息

将图片信息上传到虚拟机中/leyou/static目录下,然后使用nginx反向代理这些图片:

图片下载

二、商品查询

2.1 效果预览

页面结构与前面品牌管理相类似,也是一个data-table。

2.2 页面实现

2.2.1 页面代码

<template>
  <v-card>
    <v-card-title>
      <v-btn color="primary" @click="addGoods">新增商品</v-btn>
      <v-btn color="error" @click="deleteAllGoods">删除商品</v-btn>
      <v-spacer></v-spacer>
      <v-flex xs3>
        状态:
        <v-btn-toggle v-model="filter.saleable">
          <!--<v-btn flat>-->
            <!--全部-->
          <!--</v-btn>-->
          <v-btn flat :value="true">
            上架
          </v-btn>
          <v-btn flat :value="false">
            下架
          </v-btn>
        </v-btn-toggle>
      </v-flex>
      <v-text-field label="输入关键字搜索" class="flex sm3"  append-icon="search" v-model.lazy="filter.search"></v-text-field>
    </v-card-title>
    <v-divider></v-divider>
    <v-data-table
      :headers="headers"
      :items="goodsList"
      :pagination.sync="pagination"
      :total-items="totalGoods"
      :loading="loading"
      class="elevation-10"
      select-all
      v-model="selected"
    >
      <template slot="items" slot-scope="props">
        <td class="text-xs-center">
          <v-checkbox v-model="props.selected" primary hide-details>

          </v-checkbox>
        </td>
        <td class="text-xs-center">{{ props.item.id }}</td>
        <td class="text-xs-center">{{ props.item.title }}</td>
        <td class="text-xs-center">{{ props.item.cname}}</td>
        <td class="text-xs-center">{{ props.item.bname }}</td>
        <td class="justify-center layout">
          <v-btn icon small @click="editGoods(props.item)">
            <i class="el-icon-edit"/>
          </v-btn>
          <v-btn icon small @click="deleteItem(props.item.id)">
            <i class="el-icon-delete"/>
          </v-btn>
          <v-btn icon small v-if="props.item.saleable" @click="soldOutPut(props.item.id)">下架</v-btn>
          <v-btn icon small v-else @click="soldOutPut(props.item.id)">上架</v-btn>
        </td>
      </template>
      <template slot="no-data">
        <v-alert :value="true" color="error" icon="warning">
          对不起,没有查询到任何数据 :(
        </v-alert>
      </template>
      <template slot="pageText" slot-scope="props">
        共{{props.itemsLength}}条,当前:{{ props.pageStart }} - {{ props.pageStop }}
      </template>
    </v-data-table>

    <v-dialog max-width="900" v-model="show" persistent scrollable>
      <v-card>
        <!--对话框的标题-->
        <v-toolbar dense dark color="primary">
          <v-toolbar-title>{{isEdit ? '修改' : '新增'}}商品</v-toolbar-title>
          <v-spacer/>
          <!--关闭窗口的按钮-->
          <v-btn icon @click="closeWindow"><v-icon>close</v-icon></v-btn>
        </v-toolbar>
        <!--对话框的内容,表单-->
        <v-card-text class="px-5" style="height: 600px">
          <my-goods-form @initStep="initStep" @close="close" :oldGoods="oldGoods" :step="step" :show="show"  ref="goodsForm"/>
        </v-card-text>
        <v-card-actions>
          <v-layout row justify-center>
            <v-flex xs2>
              <v-btn :disabled="step === 1" color="primary" @click="lastStep">上一步</v-btn>
            </v-flex>
            <v-flex xs2>
              <v-btn :disabled="step === 4" color="primary" @click="nextStep">下一步</v-btn>
            </v-flex>
          </v-layout>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-card>
</template>

<script>
  // 导入自定义的表单组件
    import MyGoodsForm from './MyGoodsForm'

    export default {
      name: "MyGoods",
      data(){
          return{
            totalGoods:0, //总条数
            goodsList: [], //当前页品牌数据
            loading: true, //是否在加载中
            headers: [// 表头
              {text: 'id', align: 'center', value: 'id'},
              {text: '标题', align: 'center', sortable: false, value: 'name'},
              {text: '商品分类', align: 'center', sortable: false, value: 'image'},
              {text: '品牌', align: 'center', value: 'letter', sortable: true,},
              {text: '操作', align: 'center', value: 'id', sortable: false}
            ],
            show: false, //控制对话框的显示
            oldGoods : {}, //即将编辑的商品信息
            isEdit : false, //是否被编辑
            selected:[], //选择的条目
            pagination:{}, //分页信息
            filter :{
              saleable: true, //上架还是下架
              search: '',  //搜索过滤字段
            },
            step:1, //子组件中的步骤索引,初始为1
          }
        },
      watch:{
        pagination:{
          deep:true,
          handler(){
            this.getDataFromServer();
          }
        },
        filter:{
          deep:true,
          handler(){
            this.getDataFromServer();
          }
        }
      },
      created(){
        this.getDataFromServer();
      },
      methods:{
        close(){
          this.show = false;
          //重新获取数据
          this.getDataFromServer();
          //初始化弹窗
          this.step = 1;
        },
        initStep(){
          this.step = 1;
        },
        getDataFromServer(){ //从服务器加载数据

          // 开启进度条
          this.loading = true;

          //发起ajax请求
          // 分页查询page,rows,key,sortBy,desc

          this.$http.get("/item/goods/spu/page",{
            params:{
              page:this.pagination.page, //当前页
              rows:this.pagination.rowsPerPage, //每页大小
              sortBy:this.pagination.sortBy,  //排序字段
              desc:this.pagination.descending, //是否降序
              key:this.filter.search, //搜索条件
              saleable:this.filter.saleable ===0 ? true: this.filter.saleable//上下架
            }
          }).then(resp =>{
            this.goodsList=resp.data.items;
            this.totalGoods = resp.data.total;
            //关闭进度条
            this.loading = false;
          })

        },
        addGoods(){
          //修改标记
          this.isEdit = false;
          //控制弹窗可见
          this.show = true;
          //把oldGoods变为null
          this.oldGoods = null;
        },
        deleteAllGoods(){

          const deleteGoodsId = this.selected.map(s => {
            return s.id;
          });

          if (deleteGoodsId.length > 0){
            this.$message.confirm("全部删除,不可恢复!").then(() => {
              this.$http.delete("/item/goods/spu/"+deleteGoodsId.join("-")).then(() => {
                this.getDataFromServer();
              }).catch(() => {
                this.$message.error("删除失败!");
              })
            }).catch(() => {
              this.$message.info("删除取消!");
            })
          }
        },
        closeWindow(){
          this.oldGoods = null;
          //重新加载数据
          this.getDataFromServer();
          //关闭窗口
          this.show = false;
        },
        lastStep(){
          if (this.step > 1){
            this.step--;
          }
        },
        nextStep(){
          if (this.$refs.goodsForm.$refs.basic.validate() && this.step < 4)
          {
            this.step++;
          }
        },
        deleteItem(id){
          const selectId = this.selected.map( s => {
            return s.id;
          });
          if (selectId.length === 1 && selectId[0] === id) {
            this.$message.confirm("删除后,不可恢复!").then(() => {
              this.$http.delete("/item/goods/spu/"+id).then(() => {
                this.getDataFromServer();
              }).catch(() => {
                this.$message.error("删除失败!");
              })
            }).catch(() => {
              this.$message.info("删除取消!");
            });
          }
        },
        soldOutPut(id){
          //修改商品的saleable = false
          console.log(id);
          this.$http.put("/item/goods/spu/out/"+id).then(() => {
            this.$message.success("操作成功!");
            this.getDataFromServer();
          }).catch(() => {
            this.$message.error("操作失败!");
          });
        },
        editGoods(oldGoods){
          this.oldGoods = oldGoods;
          //构造商品分类
          const cname=oldGoods.cname.split("/");
          const categories=[];
          categories.push({id:oldGoods.cid1,name:cname[0]});
          categories.push({id:oldGoods.cid2,name:cname[1]});
          categories.push({id:oldGoods.cid3,name:cname[2]});
          this.oldGoods.categories = categories;

          this.$http.get("/item/goods/spu/"+oldGoods.id).then(({data}) => {
            this.isEdit = true;
            this.oldGoods.skusList = data.skus;
            this.oldGoods.spuDetail = data.spuDetail;
            this.oldGoods.spuDetail.specialSpecs = JSON.parse(data.spuDetail.specTemplate);
            this.oldGoods.spuDetail.specifications = JSON.parse(data.spuDetail.specifications);

            //显示弹窗
            this.show = true;
          }).catch();
        }
      },
      components:{
        MyGoodsForm
      }
    }
</script>

<style scoped>

</style>

2.2.2 页面分析

data属性

  • goodsList:当前页商品数据

  • totalGoods:商品总数

  • headers:头信息,需要修改头显示名称(id、标题、商品分类、品牌、操作)

  • oldGoods:准备要修改的商品,用于数据回显

  • pagination:分页信息

  • filter:过滤条件,页面上包含两个过滤条件搜索和上下架按钮,把这两个过滤条件都放在一个对象中进行监听。所以搜索框绑定的字段是filter.search,按钮组绑定的字段是filter.saleable。

getDataFromServer

从服务器请求数据,请求路径为:/item/goods/spu/page,参数有6个:当前页、每页大小、排序字段(id)、是否降序、搜索条件、上下架。同品牌管理一样,这6个参数在后台也是封装起来的。

addGoods

新增商品

deleteAllGoods

删除选中的全部数据条目

soldOutPut

根据id修改商品的上下架状态

editGoods

商品修改,通过传入的oldGoods数据,将数据回显,然后再进行修改保存。

deleteItem

根据传入的条目id,删除对应的单条数据。

2.2.3 总结

因为本模块只是商品查询,所以其他相关函数在下一篇介绍,本篇主要介绍商品数据查询以及显示,所以涉及到的函数只有:getDataFromServer。现在前端请求已经就绪,就差后台接口了。

2.3 后台接口

2.3.1 POJO

SPU

package com.leyou.item.pojo;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

/**
 * @author li
 */
@Table(name = "tb_spu")
public class Spu {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long brandId;
    /**
     * 1级类目
     */
    private Long cid1;
    /**
     * 2级类目
     */
    private Long cid2;
    /**
     * 3级类目
     */
    private Long cid3;
    /**
     * 标题
     */
    private String title;
    /**
     * 子标题
     */
    private String subTitle;
    /**
     * 是否上架
     */
    private Boolean saleable;
    /**
     * 是否有效,逻辑删除使用
     */
    private Boolean valid;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 最后修改时间
     */
    private Date lastUpdateTime;

    //省略get和set方法
}

SPU详情

package com.leyou.item.pojo;

import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author li
 */
@Table(name="tb_spu_detail")
public class SpuDetail {
    @Id
    /**
     * 对应的SPU的id
     */
    private Long spuId;
    /**
     * 商品描述
     */
    private String description;
    /**
     * 商品特殊规格的名称及可选值模板
     */
    private String specTemplate;
    /**
     * 商品的全局规格属性
     */
    private String specifications;
    /**
     * 包装清单
     */
    private String packingList;
    /**
     * 售后服务
     */
    private String afterService;

    //省略get和set方法
}

2.3.2 Controller

  • 请求方式:GET

  • 请求路径:/spu/page

  • 请求参数:

    • page:当前页

    • rows:每页大小

    • sortBy:排序字段

    • desc:是否降序

    • key:过滤条件

    • saleable:上架或下架

  • 返回结果:商品SPU的分页信息。

需要注意的一点是,在返回结果中,商品的分类和商品的品牌都是以id的形式存在的,但是前端页面要显示具体的信息。所以在这里需要新建一个类,继承SPU,并且在原有基础上进行属性拓展cname和bname,即具体的商品分类名称和品牌名称。

public class SpuBo extends Spu {

    String cname;// 商品分类名称
    
    String bname;// 品牌名称
    
    // 略 。。
}
    /**
     * 分页查询
     * @param page
     * @param rows
     * @param sortBy
     * @param desc
     * @param key
     * @param saleable
     * @return
     */
    @GetMapping("/spu/page")
    public ResponseEntity<PageResult<SpuBo>> querySpuByPage(
            @RequestParam(value = "page", defaultValue = "1") Integer page,
            @RequestParam(value = "rows", defaultValue = "5") Integer rows,
            @RequestParam(value = "sortBy", required = false) String sortBy,
            @RequestParam(value = "desc", defaultValue = "false") Boolean desc,
            @RequestParam(value = "key", required = false) String key,
            @RequestParam(value = "saleable",defaultValue = "true") Boolean saleable){
        SpuQueryByPageParameter spuQueryByPageParameter = new SpuQueryByPageParameter(page,rows,sortBy,desc,key,saleable);
        //分页查询spu信息
        PageResult<SpuBo> result = this.goodsService.querySpuByPageAndSort(spuQueryByPageParameter);
        if (result == null){
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
        }
        return ResponseEntity.ok(result);
    }

2.3.3 Service

接口

    /**
     * 分页查询
     * @param spuQueryByPageParameter
     * @return
     */
    PageResult<SpuBo> querySpuByPageAndSort(SpuQueryByPageParameter spuQueryByPageParameter);

实现类

    /**
     * 分页查询
     * @param spuQueryByPageParameter
     * @return
     */
    @Override
    public PageResult<SpuBo> querySpuByPageAndSort(SpuQueryByPageParameter spuQueryByPageParameter) {

        //1.查询spu,分页查询,最多查询100条
        PageHelper.startPage(spuQueryByPageParameter.getPage(),Math.min(spuQueryByPageParameter.getRows(),100));

        //2.创建查询条件
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();

        //3.条件过滤
        //3.1 是否过滤上下架
        if (spuQueryByPageParameter.getSaleable() != null){
            System.out.println(spuQueryByPageParameter.getSaleable());
            criteria.orEqualTo("saleable",spuQueryByPageParameter.getSaleable());
        }
        //3.2 是否模糊查询
        if (StringUtils.isNotBlank(spuQueryByPageParameter.getKey())){
            criteria.andLike("title","%"+spuQueryByPageParameter.getKey()+"%");
        }
        //3.3 是否排序
        if (StringUtils.isNotBlank(spuQueryByPageParameter.getSortBy())){
            System.out.println(spuQueryByPageParameter.getSortBy());
            example.setOrderByClause(spuQueryByPageParameter.getSortBy()+(spuQueryByPageParameter.getDesc()? " DESC":" ASC"));
        }
        Page<Spu> pageInfo = (Page<Spu>) this.spuMapper.selectByExample(example);


        //将spu变为spubo
        List<SpuBo> list = pageInfo.getResult().stream().map(spu -> {
            SpuBo spuBo = new SpuBo();
            //1.属性拷贝
            BeanUtils.copyProperties(spu,spuBo);

            //2.查询spu的商品分类名称,各级分类
            List<String> nameList = this.categoryService.queryNameByIds(Arrays.asList(spu.getCid1(),spu.getCid2(),spu.getCid3()));
            //3.拼接名字,并存入
            spuBo.setCname(StringUtils.join(nameList,"/"));
            //4.查询品牌名称
            Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());
            spuBo.setBname(brand.getName());
            return spuBo;
        }).collect(Collectors.toList());

        return new PageResult<>(pageInfo.getTotal(),list);
    }

2.3.4 Mapper

package com.leyou.item.mapper;

import com.leyou.item.pojo.Spu;
import tk.mybatis.mapper.common.Mapper;

/**
 * @Author: 98050
 * Time: 2018-08-14 22:14
 * Feature:
 */
@org.apache.ibatis.annotations.Mapper
public interface SpuMapper extends Mapper<Spu> {
}

2.3.5 Category中拓展查询名称的功能

页面需要商品的分类名称需要在这里查询,因此要额外提供查询分类名称的功能,

在CategoryService中添加功能:

接口

    /**
     * 根据ids查询名字
     * @param asList
     * @return
     */
    List<String> queryNameByIds(List<Long> asList);

实现类


    /**
     * 根据ids查询名字
     * @param asList
     * @return
     */
    @Override
    public List<String> queryNameByIds(List<Long> asList) {
        List<String> names = new ArrayList<>();
        if (asList != null && asList.size() !=0){
            for (Long id : asList) {
                names.add(this.categoryMapper.queryNameById(id));
            }
        }
        return names;
        //使用通用mapper接口中的SelectByIdListMapper接口查询
        //return this.categoryMapper.selectByIdList(asList).stream().map(Category::getName).collect(Collectors.toList());
    }

因为是把cid1、cid2、cid3放入list中作为参数传入,即根据id数组来查询,所以有两种解决方法:

1. 遍历id数组,每次查询一条记录,将每次得到的结果都存入list中最后返回即可。

上边的代码就是采用这种方法,所以需要自己在categoryMapper中新增方法queryByNameById:

    /**
     * 根据id查名字
     * @param id
     * @return
     */
    @Select("SELECT name FROM tb_category WHERE id = #{id}")
    String queryNameById(Long id);

2. 继承通用mapper的SelectByIdListMapper方法,一次性查出返回。

CategoryService中queryNameByIds方法新的实现类

public List<String> queryNameByIds(List<Long> ids) {
    return this.categoryMapper.selectByIdList(ids).stream().map(Category::getName).collect(Collectors.toList());
}

mapper的selectByIDList方法是来自于通用mapper。不过需要我们在mapper上继承一个通用mapper接口:

public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category, Long> { 
    // ...coding
}

2.3.6 测试

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值