news 2026/4/17 22:25:28

MP查询的实体有对象嵌套,阿里巴巴的人是怎么处理的

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MP查询的实体有对象嵌套,阿里巴巴的人是怎么处理的

阿里巴巴处理 MP 实体对象嵌套的方案

1. 阿里巴巴推荐的第一方案:DO + DTO 分层

// 1. 数据库实体(DO - Data Object)保持简单@Data@TableName("alarm_config")publicclassAlarmConfigDO{privateLongid;privateStringconfigName;privateLongschemeId;privateIntegerstatus;// ... 其他基础字段}@Data@TableName("alarm_rule")publicclassAlarmRuleDO{privateLongid;privateStringruleName;privateStringruleType;privateDoublethreshold;privateLongconfigId;// 外键,不是对象// ... 其他基础字段}// 2. 业务传输对象(DTO)包含嵌套关系@DatapublicclassAlarmConfigDTO{privateLongid;privateStringconfigName;privateLongschemeId;privateIntegerstatus;// 嵌套对象(不是数据库字段)privateList<AlarmRuleDTO>rules;@DatapublicstaticclassAlarmRuleDTO{privateLongid;privateStringruleName;privateStringruleType;privateDoublethreshold;// 可以包含 config 信息,但不是对象嵌套privateLongconfigId;privateStringconfigName;}}// 3. Service 层组装@ServicepublicclassAlarmConfigService{publicAlarmConfigDTOgetConfigWithRules(LongconfigId){// 分别查询AlarmConfigDOconfigDO=alarmConfigMapper.selectById(configId);List<AlarmRuleDO>ruleDOs=alarmRuleMapper.selectByConfigId(configId);// 手动组装 DTOAlarmConfigDTOdto=convertToDTO(configDO);dto.setRules(ruleDOs.stream().map(this::convertRuleToDTO).collect(Collectors.toList()));returndto;}}

2. 阿里巴巴的 MyBatis 映射方案(XML/注解)

// 方案一:MyBatis XML 映射// AlarmConfigMapper.xml<mapper namespace="com.aiwei.mapper.AlarmConfigMapper"><resultMap id="ConfigWithRulesMap"type="AlarmConfigDTO"><id property="id"column="id"/><result property="configName"column="config_name"/><!--集合映射:一对多--><collection property="rules"ofType="AlarmRuleDTO"select="selectRulesByConfigId"column="id"/></resultMap><select id="selectConfigWithRules"resultMap="ConfigWithRulesMap">SELECT*FROM alarm_configWHEREid=#{configId}</select><select id="selectRulesByConfigId"resultType="AlarmRuleDTO">SELECT*FROM alarm_ruleWHEREconfig_id=#{configId}ANDis_delete=0</select></mapper>// 方案二:注解映射(阿里巴巴较少用,但可用)@MapperpublicinterfaceAlarmConfigMapperextendsBaseMapper<AlarmConfigDO>{@Select("SELECT * FROM alarm_config WHERE id = #{configId}")@Results({@Result(property="id",column="id"),@Result(property="rules",column="id",many=@Many(select="selectRulesByConfigId"))})AlarmConfigDTOselectConfigWithRules(@Param("configId")LongconfigId);@Select("SELECT * FROM alarm_rule WHERE config_id = #{configId}")List<AlarmRuleDTO>selectRulesByConfigId(@Param("configId")LongconfigId);}

3. 阿里巴巴的高性能方案:多表联查 + Map 处理

