谷粒商城实战笔记-77-商品服务-API-平台属性-规格参数列表

这一节的主要内容是完成规格参数的列表查询功能。

在这里插入图片描述

一,新增product/attr/base/list接口

这个接口用来查询规格参数列表。

	@RequestMapping("/base/list/{catelogId}")
    public R list(@RequestParam Map<String, Object> params,@PathVariable Long catelogId){
        PageUtils page = attrService.queryBaseAttrPage(params, catelogId);

        return R.ok().put("page", page);
    }

在这里插入图片描述

考虑到前端展示属性信息包含其所属分类和分组信息,我们用一个VO来封装响应数据。

在这里插入图片描述

后端实现如下。

public PageUtils queryBaseAttrPage(Map<String, Object> params, Long catelogId) {
        QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<>();

        if(catelogId != 0){
            queryWrapper.eq("catelog_id",catelogId);
        }

        String key = (String) params.get("key");
        if(StrUtil.isNotEmpty(key)){
            //attr_id  attr_name
            queryWrapper.and((wrapper)->{
                wrapper.eq("attr_id",key).or().like("attr_name",key);
            });
        }

        IPage<AttrEntity> page = this.page(
                new Query<AttrEntity>().getPage(params),
                queryWrapper
        );

        PageUtils pageUtils = new PageUtils(page);
        List<AttrEntity> records = page.getRecords();
        List<AttrRespVo> respVos = records.stream().map((attrEntity) -> {
            AttrRespVo attrRespVo = new AttrRespVo();
            BeanUtils.copyProperties(attrEntity, attrRespVo);

            //1、设置分类和分组的名字
            AttrAttrgroupRelationEntity attrId = relationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));
            if (attrId != null && attrId.getAttrGroupId()!=null) {
                AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrId.getAttrGroupId());
                attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName());
            }

            CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
            if (categoryEntity != null) {
                attrRespVo.setCatelogName(categoryEntity.getName());
            }
            return attrRespVo;
        }).collect(Collectors.toList());

        pageUtils.setList(respVos);
        return pageUtils;

在这段代码中,有一个开发中需要遵循的原则:避免使用JOIN查询以提高性能

在数据库查询中,JOIN操作可能导致性能问题,尤其是涉及多表关联时。JOIN操作会增加查询复杂度,可能导致大量的磁盘I/O操作和CPU计算开销,尤其是在处理大数据量时。此外,JOIN查询还可能因索引失效而进一步降低查询效率,尤其是在没有适当索引支持的情况下。因此,减少JOIN操作是提高查询性能的有效手段之一。

具体来说,代码通过在关联表中冗余存储分类名称和分组名称,从而避免了在查询时进行表的JOIN操作。

代码首先通过QueryWrapper构建查询条件,并执行分页查询获取AttrEntity对象列表。随后,遍历这些对象,通过额外的查询操作来获取分类名称和分组名称,而不是直接通过JOIN操作获取这些信息。

这种做法的必要性在于,JOIN操作可能会导致性能瓶颈,尤其是在涉及多表关联查询的情况下。

二,踩坑记录

启动项目失败,原因是出现了循环依赖。

在这里插入图片描述

The dependencies of some of the beans in the application context form a cycle:

   attrController (field private com.atguigu.gulimall.product.service.AttrService com.atguigu.gulimall.product.controller.AttrController.attrService)
      ↓
   attrService (field com.atguigu.gulimall.product.service.CategoryService com.atguigu.gulimall.product.service.impl.AttrServiceImpl.categoryService)
      ↓
   categoryService (field com.atguigu.gulimall.product.service.CategoryBrandRelationService com.atguigu.gulimall.product.service.impl.CategoryServiceImpl.categoryBrandRelationService)
┌─────┐
|  categoryBrandRelationService (field com.atguigu.gulimall.product.service.BrandService com.atguigu.gulimall.product.service.impl.CategoryBrandRelationServiceImpl.brandService)
↑     ↓
|  brandService (field com.atguigu.gulimall.product.service.CategoryBrandRelationService com.atguigu.gulimall.product.service.impl.BrandServiceImpl.categoryBrandRelationService)
└─────┘

当然可以。下面是针对每一种解决Spring循环依赖方法的代码示例。

1. 使用 @Lazy 注解

解决思路:通过使用 @Lazy 注解,可以让Spring容器延迟初始化bean,直到真正使用时才创建实例。

在这里插入图片描述

示例代码:

public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService {
    @Autowired
    @Lazy
    private CategoryBrandRelationService categoryBrandRelationService;
    
    // ....
}

2. 使用 @PostConstruct 注解

解决思路

  • 这种方式不是@Lazy简单。
  • ServiceA不要自动注入ServiceB,而是提供一个public的setServiceB方法。
  • ServiceB自动注入ServiceA,且提供一个PostConstruct方法,在方法中调用ServiceA的set方法,把自身对象传递给ServiceB

示例代码:

@Service
public class ServiceA {
    private ServiceB serviceB;
    
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}


@Service
public class ServiceB {
	@Autowired
    private ServiceA serviceA;

    @PostConstruct
    public void setServiceB() {
        this.serviceA.setServiceB(this);
    }
}


这段代码展示了如何通过使用setter注入和@PostConstruct注解来解决循环依赖的问题。下面是对这个方案的解释:

代码分析

  1. ServiceA 类:

    @Service
    public class ServiceA {
        private ServiceB serviceB;
        
        public void setServiceB(ServiceB serviceB) {
            this.serviceB = serviceB;
        }
    }
    
    • ServiceA 类包含一个 ServiceB 类型的私有成员变量 serviceB
    • 提供了一个 setServiceB 方法来注入 ServiceB 的实例。
  2. ServiceB 类:

    @Service
    public class ServiceB {
        @Autowired
        private ServiceA serviceA;
        
        @PostConstruct
        public void setServiceB() {
            this.serviceA.setServiceB(this);
        }
    }
    
    • ServiceB 类包含一个 ServiceA 类型的私有成员变量 serviceA,并通过 @Autowired 注解自动注入。
    • 定义了一个带有 @PostConstruct 注解的方法 setServiceB,该方法在bean初始化完成后执行。
    • setServiceB 方法中,通过调用 serviceAsetServiceB 方法来设置 ServiceB 的实例。

解决方案分析

  1. setter注入:

    • ServiceAserviceB 成员变量通过setter方法注入。
    • ServiceBserviceA 成员变量通过Spring自动注入。
  2. @PostConstruct 注解:

    • ServiceB 类中的 setServiceB 方法使用了 @PostConstruct 注解,这意味着该方法将在bean初始化完成之后调用。
    • setServiceB 方法中,通过调用 serviceAsetServiceB 方法来设置 ServiceB 的实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小手追梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值