mybatis中collection子查询注入参数为null

具体实现参照网上,但是可能遇到注入参数为null的情况,经过查阅及自己测试记录一下:

  • 子查询的参数中,有<if test="">之类,需要指定别名,通过 http://blog.youkuaiyun.com/mamba10/article/details/20927225 ,了解到设置注入参数时可以设置别名
<collection property="projectList" column="{projectId=project_id}" javaType="ArrayList" ofType="Project" select="selectProjectList"/>
  • 但是在没有<if test="">这种情况下,有时不能有别名,否则会出现注入参数为空
 <collection property="projectList" column="project_id" javaType="ArrayList" ofType="Project" select="selectProjectList"/>

 

具体原因不明

 

<think>我们正在处理MyBatis中一对多映射(collection)出现null值的问题。根据用户的问题和之前的回答,我们需要总结出解决方法。同时,参考站内引用,我们可以得到一些关键点。 引用[1]提到使用<collection>标签实现一对多映射,引用[2]说明了resultMap的基本结构,引用[3]展示了一个查询结果,其中员工集合被正确映射(尽管dept属性为null,但这不是我们当前关注点)。 用户的问题:在MyBatis中处理一对多映射(collection)时,如何应对映射结果为null的情况。 根据之前的回答,我们可以总结以下步骤: 1. **检查resultMap配置**:确保<collection>标签的property、ofType、column等属性正确,并且内部有必要的<id>和<result>映射。 2. **检查SQL查询**:确保SQL查询包含了关联表的所有必要字段,并且使用了正确的JOIN(如LEFT JOIN)来确保即使关联表没有数据,主表数据也能返回,避免关联数据为null时整个集合不显示。 3. **检查延迟加载**:如果使用了延迟加载(fetchType="lazy"),需要确保在代码中触发了集合的加载。 4. **使用嵌套查询(分步查询)**:如果单条SQL语句过于复杂,可以考虑使用分步查询,即先查询主表,再通过另一个查询查询关联表。 现在,我们针对这些步骤给出详细的解决方案。 ### 1. 检查resultMap配置 在resultMap中,<collection>标签用于映射一对多关系。如果配置不正确,可能导致集合为null。 错误示例: ```xml <resultMap id="orderMap" type="Order"> <id property="id" column="id"/> <collection property="items" ofType="Item"/> </resultMap> ``` 上面的配置中,<collection>内部没有定义任何映射,MyBatis无法将查询结果映射到Item对象的属性上,因此集合可能为null或空。 正确配置: ```xml <resultMap id="orderMap" type="Order"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <!-- 使用collection映射一对多 --> <collection property="items" ofType="Item"> <id property="id" column="item_id"/> <result property="name" column="item_name"/> <result property="quantity" column="quantity"/> </collection> </resultMap> ``` 注意:必须为集合中的元素定义主键(<id>标签),这有助于MyBatis识别对象的唯一性,避免重复创建对象。 ### 2. 检查SQL查询 SQL查询必须返回<collection>映射中需要的所有列,并且使用正确的JOIN。 例如,我们有一个订单(order)和订单项(item)表,订单表的主键是id,订单项表有外键order_id关联订单表。 错误示例(只查询了订单表,没有关联订单项表): ```sql SELECT * FROM order WHERE id = #{id} ``` 正确示例(使用LEFT JOIN,即使没有订单项,也会返回订单,且items集合为空列表而不是null): ```sql SELECT o.id, o.order_no, i.id as item_id, i.name as item_name, i.quantity FROM order o LEFT JOIN item i ON o.id = i.order_id WHERE o.id = #{id} ``` 注意:使用LEFT JOIN可以确保即使没有关联的订单项,订单数据也会被查询出来,此时items集合为空(不是null)。如果使用INNER JOIN,当没有订单项时,整个订单都不会被查询出来。 ### 3. 检查延迟加载 如果配置了延迟加载,需要确保在事务关闭前访问了集合属性,否则会抛出异常或得到null。 在<collection>中配置了select属性时,通常会使用延迟加载。例如: ```xml <resultMap id="orderMap" type="Order"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <collection property="items" ofType="Item" column="id" select="selectItemsByOrderId" fetchType="lazy"/> </resultMap> ``` 在查询主对象后,需要访问集合属性以触发加载: ```java Order order = orderMapper.selectOrder(orderId); // 访问items集合,触发加载 List<Item> items = order.getItems(); ``` 如果不想使用延迟加载,可以将fetchType设置为eager: ```xml <collection property="items" ofType="Item" column="id" select="selectItemsByOrderId" fetchType="eager"/> ``` ### 4. 使用分步查询(嵌套查询) 分步查询可以避免复杂的JOIN,将一次查询拆分为两次查询,先查询主表,再根据主表结果查询关联表。 步骤: (1)定义查询订单项的方法(在ItemMapper中): ```xml <select id="selectItemsByOrderId" resultType="Item"> SELECT * FROM item WHERE order_id = #{orderId} </select> ``` (2)在OrderMapper的resultMap中引用这个方法: ```xml <resultMap id="orderMap" type="Order"> <id property="id" column="id"/> <result property="orderNo" column="order_no"/> <collection property="items" ofType="Item" column="id" select="com.example.mapper.ItemMapper.selectItemsByOrderId"/> </resultMap> ``` (3)查询订单: ```xml <select id="selectOrder" resultMap="orderMap"> SELECT * FROM order WHERE id = #{id} </select> ``` 这样,MyBatis会先执行selectOrder,然后根据返回的订单id执行selectItemsByOrderId,将结果放入items集合中。 注意:分步查询可能会引起N+1问题,即查询一个订单列表时,会执行1次查询订单和N次查询订单项。可以通过MyBatis的延迟加载和批量加载来优化。 ### 总结 当MyBatiscollection映射出现null时,请按照以下步骤排查: 1. **检查resultMap配置**:确保<collection>内部有正确的映射,特别是主键<id>。 2. **检查SQL查询**:使用LEFT JOIN确保关联表数据被查询,且列名与resultMap中的column一致。 3. **检查延迟加载**:如果使用了延迟加载,确保在事务关闭前访问了集合属性;或者设置fetchType="eager"。 4. **考虑使用分步查询**:将复杂的JOIN拆分为多个简单查询。 通过以上方法,可以解决collection映射为null的问题。 §§相关问题§§ 1. MyBatis中多对一(association)映射出现null值如何解决? 2. 如何优化MyBatiscollection分步查询性能(避免N+1问题)? 3. MyBatis中如何配置全局的延迟加载开关?</think>### MyBatis 一对多映射(collectionnull值处理指南 在MyBatis中处理一对多映射时,`<collection>`出现null值通常由配置错误或数据问题导致。以下是系统化的解决方案: --- #### 一、核心问题排查步骤 1. **检查SQL查询结果** 确认SQL是否返回了关联数据: ```sql -- 示例:验证关联数据是否存在 SELECT * FROM orders o LEFT JOIN order_items oi ON o.id = oi.order_id WHERE o.id = #{orderId} ``` - 若结果集中`order_items`相关列为`NULL`,说明无关联数据(正常现象) - 若存在数据但映射失败,进入下一步排查 2. **验证resultMap配置** ```xml <resultMap id="orderMap" type="Order"> <id property="id" column="id"/> <!-- 必须定义主键 --> <!-- 关键检查点 --> <collection property="orderItems" <!-- 1.属性名是否与Java类一致 --> ofType="OrderItem" <!-- 2.集合元素类型是否正确 --> column="id" <!-- 3.关联列名是否匹配 --> notNullColumn="order_id"> <!-- 4.空值处理属性 --> <id property="id" column="item_id"/> <!-- 5.必须定义子元素主键 --> <result property="name" column="item_name"/> </collection> </resultMap> ``` 3. **启用MyBatis日志** 在`mybatis-config.xml`添加: ```xml <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> ``` 观察日志中是否出现: - `Mapped Statements collection found null`(映射语句返回null) - `Property not found`(属性匹配失败) --- #### 二、解决方案大全 ##### 方案1:空集合代替null(推荐) ```xml <collection property="orderItems" ofType="OrderItem" column="id" select="selectOrderItems" notNullColumn="order_id"/> <!-- 当order_id非空时初始化集合 --> <select id="selectOrderItems" resultType="OrderItem"> SELECT * FROM order_items WHERE order_id = #{id} </select> ``` ```java // Java实体类初始化默认集合 public class Order { private List<OrderItem> orderItems = new ArrayList<>(); // 默认空集合 } ``` ##### 方案2:LEFT JOIN + 空值检测 ```xml <resultMap id="orderMap" type="Order"> <collection property="orderItems" resultMap="orderItemMap"/> </resultMap> <resultMap id="orderItemMap" type="OrderItem"> <!-- 当列值为NULL时跳过映射 --> <id property="id" column="item_id" /> <result property="name" column="item_name" nullValue="[EMPTY]"/> <!-- 自定义空值标记 --> </resultMap> ``` ##### 方案3:自定义类型处理器 ```java public class EmptyCollectionHandler extends BaseTypeHandler<List<?>> { @Override public List<?> getNullableResult(ResultSet rs, String col) { return Collections.emptyList(); // 始终返回空集合 } } ``` ```xml <resultMap id="orderMap" type="Order"> <collection property="orderItems" typeHandler="com.handler.EmptyCollectionHandler"/> </resultMap> ``` ##### 方案4:WHERE子句过滤(确保非空) ```xml <collection property="nonNullItems" column="id" select="selectNonNullItems"/> <select id="selectNonNullItems" resultType="OrderItem"> SELECT * FROM order_items WHERE order_id = #{id} AND item_name IS NOT NULL <!-- 排除空记录 --> </select> ``` --- #### 三、最佳实践总结 1. **防御性编程** 在Java实体类中初始化空集合: ```java public class Order { private List<OrderItem> orderItems = new ArrayList<>(); } ``` 2. **明确映射规则** ```xml <collection property="items" columnPrefix="item_" <!-- 避免列名冲突 --> notNullColumn="order_id" <!-- 关联键非空才初始化 --> fetchType="eager"> <!-- 禁用延迟加载 --> ``` 3. **SQL质量保障** ```sql SELECT o.id, oi.id AS item_id, -- 别名必须与resultMap匹配 NULLIF(oi.name, '') AS item_name -- 空字符串转NULL FROM orders o LEFT JOIN order_items oi ON o.id = oi.order_id ``` > **关键点**:当关联数据不存在时,MyBatis默认**不初始化集合**(保持null),通过上述方案可统一返回空集合[^1][^2]。 --- #### 四、典型错误案例 ```xml <!-- 错误配置:缺少主键定义 --> <resultMap id="errorMap" type="Order"> <collection property="items" ofType="OrderItem"> <!-- 缺失<id>导致映射失败 --> <result property="name" column="item_name"/> </collection> </resultMap> ``` ```java // 错误示例:未初始化集合 public class Order { private List<OrderItem> items; // 默认为null } ``` **修正**:添加`<id>`标签并初始化集合字段[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值