// 避免 N+1 查询问题,使用 JOIN 一次查完@ServicepublicclassAlarmQueryService{/** * 阿里巴巴推荐:联表查询返回 Map,手动组装 */publicMap<String,Object>getConfigWithRules(LongconfigId){// 1. 联表查询(避免多次查询)List<Map<String,Object>>resultList=alarmConfigMapper.selectConfigWithRulesJoin(configId);// 2. 手动组装为嵌套结构Map<String,Object>configMap=newHashMap<>();List<Map<String,Object>>ruleList=newArrayList<>();if(!resultList.isEmpty()){// 第一条记录包含 config 信息Map<String,Object>firstRow=resultList.get(0);configMap.put("id",firstRow.get("config_id"));configMap.put("name",firstRow.get("config_name"));// 提取所有规则for(Map<String,Object>row:resultList){if(row.get("rule_id")!=null){Map<String,Object>ruleMap=newHashMap<>();ruleMap.put("id",row.get("rule_id"));ruleMap.put("name",row.get("rule_name"));ruleMap.put("type",row.get("rule_type"));ruleMap.put("threshold",row.get("threshold"));ruleList.add(ruleMap);}}}configMap.put("rules",ruleList);returnconfigMap;}}// Mapper SQL@Select("SELECT "+" c.id as config_id, c.config_name, "+" r.id as rule_id, r.rule_name, r.rule_type, r.threshold "+"FROM alarm_config c "+"LEFT JOIN alarm_rule r ON c.id = r.config_id "+"WHERE c.id = #{configId} AND r.is_delete = 0")List<Map<String,Object>>selectConfigWithRulesJoin(@Param("configId")LongconfigId);

4. 阿里巴巴的 COLA 架构模式

// 按照 COLA 架构(Clean Object-oriented & Layered Architecture)// 1. 领域对象(Domain Object)@DatapublicclassAlarmConfig{privateLongid;privateStringname;privateList<AlarmRule>rules;// 领域方法publicvoidaddRule(AlarmRulerule){if(rules==null){rules=newArrayList<>();}rules.add(rule);rule.setConfigId(this.id);}publicbooleanvalidate(){returnrules!=null&&rules.stream().allMatch(AlarmRule::isValid);}}// 2. 数据转换器(Assembler)@ComponentpublicclassAlarmConfigAssembler{publicAlarmConfigtoDomain(AlarmConfigDOconfigDO,List<AlarmRuleDO>ruleDOs){AlarmConfigconfig=newAlarmConfig();config.setId(configDO.getId());config.setName(configDO.getConfigName());// 转换规则List<AlarmRule>rules=ruleDOs.stream().map(this::toRuleDomain).collect(Collectors.toList());config.setRules(rules);returnconfig;}publicAlarmConfigDOtoDO(AlarmConfigconfig){AlarmConfigDOconfigDO=newAlarmConfigDO();configDO.setId(config.getId());configDO.setConfigName(config.getName());returnconfigDO;}}// 3. 仓储层(Repository)@RepositorypublicclassAlarmConfigRepository{@AutowiredprivateAlarmConfigMapperconfigMapper;@AutowiredprivateAlarmRuleMapperruleMapper;@AutowiredprivateAlarmConfigAssemblerassembler;publicAlarmConfigfindById(LongconfigId){// 分别查询AlarmConfigDOconfigDO=configMapper.selectById(configId);List<AlarmRuleDO>ruleDOs=ruleMapper.selectByConfigId(configId);// 组装为领域对象returnassembler.toDomain(configDO,ruleDOs);}}

5. 阿里巴巴的 MapStruct + Builder 模式

// 使用 MapStruct 自动转换@Mapper(componentModel="spring",uses={AlarmRuleMapperConverter.class})publicinterfaceAlarmConfigConverter{AlarmConfigDTOtoDTO(AlarmConfigDOconfigDO);@Mapping(target="rules",ignore=true)AlarmConfigDTOtoSimpleDTO(AlarmConfigDOconfigDO);// 带嵌套的转换defaultAlarmConfigDTOtoDTOWithRules(AlarmConfigDOconfigDO,List<AlarmRuleDO>ruleDOs){AlarmConfigDTOdto=toSimpleDTO(configDO);dto.setRules(ruleDOs.stream().map(AlarmRuleMapperConverter.INSTANCE::toDTO).collect(Collectors.toList()));returndto;}}// Service 使用@Service@RequiredArgsConstructorpublicclassAlarmConfigService{privatefinalAlarmConfigMapperconfigMapper;privatefinalAlarmRuleMapperruleMapper;privatefinalAlarmConfigConverterconverter;publicAlarmConfigDTOgetConfigDetail(LongconfigId){AlarmConfigDOconfigDO=configMapper.selectById(configId);List<AlarmRuleDO>ruleDOs=ruleMapper.selectByConfigId(configId);returnconverter.toDTOWithRules(configDO,ruleDOs);}}

