2.3 结果映射 (ResultMap)


文档摘要

2.3 结果映射 (ResultMap) MyBatis 映射器深入:2.3 结果映射 (ResultMap) 详解 2.3.1 ResultMap 的基本概念 ResultMap 本质上是一个 XML 元素,它定义了从数据库结果集到 Java 对象的映射规则。 它可以处理简单的列到属性的直接映射,也可以处理更复杂的情况,例如: 不同的列名和属性名: 当数据库列名与 Java 对象的属性名不一致时,ResultMap 允许你指定映射关系。 类型转换: ResultMap 可以处理数据库列的数据类型与 Java 对象属性的数据类型不同的情况,并进行必要的类型转换。 嵌套结果: ResultMap 允许你将一个 Java 对象嵌套到另一个 Java 对象中,从而表示数据库表之间的关联关系。

2.3 结果映射 (ResultMap)

MyBatis 映射器深入:2.3 结果映射 (ResultMap) 详解

2.3.1 ResultMap 的基本概念

ResultMap 本质上是一个 XML 元素,它定义了从数据库结果集到 Java 对象的映射规则。 它可以处理简单的列到属性的直接映射,也可以处理更复杂的情况,例如:

  • 不同的列名和属性名: 当数据库列名与 Java 对象的属性名不一致时,ResultMap 允许你指定映射关系。

  • 类型转换: ResultMap 可以处理数据库列的数据类型与 Java 对象属性的数据类型不同的情况,并进行必要的类型转换。

  • 嵌套结果: ResultMap 允许你将一个 Java 对象嵌套到另一个 Java 对象中,从而表示数据库表之间的关联关系。

  • 集合: ResultMap 允许你将数据库查询结果映射到 Java 集合,例如 List 或 Set。

  • 鉴别器: ResultMap 允许你根据数据库列的值选择不同的映射规则,从而处理继承或多态的情况。

ResultMap 的基本结构:

<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> </resultMap>
  • id: ResultMap 的唯一标识符,用于在 SQL 语句中引用。

  • type: Java 对象的完全限定类名,表示要映射到的目标类。

  • id: 映射主键列。 property 属性指定 Java 对象的属性名,column 属性指定数据库列名。

  • result: 映射普通列。 property 属性指定 Java 对象的属性名,column 属性指定数据库列名。

2.3.2 ResultMap 的属性详解

ResultMap 元素及其子元素提供了丰富的属性,用于控制映射行为。

1. resultMap 元素:

  • id (required): ResultMap 的唯一标识符。

  • type (required): Java 对象的完全限定类名。

  • extends (optional): 继承另一个 ResultMap,可以继承其映射规则。

  • autoMapping (optional): 是否启用自动映射。 默认值为 false。 如果设置为 true,MyBatis 会尝试自动将查询结果集中的列映射到 Java 对象的属性,而无需显式定义 result 元素。

  • resultOrdered (optional): 仅在嵌套结果映射中有效。 如果设置为 true,则结果集将按照 ResultMap 中定义的顺序进行排序。

2. id 元素:

  • property (required): Java 对象的属性名。

  • column (required): 数据库列名。

  • javaType (optional): Java 属性的类型。 通常 MyBatis 可以自动推断,但有时需要显式指定。

  • jdbcType (optional): 数据库列的 JDBC 类型。 通常 MyBatis 可以自动推断,但有时需要显式指定。

  • typeHandler (optional): 用于处理类型转换的 TypeHandler。

3. result 元素:

  • property (required): Java 对象的属性名。

  • column (required): 数据库列名。

  • javaType (optional): Java 属性的类型。 通常 MyBatis 可以自动推断,但有时需要显式指定。

  • jdbcType (optional): 数据库列的 JDBC 类型。 通常 MyBatis 可以自动推断,但有时需要显式指定。

  • typeHandler (optional): 用于处理类型转换的 TypeHandler。

4. association 元素:

用于处理一对一关联关系。

  • property (required): Java 对象的属性名,该属性是一个 Java 对象。

  • javaType (required): 关联的 Java 对象的完全限定类名。

  • column (optional): 用于传递给嵌套查询的列名。

  • select (optional): 用于获取关联对象的 SELECT 语句的 ID。

  • resultMap (optional): 用于映射关联对象的 ResultMap 的 ID。

  • fetchType (optional): 指定是否延迟加载关联对象。 可选值为 lazy (延迟加载) 和 eager (立即加载)。 默认为 MyBatis 的全局配置。

