元素类型为 "resultMap" 的内容顺序

本文分享了一位初学者在使用MyBatis框架时的教训:在resultMap中,association和collection元素的顺序至关重要。错误的顺序会导致运行时异常,正确的顺序则能确保数据的正确映射。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刚刚开始学mybatis,在resultMap里同时写一对多和多对一时发现,原来resultMap下的内容是有顺序的
在这里插入图片描述
刚开始把collection写到了association前面结果报错,换了顺序之后就对了

<think>我们参考用户提供的引用内容,特别是引用[4]中的示例,来构建一个包含List<String>属性的resultMap。 实体类User有一个List<String>类型的属性otherName,我们需要在resultMap中映射这个属性。 在MyBatis中,对于集合类型(如List)我们使用<collection>标签进行映射。其中: - property属性指定实体类中集合属性的名称(如otherName) - ofType属性指定集合中元素的类型(对于List<String>,ofType="java.lang.String") - 在<collection>内部,使用<result>标签来映射数据库列到集合元素。注意,<result>标签没有property属性,因为集合元素是基本类型(String)而不是对象。 同时,引用[4]提到,<collection>标签通常放在resultMap的最后,以避免与其他字段映射冲突。 因此,我们可以这样写resultMap: 假设我们有一个User表,有id, name,以及另一个表(或同一表的多个行)用于存储otherName(例如,一个用户可能有多个其他名字,存储在另一张表中,通过用户id关联)。但在实际查询中,我们可能需要通过连接查询将多个otherName作为多行返回,然后MyBatis会自动将多个行中的oName值收集到List中。 但是,如果我们的查询只返回一行数据,而这一行中只有一个oName,那么集合中只有一个元素。如果查询返回多行(比如通过一对多的连接),那么每一行都会有一个oName,这些值会被收集到集合中。 下面是一个具体的resultMap配置示例: ```xml <resultMap id="UserMap" type="com.example.User"> <!-- 映射基本属性 --> <id column="id" property="id" /> <result column="name" property="name" /> <!-- 映射List<String>类型的otherName属性 --> <collection property="otherName" ofType="java.lang.String"> <result column="oName"/> </collection> </resultMap> ``` 对应的SQL查询语句可能是这样的(假设otherName存储在user_other_name表中,通过user_id关联): ```xml <select id="selectUser" resultMap="UserMap"> SELECT u.id, u.name, o.name as oName FROM user u LEFT JOIN user_other_name o ON u.id = o.user_id WHERE u.id = #{id} </select> ``` 注意:上面的查询使用了LEFT JOIN,这样即使用户没有otherName,也会返回用户的基本信息,otherName集合会是空(而不是null)。如果希望返回多个用户,那么需要将查询条件去掉,然后MyBatis会自动将同一个用户的多条记录分组,将多个oName值收集到同一个用户的otherName集合中。但是,如果查询多个用户,那么需要配合<collection>的分组功能(MyBatis会自动根据id等唯一标识分组,因此需要在resultMap中配置<id>标签,这样MyBatis才能正确分组)。 另外,引用[2]和[3]提到了当整个结果集就是一个List<String>时(即没有其他属性,整个查询结果就是字符串列表)的情况,但用户的问题是关于实体属性是List<String>,而不是整个结果是一个List。所以这里我们采用引用[4]的方式。 总结:在resultMap中,使用<collection>来映射List<String>属性,指定ofType为java.lang.String,然后在<collection>内部使用<result>映射列。 注意:如果实体类中的List<String>属性对应的数据库列在同一个表中有多列(例如列名为other_name1, other_name2),那么这种设计并不符合第一范式,通常应该拆分到另一个表。但是,如果确实存在这种情况,我们可以使用多个<result>标签,但这样不能动态扩展。因此,更推荐使用关联表的方式。 如果多个值存储在同一行的多个列中,那么我们可以使用以下方式(但这种情况较少见,且列数固定): ```xml <resultMap id="UserMap" type="com.example.User"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="otherName" ofType="java.lang.String"> <result column="other_name1"/> <result column="other_name2"/> <!-- 这样,otherName集合中会包含两个元素:other_name1列的值和other_name2列的值 --> </collection> </resultMap> ``` 但是,这种方式要求查询返回的列中包含了other_name1和other_name2,而且集合中的元素顺序就是<result>标签的顺序。注意,这种方式只适用于固定数量的列,且每列对应集合中的一个元素。 然而,更常见的做法是使用一对多的关系,将多个值放在多行中,通过关联查询得到多行数据,然后MyBatis自动收集到集合中。 因此,我们推荐使用第一种方式(通过连接查询,返回多行,每行一个值)。 下面,我们根据引用内容,给出一个完整的示例: 实体类: ```java public class User { private String id; private String name; private List<String> otherName; // 其他名字列表 // 省略getter和setter } ``` XML配置: ```xml <resultMap id="UserMap" type="com.example.User"> <id column="id" property="id"/> <!-- 使用id标签标识主键,有助于MyBatis进行结果集分组 --> <result column="name" property="name"/> <!-- 映射List<String>的otherName --> <collection property="otherName" ofType="java.lang.String"> <result column="oName"/> </collection> </resultMap> <select id="selectUserById" resultMap="UserMap"> SELECT u.id, u.name, o.name AS oName FROM user u LEFT JOIN user_other_name o ON u.id = o.user_id WHERE u.id = #{id} </select> ``` 这样,当查询一个用户时,可能会返回多行(每个otherName一行),MyBatis会根据id将多行分组,将oName的值收集到List中,赋值给otherName属性。 注意:如果同一个用户有多个oName,那么查询返回多行,每行包含相同的用户id和name,不同的oName。MyBatis会通过id列(标记为<id>)来识别属于同一个用户的记录,然后将这些记录的oName收集到同一个List中。 如果用户没有otherName,那么由于使用了LEFT JOIN,oName列会是NULL,但MyBatis会创建一个空的List(注意不是null)?实际上,MyBatis会创建一个集合,里面包含一个null元素。为了避免这种情况,可以在<collection>标签上添加`notNullColumn="oName"`属性(但MyBatis 3.4.6以上版本支持)或者使用<association>的fetchType来避免,但更常见的做法是在业务逻辑中处理空值,或者使用条件构造resultMap(比较复杂)。另一种方法是,在SQL查询中过滤掉oName为NULL的行(但这样会使得没有otherName的用户不会出现在结果中,所以不行)。因此,如果使用LEFT JOIN,那么当没有关联记录时,oName为NULL,MyBatis会向集合中添加一个null。为了避免,可以在实体类的setter方法中过滤掉null,或者使用如下方式: 在<collection>标签中,添加一个条件,当列不为空时才添加: 但是,MyBatis并没有直接提供这样的功能。所以,我们可以在业务逻辑中处理,或者在SQL中使用COALESCE函数并设置一个不可能的值,然后在Java中过滤掉这个值,但这并不优雅。 另一种做法是,在映射后,在Java中移除null: ```java public class User { ... public void setOtherName(List<String> otherName) { // 移除null if (otherName != null) { otherName.removeIf(Objects::isNull); } this.otherName = otherName; } } ``` 或者,在查询中避免返回NULL行(但这样会丢失用户记录,所以不行)。因此,我们只能接受集合中包含NULL,或者在业务逻辑中处理。 但是,如果使用内连接(INNER JOIN),那么没有otherName的用户将不会被查询出来,所以需要根据业务需求选择连接方式。 综上所述,我们主要关注resultMap的写法,即使用<collection>映射List<String>属性。 因此,回答用户的问题:在MyBatis的XML中,为包含List<String>属性的实体类编写resultMap时,使用<collection>标签,指定property为属性名,ofType为java.lang.String,并在其中使用<result>映射列名。 注意:<collection>标签需要放在resultMap定义的末尾,以避免映射冲突[^4]。</think>在 MyBatis XML 中为包含 `List<String>` 属性的实体类编写 `resultMap` 时,需要使用 `<collection>` 标签进行映射。以下是具体实现方法: ### 实体类示例 ```java public class User { private String id; private String name; private List<String> otherName; // List<String> 类型的属性 // Getter 和 Setter } ``` ### XML resultMap 配置 ```xml <resultMap id="UserMap" type="com.example.User"> <!-- 映射基本属性 --> <id column="id" property="id"/> <result column="name" property="name"/> <!-- 映射 List<String> 属性 --> <collection property="otherName" ofType="java.lang.String"> <result column="oName"/> <!-- 只需指定列名 --> </collection> </resultMap> ``` ### SQL 查询示例 ```xml <select id="selectUser" resultMap="UserMap"> SELECT u.id, u.name, o.name AS oName -- 对应集合元素的列 FROM user u LEFT JOIN user_other_names o ON u.id = o.user_id WHERE u.id = #{id} </select> ``` ### 关键点说明 1. **`<collection>` 标签**: - `property`:实体类中的集合属性名(如 `otherName`) - `ofType`:集合元素类型(`java.lang.String`) - 不需要指定 `javaType`,因为 MyBatis 能自动识别 `List` 类型[^1][^4] 2. **`<result>` 子标签**: - 只需指定 `column`(数据库列名) - 不需要 `property` 属性,因为元素是简单类型[^4] 3. **SQL 要求**: - 查询需要返回多个关联行(每行一个字符串元素) - 使用 `LEFT JOIN` 确保即使没有关联数据也能返回主记录 - MyBatis 会根据主对象 ID 自动分组集合元素[^1][^4] 4. **最佳实践**: - 将 `<collection>` 放在 `resultMap` 末尾[^4] - 确保主键(`<id>`)正确映射,这是结果集分组的关键 - 关联表设计建议: ```sql CREATE TABLE user_other_names ( user_id VARCHAR(32), name VARCHAR(50), FOREIGN KEY (user_id) REFERENCES user(id) ) ``` > **注意**:当查询返回多行时,MyBatis 会基于主对象 ID 自动将多个 `oName` 值收集到 `otherName` 列表中。例如用户有 "昵称A" 和 "昵称B",查询返回两行数据,MyBatis 会自动创建包含两个元素的 List[^1][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值