6. 阿里巴巴处理嵌套查询的实际案例

// 案例:告警方案 -> 配置 -> 规则 三级嵌套@ServicepublicclassAlarmSchemeService{/** * 三级嵌套查询的最佳实践 */publicMap<String,Object>getSchemeDetail(LongschemeId){// 1. 查询方案基础信息AlarmSchemeDOscheme=alarmSchemeMapper.selectById(schemeId);// 2. 查询所有配置(避免N+1,批量查询)List<AlarmConfigDO>configs=alarmConfigMapper.selectBySchemeId(schemeId);List<Long>configIds=configs.stream().map(AlarmConfigDO::getId).collect(Collectors.toList());// 3. 批量查询所有规则(一次查询)List<AlarmRuleDO>allRules=alarmRuleMapper.selectByConfigIds(configIds);// 4. 按configId分组规则Map<Long,List<AlarmRuleDO>>rulesByConfigId=allRules.stream().collect(Collectors.groupingBy(AlarmRuleDO::getConfigId));// 5. 组装结果Map<String,Object>result=newLinkedHashMap<>();result.put("scheme",convertScheme(scheme));List<Map<String,Object>>configList=configs.stream().map(config->{Map<String,Object>configMap=convertConfig(config);List<AlarmRuleDO>rules=rulesByConfigId.get(config.getId());if(rules!=null){configMap.put("rules",rules.stream().map(this::convertRule).collect(Collectors.toList()));}returnconfigMap;}).collect(Collectors.toList());result.put("configs",configList);returnresult;}/** * 使用 MP 的 QueryWrapper 进行批量查询 */privateList<AlarmRuleDO>batchQueryRules(List<Long>configIds){if(configIds.isEmpty()){returnCollections.emptyList();}QueryWrapper<AlarmRuleDO>wrapper=newQueryWrapper<>();wrapper.in("config_id",configIds).eq("is_delete",0).eq("status",1).orderByAsc("sort_order");returnalarmRuleMapper.selectList(wrapper);}}

7. 阿里巴巴的缓存方案处理嵌套

// 使用缓存避免重复查询嵌套数据@Service@Slf4jpublicclassCachedAlarmService{@AutowiredprivateRedisTemplate<String,Object>redisTemplate;/** * 阿里巴巴常用:三级缓存策略 */@Cacheable(value="alarm:config:detail",key="#configId",unless="#result == null")publicAlarmConfigDTOgetCachedConfigWithRules(LongconfigId){// 1. 查询配置AlarmConfigDOconfig=alarmConfigMapper.selectById(configId);if(config==null){returnnull;}// 2. 查询规则(使用本地缓存)List<AlarmRuleDO>rules=getCachedRulesByConfigId(configId);// 3. 组装AlarmConfigDTOdto=newAlarmConfigDTO();BeanUtils.copyProperties(config,dto);dto.setRules(rules.stream().map(this::convertRuleToDTO).collect(Collectors.toList()));returndto;}/** * 批量查询规则并缓存 */@Cacheable(value="alarm:rules",key="#configId")publicList<AlarmRuleDO>getCachedRulesByConfigId(LongconfigId){QueryWrapper<AlarmRuleDO>wrapper=newQueryWrapper<>();wrapper.eq("config_id",configId).eq("is_delete",0).orderByAsc("sort_order");returnalarmRuleMapper.selectList(wrapper);}}

8. 阿里巴巴推荐的最佳实践总结

核心原则:

  1. DO 保持简单:数据库实体只包含数据库字段
  2. DTO 承载业务:传输对象包含嵌套关系
  3. 避免 N+1 查询:优先使用 JOIN 或批量查询
  4. 手动组装优于自动映射:更可控,性能更好

具体做法:

