hibernate多表联合查询接收返回结果集

本文介绍了一种使用List<Object>接收数据的方法,并通过循环和类型转换处理数据,适用于2019年独角兽企业对Python工程师的招聘需求。
<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 企业级开发的标准实践。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值