5. collection 元素:

用于处理一对多关联关系。

  • property (required): Java 对象的属性名,该属性是一个 Java 集合。

  • javaType (required): Java 集合的类型,例如 java.util.Listjava.util.Set

  • ofType (required): 集合中元素的类型。

  • column (optional): 用于传递给嵌套查询的列名。

  • select (optional): 用于获取集合元素的 SELECT 语句的 ID。

  • resultMap (optional): 用于映射集合元素的 ResultMap 的 ID。

  • fetchType (optional): 指定是否延迟加载集合。 可选值为 lazy (延迟加载) 和 eager (立即加载)。 默认为 MyBatis 的全局配置。

6. discriminator 元素:

用于根据数据库列的值选择不同的映射规则。

  • column (required): 用于鉴别的数据库列名。

  • javaType (optional): 鉴别列的 Java 类型。

每个 discriminator 元素包含一个或多个 case 元素,每个 case 元素定义一个特定的映射规则。

  • value (required): 鉴别列的值。

  • resultMap (required): 用于映射该值的 ResultMap 的 ID。

2.3.3 代码实践与详解

1. 简单映射:

假设我们有一个 users 表,包含 user_id, username, 和 email 列。 我们想将这些数据映射到 com.example.User 对象。

package com.example; public class User { private Long id; private String username; private String email; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }

对应的 ResultMap 如下:

<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> </resultMap>

SQL 映射:

<select id="getUserById" parameterType="long" resultMap="userResultMap"> SELECT user_id, username, email FROM users WHERE user_id = #{id} </select>

2. 自动映射:

如果数据库列名与 Java 对象的属性名完全一致,你可以使用 autoMapping="true" 启用自动映射。

<resultMap id="userResultMap" type="com.example.User" autoMapping="true"> <id property="id" column="id"/> </resultMap>

在这种情况下,MyBatis 会自动将 id, username, 和 email 列映射到 User 对象的相应属性,无需显式定义 result 元素。 注意: id 元素仍然需要显式定义,因为它是主键。

3. 类型转换:

假设 users 表中的 created_at 列存储的是时间戳 (Long),而 User 对象中的 createdAt 属性是 java.util.Date 类型。

package com.example; import java.util.Date; public class User { private Long id; private String username; private String email; private Date createdAt; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } }
<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> <result property="createdAt" column="created_at" javaType="java.util.Date" jdbcType="BIGINT"/> </resultMap>

这里,我们使用 javaTypejdbcType 属性来指定类型转换。 MyBatis 会自动将 created_at 列的时间戳转换为 java.util.Date 对象。 你也可以使用 typeHandler 属性来指定自定义的类型处理器。

4. 一对一关联 (association):

假设我们有一个 users 表和一个 addresses 表,一个用户对应一个地址。

package com.example; public class User { private Long id; private String username; private String email; private Address address; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
package com.example; public class Address { private Long id; private String street; private String city; private String zipCode; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } }
<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> <association property="address" javaType="com.example.Address" column="address_id" select="getAddressById"/> </resultMap> <select id="getUserById" parameterType="long" resultMap="userResultMap"> SELECT user_id, username, email, address_id FROM users WHERE user_id = #{id} </select> <select id="getAddressById" parameterType="long" resultType="com.example.Address"> SELECT id, street, city, zip_code FROM addresses WHERE id = #{id} </select>

或者使用 join 查询:

<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> <association property="address" javaType="com.example.Address"> <id property="id" column="address_id"/> <result property="street" column="street"/> <result property="city" column="city"/> <result property="zipCode" column="zip_code"/> </association> </resultMap> <select id="getUserById" parameterType="long" resultMap="userResultMap"> SELECT u.user_id, u.username, u.email, a.id as address_id, a.street, a.city, a.zip_code FROM users u LEFT JOIN addresses a ON u.address_id = a.id WHERE u.user_id = #{id} </select>

5. 一对多关联 (collection):

假设我们有一个 users 表和一个 posts 表,一个用户可以拥有多个帖子。

