文章目录
- 一,新增product/attr/base/list接口
 - 二,踩坑记录
 - 1. 使用 `@Lazy` 注解
 - 2. 使用 `@PostConstruct` 注解
 - 代码分析
 - 解决方案分析
 
这一节的主要内容是完成规格参数的列表查询功能。

一,新增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注解来解决循环依赖的问题。下面是对这个方案的解释:
代码分析
-  
ServiceA 类:
@Service public class ServiceA { private ServiceB serviceB; public void setServiceB(ServiceB serviceB) { this.serviceB = serviceB; } }- ServiceA 类包含一个 
ServiceB类型的私有成员变量serviceB。 - 提供了一个 
setServiceB方法来注入ServiceB的实例。 
 - ServiceA 类包含一个 
 -  
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方法中,通过调用serviceA的setServiceB方法来设置ServiceB的实例。 
 - ServiceB 类包含一个 
 
解决方案分析
-  
setter注入:
- ServiceA 的 
serviceB成员变量通过setter方法注入。 - ServiceB 的 
serviceA成员变量通过Spring自动注入。 
 - ServiceA 的 
 -  
@PostConstruct 注解:
- ServiceB 类中的 
setServiceB方法使用了@PostConstruct注解,这意味着该方法将在bean初始化完成之后调用。 - 在 
setServiceB方法中,通过调用serviceA的setServiceB方法来设置ServiceB的实例。 
 - ServiceB 类中的 
 





![[QT开发_音乐播放器项目笔记01]](https://i-blog.csdnimg.cn/direct/f2e8d9042bd44fddb8654f32eb3de2bb.png)