// ✅ 阿里巴巴推荐做法:publicAlarmConfigDTOgetConfigDetail(LongconfigId){// 1. 分别查询基础数据AlarmConfigDOconfig=configMapper.selectById(configId);List<AlarmRuleDO>rules=ruleMapper.selectByConfigId(configId);// 2. 手动组装 DTOAlarmConfigDTOdto=newAlarmConfigDTO();BeanUtils.copyProperties(config,dto);// 3. 转换嵌套对象dto.setRules(rules.stream().map(rule->{AlarmRuleDTOruleDto=newAlarmRuleDTO();BeanUtils.copyProperties(rule,ruleDto);returnruleDto;}).collect(Collectors.toList()));returndto;}// ❌ 不推荐做法:// 在 DO 中直接定义对象属性// 依赖 MyBatis 的自动嵌套映射(性能差,难维护)

对于你的告警分析业务:

// 推荐方案:publicMap<String,Object>analyzeAlarm(LongconfigId,LongflightRecordId){// 1. 分别查询AlarmConfigDOconfig=alarmConfigMapper.selectById(configId);List<AlarmRuleDO>rules=alarmRuleMapper.selectEnabledByConfigId(configId);List<FlightDataDO>flightData=flightDataMapper.selectByRecordId(flightRecordId);// 2. 转换为 Map 格式Map<String,List<Map<String,Object>>>ruleMap=rules.stream().collect(Collectors.groupingBy(AlarmRuleDO::getRuleType,Collectors.mapping(this::convertRuleToMap,Collectors.toList())));// 3. 执行业务逻辑Map<String,Object>result=executeAnalysis(ruleMap,flightData);// 4. 组装返回result.put("config",convertConfigToMap(config));result.put("queryTime",LocalDateTime.now());returnresult;}

阿里巴巴内部通常选择:手动组装 + Map 结构,而不是依赖 ORM 的自动嵌套映射。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 15:45:28

Qoder IDE 0.2.21 版更新来了:新增自定义指令与 NES 自动导入功能

今天&#xff0c;Qoder IDE 迎来新版本升级&#xff0c;新增自定义指令与 NES 自动导入功能。 特性 自定义指令&#xff1a;将常用提示词或工作流封装为指令&#xff0c;在 Agent 中通过 / 一键调用&#xff0c;提升日常开发效率。 NES 支持自动导入&#xff1a;基于 LSP 提供…

作者头像 李华
网站建设 2026/4/8 16:37:42

微信扫码登录 iframe 方案中的状态拦截陷阱

微信扫码登录 iframe 方案中的状态拦截陷阱 背景 在 Web 端实现微信扫码登录时&#xff0c;常见的方案是使用 iframe 嵌入微信二维码页面。用户扫码授权后&#xff0c;iframe 内部会重定向到我们配置的回调页面&#xff0c;回调页面再通过 postMessage 通知父页面完成登录。 …

作者头像 李华
网站建设 2026/4/16 12:35:27

为什么你的Agent日志总是丢失?深度解析Docker Compose日志驱动机制

第一章&#xff1a;为什么你的Agent日志总是丢失&#xff1f;在分布式系统和自动化任务调度中&#xff0c;Agent作为核心执行单元&#xff0c;其运行日志是排查故障、监控状态的关键依据。然而&#xff0c;许多开发者发现日志“莫名消失”&#xff0c;导致问题难以追溯。这通常…

作者头像 李华
网站建设 2026/4/7 0:20:38

如何3步搞定多模态Agent的复杂依赖?Docker多阶段构建深度解读

第一章&#xff1a;多模态 Agent 的 Docker 依赖管理 在构建多模态 Agent 系统时&#xff0c;Docker 成为管理复杂依赖关系的核心工具。这类系统通常融合视觉、语音、文本等多种处理模块&#xff0c;每个模块可能依赖不同版本的框架&#xff08;如 PyTorch、TensorFlow&#xf…

作者头像 李华
网站建设 2026/4/17 18:58:20

EDI数据交换2026年展望:洞察2026年EDI数据交换的新范式

电子数据交换&#xff08;EDI&#xff09;作为企业间&#xff08;B2B&#xff09;数据自动交换的基石&#xff0c;已在全球供应链中运行数十年。然而&#xff0c;在近两年&#xff0c;面对日益复杂的全球供应链挑战、技术的快速迭代以及对数据智能的更高要求&#xff0c;EDI正经…

作者头像 李华