up:: SpringBoot电商项目商品模块商品详情接口
说明:
(1) 本篇博客比较重要的点:
●【参数没有放在body中,而是放在了url中】+【后端使用实体类去接收参数】:对于这种情况,不能使用@RequestBody注解,自然也不能使用@RequestParam注解;(只是,我们在写实体类的时候,实体类的属性要比照着接口参数写,不能写错)
● 对于那些条件比较多的接口,专门创建一个Query对象,来组织条件;是一个不错的开发技巧;
● 对于排序方式这种查询条件,我们最好根据接口要求,在程序中规定好【究竟有哪些排序条件】;这能很好的防止接口乱调用,程序也更加安全;
● 本篇博客也涉及到了【mybatis动态SQL,<if></if>】、【MySQL的模糊查询,like】、【MySQL的in子句】、【mybatis的批处理中的<foreach>子标签】等;
(2) 本篇博客一个待进一步研究的地方:这人的查询,我们使用的是MySQL的原生查询方式;但是,如果以后我们的项目和大数据结合的时候,似乎可以了解一下ElasticSearch这些技术,来更好的实现数据的检索和查询;
一:前台的【商品列表】接口说明;
1.前台的【商品列表】接口,在界面上的表现;
(1)我们可以不使用任何条件,来分页显示商品数据;
(2)我们可以使用【按价格从低到高或从高到低排序】这个条件,来分页显示商品数据;
(3)我们可以使用【商品名需要包含某个关键词】这个条件,来分页显示商品数据;与此同时,也可以加入【按价格从低到高或从高到低排序】这个条件;
(4)我们可以使用【商品需要隶属于“某商品分类及其子分类”】这个条件,来分页显示商品数据;与此同时,也可以加入【按价格从低到高或从高到低排序】这个条件;
PS:上图少标注了一个,id=19的果冻橙这个分类,是id=4的橘子橙子这个分类的子目录;;;所以,id=36的四川果冻橙要需要查询出来;
可以看到,前台的【商品列表】接口,这一个接口需要承担的任务还是挺多的;这就需要我们在开发这个接口时,做好条件判断和处理;
2.前台的【商品列表】接口文档;
以这个请求实例为例,来仔细说下前台的【商品列表】接口文档;
接口返回内容:
这儿主要说明两点:
● 这儿显示的商品都是扁平的,没有嵌套;
● 根据接口文档对返回数据的格式要求,我们可以发现,需要使用PageHelper分页组件的PageInfo对象来组织返回的分页数据;
3. 前台的【商品列表】接口,开发分析;
说明:
(1) 前台的【商品列表】接口,这儿主要的地方就是根据不同的条件,去搜索的功能;
(2) 入参判空;判断参数是否为空;
● orderBy、categoryId、keyword这三个参数,可传可不传;如果传的话,我们才会使用这个参数,把这个参数作为条件去查询;
● 所以,对于orderBy、categoryId、keyword这三个参数的判空:就不使用@Valid注解的Validation参数校验的方式,来判空了;
(3) 如果传了keyword这个参数,比如传了“桃”,就表示我们搜索所有商品名中有“桃”的商品;对于这种模糊搜索的功能,我们使用%通配符和like关键字,去实现模糊查找;
● 在查找的时候,我们会使用【%通配符】和【like关键字】,在SQL中进行拼接,实现查找功能;
二:正式开发;
1.在ProductController类中,创建查询前台的商品列表的方法:list()方法;
说明:
(1)因为前台的【商品列表】接口的参数很多,所以我们创建了一个实体类ProductListReq类,用这个实体类来接收参数;
ProductListReq类:
● 根据前台的【商品列表】接口的参数,编写ProductListReq类,并且这儿给pageNum和pageSize设了默认值;
● 因为,接口是Get请求,即不是【Post请求,且把参数放在Body中】的情况;所以,这儿使用实体类接收参数的时候,是不能使用@RequestBody注解(这个注解是在,POST请求,且参数是放在Body中,而且后端我们使用实体类去接收参数时:才使用的)的;然后,又因为我们这儿是使用实体类去接收参数,所以也不能使用@RequestParam注解;所以,这儿我们使用实体类去接收参数的时候,实体类的属性一定要和接口的参数名写一样,别写错了;
(2)请求方式,url,接收参数,要写对;
(3)因为根据接口文档中对接口返回数据格式的要求,我们需要使用PageHelper的PageInfo来包装分页数据,所以我们在开发Service层的方法时,其返回类型就是PageInfo格式的;
(4)Service层的list方法,在下一部分介绍;
2.1.在ProductServiceImpl类中,创建以分页的方式查询符合条件的商品的方法:list()方法;(重点!!!)
在ProductServiceImpl类中,创建查询前台的商品列表的方法:list()方法;
说明:
(1.1)【categoryId,keyword】这两个查询条件处理:
构建一个专门用于组织查询条件的Query对象:ProductListQuery类:用Query对象来组织查询条件,代码会更加有条理,也方便以后的扩展;(这个开发技巧,比较重要!!!)
● 因为,根据接口文档和项目的实际情况,可以看到前台的【商品列表】接口在查询商品时候,其查询条件可能存在以下几种情况:(PS:这儿的条件种类,其实还不算太多;以后可能遇到条件种类很多的情况,到那时,专门构建一个Query对象的好处,会更加明显)
● 创建ProductListQuery类;
说明:
(1) 该Query类的第一个属性,就是keyword,其对应条件中的搜索关键词;
(2) 该Query类的第二个属性,是一个List集合,这里面是:当接口传了categoryId这个商品分类的条件后:所有符合条件的商品的category_id;
也就是说:【如果在界面上,我们点击了“新鲜水果”后】→【就相当于我们传了categoryId=3,这个参数】→【就表示,我们此时加入了categoryId=3,这个搜索条件】→【这个条件经过处理,等到具体查product表的时候,就变成了:category_id在{3,4,11,12,14,19,28}的商品了】;
其实,这样一看,这个查询条件还是比较复杂的,由这儿也能感受到【专门构建查询条件的Query对象】的好处;
● 即,我们根据前端传过来的条件,把这些条件进行处理、分析,然后构建一个Query对象,然后再以这个Query对象作为条件,去数据库查询我们最终需要的结果;这是一个十分不错的、条理清晰的策略;
(1.2) 【categoryId,keyword】这两个查询条件处理:
构建Query对象之:判断是否有【搜索关键词】这个条件:如果有,就把这个条件构建到Query对象上去;
● 这儿使用了Spring提供的一个判空工具:StringUtils.isEmpty();
● 因为,搜索关键词时:比如我们搜索“冰”,是要查询商品名中包含“冰”是商品;所以,在查数据库的时候,需要是模糊查找;所以,这儿,我们要先拼凑一个模糊查找要用的字符串;(PS:有关MySQL模糊查询,可以参考【WHERE子句条件查询】);
(1.3) 【categoryId,keyword】这两个查询条件处理:
构建Query对象之:判断是否有【商品分类Id】这个条件:如果有,就把这个条件处理一下,然后构建到Query对象上去;
● 开发前台的【分类列表(递归)】接口时,创建的:递归查询分类目录的方法listCategoryForCustomer()方法回顾;如果忘记了,可以回去看下【SpringBoot电商项目商品分类模块前台的分类列表递归接口】;
● listCategoryForCustomer()方法在此处使用的说明;
●【商品分类Id】这个条件的:处理流程分析;
由此,也可以发现,当接口传入了一个条件后,我们有的时候是无法直接用这个条件去查数据库;;;而是,要先处理下这个条件,把其处理成合适的东西,然后再那这个处理后的东西作为条件去查数据库;
具体来说,就是构建查询对象;;;这会使得程序结构更加条理清晰、有助于后期的功能扩展;
●工具方法:getCategoryIds()分析;这个方法很简单,没什么好说的;
至此,接口可能的条件:keyword和categoryId都已经构建到ProductListQuery这个Query对象上去了;
疑问:我们接口还有一个可能的条件就是排序方式,为什么这个条件不也构建到ProductListQuery这个Query对象上去呐?
这是因为,orderBy是有关排序的一个条件;我们这儿是使用PageHelper这个分页插件来实现分页查询;而,PageHelper在构建的时候,就可以设置排序方式,即我们可以通过PageHelper来应用这个条件;所以,就没有把其构建到ProductListQuery这个Query对象上去;
(2) 【orderBy】这一个条件处理:在构建PageHelper的时候,应用这个排序条件;需要注意:这儿我们需要【在程序中,规定好有哪些字段可以排序】,这非常重要,有关程序的安全;
一种错误写法:前端传什么条件,我们就原样不动的把其传到MySQL上去进行排序;;;其实,有哪些字段可以排序,我们必须要提前设计好;
(3)以前面构建的Query对象作为条件,去调用我们在Dao层编写查询方法:selectList()方法;;这个方法,在下一部分介绍;
(4)把查询结果,包装成PageInfo对象,返回给调用方;
2.2.在ProductService接口中,反向生成方法的声明;
3.1在ProductMapper中,声明根据条件,查询商品的方法:selectList()方法;
说明:
(1) 可以看到,虽然这儿我们传给Dao层的参数是一个实体类,但我们这儿依旧使用了@Param注解;
3.2在ProductMapper.xml中,编写对应的实现SQL;
说明:
(1) 入参需要是我们的Query查询类:parameterType=“com.imooc.mall.model.query.ProductListQuery”;
(2) 因为,这儿ProductListQuery中的两个条件是可有可无的;即,如果前端传了条件,这儿就有,如果前端没传条件,这儿就没有;所以,这儿查询SQL的时候,需要判断一下;于是就涉及到了Mybatis的动态SQL了;
● 有关mybatis动态SQL,如有需要可以参考:【动态sql】;
(3) SQL语句,整体分析;
(4) 这儿使用到了MySQL的like模糊查询;
● 有关MySQL的like模糊查询,如有需要可以参考:【WHERE子句条件查询】;
(5) 这儿使用到了MySQL的in子句,用来进行范围判断;
● 有关MySQL的in子句,如有需要可以参考:【WHERE子句条件查询】;
(6) 这儿使用到了Mybatis的foreach子标签;
● 上一次我们使用到Mybatis的foreach子标签是在【SpringBoot电商项目商品模块批量上下架商品接口】
● Mybatisforeach子标签:最初,是在【MyBatis批处理】中进行介绍的;如有需要,可以去参考;
至此,前台的【商品列表】接口就开发完成了;
三:测试;
启动项目;
测试1:
测试2:
测试3:
测试4:
测试5:一个在目前的项目中,实际上不存在的情况;