package com.example; import java.util.List; public class User { private Long id; private String username; private String email; private List<Post> posts; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List<Post> getPosts() { return posts; } public void setPosts(List<Post> posts) { this.posts = posts; } }
package com.example; public class Post { private Long id; private String title; private String content; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> <collection property="posts" javaType="java.util.List" ofType="com.example.Post" column="user_id" select="getPostsByUserId"/> </resultMap> <select id="getUserById" parameterType="long" resultMap="userResultMap"> SELECT user_id, username, email FROM users WHERE user_id = #{id} </select> <select id="getPostsByUserId" parameterType="long" resultType="com.example.Post"> SELECT id, title, content FROM posts WHERE user_id = #{user_id} </select>

或者使用 join 查询:

<resultMap id="userResultMap" type="com.example.User"> <id property="id" column="user_id"/> <result property="username" column="username"/> <result property="email" column="email"/> <collection property="posts" javaType="java.util.List" ofType="com.example.Post"> <id property="id" column="post_id"/> <result property="title" column="title"/> <result property="content" column="content"/> </collection> </resultMap> <select id="getUserById" parameterType="long" resultMap="userResultMap"> SELECT u.user_id, u.username, u.email, p.id as post_id, p.title, p.content FROM users u LEFT JOIN posts p ON u.user_id = p.user_id WHERE u.user_id = #{id} </select>

6. 鉴别器 (discriminator):

假设我们有一个 vehicles 表,包含 id, type, 和 color 列。 type 列的值可以是 cartruck。 我们想根据 type 列的值将数据映射到不同的 Java 对象 (CarTruck)。

package com.example; public abstract class Vehicle { private Long id; private String color; // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
package com.example; public class Car extends Vehicle { private int numberOfDoors; // Getters and setters public int getNumberOfDoors() { return numberOfDoors; } public void setNumberOfDoors(int numberOfDoors) { this.numberOfDoors = numberOfDoors; } }
package com.example; public class Truck extends Vehicle { private int loadCapacity; // Getters and setters public int getLoadCapacity() { return loadCapacity; } public void setLoadCapacity(int loadCapacity) { this.loadCapacity = loadCapacity; } }
<resultMap id="vehicleResultMap" type="com.example.Vehicle" > <id property="id" column="id"/> <result property="color" column="color"/> <discriminator column="type" javaType="string"> <case value="car" resultMap="carResultMap"/> <case value="truck" resultMap="truckResultMap"/> </discriminator> </resultMap> <resultMap id="carResultMap" type="com.example.Car" extends="vehicleResultMap"> <result property="numberOfDoors" column="number_of_doors"/> </resultMap> <resultMap id="truckResultMap" type="com.example.Truck" extends="vehicleResultMap"> <result property="loadCapacity" column="load_capacity"/> </resultMap> <select id="getVehicleById" parameterType="long" resultMap="vehicleResultMap"> SELECT id, type, color, number_of_doors, load_capacity FROM vehicles WHERE id = #{id} </select>

2.3.4 ResultMap 的高级特性

  • 继承 (extends): ResultMap 可以继承另一个 ResultMap 的映射规则,从而避免重复定义。

  • 自动映射 (autoMapping): 启用自动映射可以简化 ResultMap 的定义,但需要保证数据库列名与 Java 对象的属性名一致。

  • TypeHandler: 允许你自定义类型转换逻辑。

  • 延迟加载 (lazy loading): 可以提高性能,只在需要时才加载关联对象或集合。

2.3.5 ResultMap 的优势

  • 灵活性: ResultMap 允许你灵活地控制数据如何映射到 Java 对象。

  • 解耦: ResultMap 将数据库结构与 Java 对象结构解耦,使得你可以更容易地修改数据库结构而无需修改 Java 代码。

  • 性能优化: ResultMap 可以通过延迟加载等技术来提高性能。

2.3.6 总结

ResultMap 是 MyBatis 中一个非常重要的特性,它提供了强大的数据映射功能,可以处理各种复杂的场景。 掌握 ResultMap 的使用,可以让你更加灵活和高效地进行数据库访问。 通过合理地使用 ResultMap,可以提高代码的可维护性、可扩展性和性能。 希望本文能够帮助你深入理解 MyBatis 的 ResultMap,并在实际项目中灵活运用。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U