MyBatis入门七:多表关联查询一:获取多表关联查询结果;(查询结果包括多张表的字段,使用Map去承载存储查询结果)

本篇博客的内容很简单:就是当查询结果包括多张表的字段的时候,可以使用Map类承载存储查询结果。  

目录

0.问题描述

1.实际演示

首先,在goods.xml中增加一个select标签

然后实际调用测试:

说明:


0.问题描述

以前的查询都是在一张表上进行查询,自然查询结果可以只是用一个实体类的对象去承载和保存:如下,都是在t_goods一张表上查询的,又因为在写Goods实体类的时候,人为有意让Goods类和t_goods表对应,所以Goods类的属性故意按照t_goods表的字段来设置属性,所以,只查询t_goods表的时候,其查询结果自然可以使用Goods类型的对象去对应接受和承载。。。。。。

但是,如果涉及多表查询的时候,如果某次查询的结果的字段包括了多张表的字段,单纯使用一个实体类对象去对应接受查询结果,就够呛了,就无法承载了。


底层的操作仍然离不开Map,即在实际中不仅参数可以指定为Map类型(parameterType=“java.util.Map”),结果resultType也可以使用Map类型。

1.实际演示

首先,在goods.xml中增加一个select标签

<think>在Java中进行关联查询时,使用**VO(View Object)来承载查询结果种非常推荐且常见的做法**。以下是详细分析和实践建议: --- ### ✅ 为什么VO适合关联查询? 1. **字段定制化** VO可以**只包含查询需要的字段**(可能来自多张),避免实体类(Entity)承载无关字段造成的冗余[^1][^4]。 ```java // 示例:学校-教师关联VO(引用[1]) public class SchoolTeacherVo { private String teacherIdCard; // 教师字段 private String teacherName; // 教师字段 private String schoolName; // 学校字段 // 省略getter/setter } ``` 2. **解耦数据库与业务逻辑** - Entity通常与数据库严格对应,而**VO是面向视图的定制化对象**。 - 当查询结果包含聚合函数、计算字段时,VO能更灵活地承载(如`itemCount`)[^4]: ```java public class FormContainItemCountVO { private Long formId; // 单ID private String formName; // 单名称 private Long itemCount; // 关联项统计(非直接字段) } ``` 3. **避免N+1查询问题** 通过单条SQL关联查询后直接用VO接收,比用Entity的关联映射(如Hibernate的`@OneToMany`)更高效[^3]。 --- ### 📌 VO vs Entity vs DTO 的使用场景 | 类型 | 适用场景 | 查询适用性 | |---------|---------------------------------|----------------| | Entity | 单CRUD操作 | ❌ 字段不匹配 | | DTO | 服务层数据传输(可包含字段) | ✅ 可用 | | **VO** | **直接面向前端/接口的查询结果** | ✅ **最推荐** | > **关键区别**: > - DTO常用于**服务层之间传递数据**(如Service→Controller) > - VO专用于**接口响应数据封装**(如Controller→前端)[^3] --- ### 🔧 如何实现VO的映射? #### 方案1:MyBatis的`resultMap`手动映射 ```xml <resultMap id="schoolTeacherMap" type="SchoolTeacherVo"> <!-- 教师字段 --> <result property="teacherName" column="teacher_name"/> <!-- 学校字段 --> <result property="schoolName" column="school_name"/> </resultMap> <select id="selectSchoolTeacher" resultMap="schoolTeacherMap"> SELECT t.name AS teacher_name, s.name AS school_name FROM teacher t JOIN school s ON t.school_id = s.id </select> ``` #### 方案2:MyBatis注解自动映射(推荐) ```java @Select("SELECT t.name AS teacherName, s.name AS schoolName " + "FROM teacher t JOIN school s ON t.school_id = s.id") List<SchoolTeacherVo> getSchoolTeacherList(); ``` > **自动映射条件**: > 确保SQL的`AS`别名与VO属性名致(如`teacher_name` → `teacherName`)[^2]。 --- ### ⚠️ 使用VO的注意事项 1. **不要用`Map`代替VO** - 虽然`Map`可以临时承载结果,但会导致: - 类型不安全(需手动转换) - 字段名硬编码(如`map.get("school_name")`) - 难以维护[^3] 2. **复杂嵌套用DTO组合** 当结果包含层嵌套时(如用户→订单→商品),用组合DTO: ```java // 引用[3]示例 public class UserOrderDTO { private Integer uid; private String userName; private List<OrderGoodsDTO> orderGoodsList; // 嵌套DTO } ``` --- ### 🌰 完整实战流程(SpringBoot + MyBatis) **步骤1:定义VO** ```java public class OrderDetailVO { private String orderNo; // 订单号 private String userName; // 用户姓名(来自用户) private String productName; // 商品名称(来自商品) // getters/setters } ``` **步骤2:Mapper接口声明** ```java @Mapper public interface OrderMapper { @Select("SELECT o.order_no, u.name AS user_name, p.name AS product_name " + "FROM orders o " + "JOIN user u ON o.user_id = u.id " + "JOIN product p ON o.product_id = p.id") List<OrderDetailVO> selectOrderDetails(); } ``` **步骤3:Controller直接返回VO** ```java @RestController public class OrderController { @Autowired private OrderMapper orderMapper; @GetMapping("/orders") public List<OrderDetailVO> getOrders() { return orderMapper.selectOrderDetails(); } } ``` --- ### 💎 结论 在Java关联查询中: 1. **优先使用VO** → 面向接口输出,灵活定制字段[^1][^4] 2. **嵌套场景用DTO组合** → 处理层级数据[^3] 3. **禁止使用Map** → 保证类型安全和可维护性[^3] > 通过VO接收查询结果,是Java企业开发中的**标准实践**,既能精准控制输出数据,又能避免实体类污染。 --- §§相关问题§§ 1. VO和DTO在查询中有什么区别? 2. MyBatis如何自动将SQL结果映射到VO? 3. 如何设计层嵌套的查询结果(如用户→订单→商品)?</think>在 Java 进行关联查询时,**使用 VO(View Object)接收结果是最佳实践之**。以下是详细分析和建议: ### ✅ 为什么 VO 适合关联查询? 1. **字段定制化** VO 可以**只包含查询需要的字段**(可能来自多张),避免实体类(Entity)承载无关字段造成的冗余[^1][^4]: ```java // 示例:学校-教师关联VO(引用[1]) public class SchoolTeacherVo { private String teacherIdCard; // 教师字段 private String teacherName; // 教师字段 private String schoolName; // 学校字段 // 无需包含教师/学校实体的其他字段 } ``` 2. **解耦数据库与业务逻辑** - Entity 通常与数据库严格对应(如 `User` → `User` 实体) - VO 是**面向业务视图的对象**,可自由组合字段[^3]: ```java // 订单详情VO(跨3张) public class OrderDetailVO { private String orderNo; // 订单 private String userName; // 用户 private String productName; // 商品 } ``` 3. **安全控制** 避免将敏感字段(如 `password`、`salary`)通过 Entity 直接暴露给前端。 4. **兼容聚合查询** 轻松承载统计字段(引用[4]): ```java public class FormContainItemCountVO { private Long formId; // 单ID private String formName; // 单名称 private Long itemCount; // 关联项统计(非直接字段) } ``` --- ### ⚠️ VO vs Entity 使用场景对比 | **场景** | **使用 Entity** | **使用 VO** | |------------------------|----------------------|--------------------------| | 单简单查询 | ✅ 推荐 | ⚠️ 过度设计 | | 关联查询 | ❌ 字段冗余/结构不符 | ✅ **强烈推荐** | | 包含统计/计算字段 | ❌ 无法直接映射 | ✅ 原生支持 | | 嵌套层级数据() | ⚠️ 需复杂配置 | ✅ 通过DTO组合更清晰[^3] | --- ### 🔧 如何实现 VO 映射? #### 方案 1:MyBatis `resultType` 自动映射(推荐) ```java // 1. 定义VO public class OrderUserVO { private String orderNo; private String userName; // 自动匹配SQL别名 } // 2. Mapper接口 @Select("SELECT o.order_no, u.name AS userName " + "FROM orders o JOIN users u ON o.user_id = u.id") List<OrderUserVO> getOrderDetails(); ``` > **注意**:SQL 列别名必须与 VO 字段致(如 `u.name AS userName`) #### 方案 2:MyBatis `resultMap` 显式映射 ```xml <resultMap id="orderUserVOMap" type="OrderUserVO"> <result property="orderNo" column="order_no"/> <result property="userName" column="user_name"/> </resultMap> <select id="getOrderDetails" resultMap="orderUserVOMap"> SELECT o.order_no, u.name AS user_name FROM orders o JOIN users u ON o.user_id = u.id </select> ``` #### 方案 3:嵌套 DTO 处理(引用[3]) ```java // 用户订单DTO(主VO) public class UserOrderDTO { private Integer uid; private String userName; private List<OrderGoodsDTO> orders; // 嵌套子VO } // 订单商品DTO(子VO) public class OrderGoodsDTO { private Integer oid; private String productName; } // SQL使用JOIN查询后,MyBatis通过resultMap组装嵌套结构 ``` --- ### ❌ 避免的做法 1. **用 Entity 强撑查询** 导致字段污染(如 `Order` 实体包含 `userName` 违反设计原则)。 2. **用 `Map` 接收结果** 虽然技术可行,但带来问题(引用[3]): ```java // 不推荐!类型不安全、难维护 List<Map<String, Object>> result = sqlSession.selectList("getData"); String name = (String) result.get(0).get("user_name"); // 硬编码字段名 ``` 3. **用 Entity 包含关联实体** 复杂查询时易引发 N+1 性能问题(如 `User` 包含 `List<Order>` 触发额外查询)。 --- ### 💎 最佳实践总结 1. **简单查询** → 创建**扁平化 VO** 直接接收字段[^1][^4] 2. **层嵌套查询** → 使用 **DTO 组合 VO**(如 `UserOrderDTO` 包含 `OrderGoodsDTO`)[^3] 3. **统计/计算字段** → VO 中直接添加聚合字段(如 `itemCount`)[^4] 4. **避免** 使用 `Map` 或强行用 Entity 承载异构数据[^3] > **关键结论**:VO 在关联查询中具有不可替代性,它能精准匹配业务需求,保持代码可维护性,是 Java 企业级开发的标准实践。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值