MyBatis关联映射之ResultMap

1.ResultMap简介

MyBtis关联映射不得不提到ResultMap元素
resultMap元素是 MyBatis 中最重要最强大的元素。
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
默认情况下,MyBatis程序运行时会自动将查询到的数据与需要返回的对象的属性进行匹配赋值(数据包中的列名与对象属性名称完全一致才能匹配成功赋值)。然而在实际开发的过程中,数据表中的列和需要返回对象的属性可能不会完全一致,这种情况下MyBatis不会自动赋值,这个时候需要使用resultMap元素进行结构映射。另外一种情况,查询的是复杂的数据(查询到几个表中数据)映射到一个结果集当中,这种情况使用 resultMap 做自定义结果映射,字段名可以不一致,并且还可以指定要显示的列,比较灵活,应用也广泛(推荐使用)。

2.ResultMap简单映射实例

2.1数据库和实体类准备

create table tbl_user (
    user_id int primary key auto_increment,
    user_name varchar(20) not null,
    user_age int not null
);
insert into tbl_user (user_id,user_name,user_age) values(null,'张三',20),(null,'李四',18);
package com.biem.pojo;

import lombok.*;

/**
 * ClassName: User
 * Package: com.biem.pojo
 * Description:
 *
 * @Create 2023/4/19 9:27
 * @Version 1.0
 */
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    
    
    private Integer id;
    private String name;
    private Integer age;
}

2.2 UserMapper.xml 中的查询

    <select id="findUserById" resultType="user">
        select  user_id, user_name, user_age  from tbl_user where user_id=#{id}
    </select>

结果如下:

DEBUG 04-19 09:53:36,303 ==>  Preparing: select user_id, user_name, user_age from tbl_user where user_id=?  (BaseJdbcLogger.java:159) 
DEBUG 04-19 09:53:36,383 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:159) 
DEBUG 04-19 09:53:36,460 <==      Total: 1 (BaseJdbcLogger.java:159) 
user = null

原因就是列名和属性名不能匹配
列名是:user_id,user_name,user_age
属性名是:id,name,age

2.3 解决方案

2.3.1 方案1:SELECT 语句中设置列别名

    <select id="findUserById" resultType="user">
        select
            user_id as "id",
            user_name as "name",
            user_age as "age"
        from tbl_user
        where user_id=#{id}
    </select>

结果如下

DEBUG 04-19 09:54:51,717 ==>  Preparing: select user_id as "id", user_name as "name", user_age as "age" from tbl_user where user_id=?  (BaseJdbcLogger.java:159) 
DEBUG 04-19 09:54:51,882 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:159) 
DEBUG 04-19 09:54:51,953 <==      Total: 1 (BaseJdbcLogger.java:159) 
user = User(id=1, name=张三, age=20)

2.3.2 方案2:显式配置 ResultMap

    <resultMap id="userResultMap" type="user">
        <id property="id" column="user_id"/>
        <result property="name" column="user_name"/>
        <result property="age" column="user_age"/>
    </resultMap>
    <select id="findUserById" resultMap="userResultMap">
        select
            user_id, user_name, user_age
        from tbl_user
        where user_id=#{id}
    </select>

结果如下:

DEBUG 04-19 14:29:17,036 ==>  Preparing: select user_id, user_name, user_age from tbl_user where user_id=?  (BaseJdbcLogger.java:159) 
DEBUG 04-19 14:29:17,121 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:159) 
DEBUG 04-19 14:29:17,201 <==      Total: 1 (BaseJdbcLogger.java:159) 
user = User(id=1, name=张三, age=20)

3 resultMap 属性和子元素

3.1ResultMap 的属性列表

属性 描述
id 当前命名空间中的一个唯一标识,用于标识一个结果映射。
type 类的完全限定名, 或者一个类型别名
autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

3.2 resultMap 子元素

constructor - 用于在实例化类时,注入结果到构造方法中

  • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
  • arg - 将被注入到构造方法的一个普通结果

id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

result – 注入到字段或 JavaBean 属性的普通结果

association – 一个复杂类型的关联;许多结果将包装成这种类型

  • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

collection – 一个复杂类型的集合

  • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

discriminator – 使用结果值来决定使用哪个 resultMap

case – 基于某些值的结果映射
  • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

3.3 常用子元素

3.3.1 id 和 result

        <id property="id" column="user_id"/>
        <result property="name" column="user_name"/>

这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。

这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。

Id 和 Result 的属性

属性 描述
property 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column 数据库中的列名,或者是列的别名。
javaType 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType JDBC 类型只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
typeHandler 默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。

支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。

BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR ARRAY

3.3.2 constructor 元素

constructor 元素主要用于很少改变或基本不变的包含引用或数据的表,很适合使用不可变类。 构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBean 属性来完成注入,但有一些人更青睐于通过构造方法进行注入。

3.3.3 关联(association)

关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。

关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:

嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。

首先,先让我们来看看这个元素的属性。你将会发现,和普通的结果映射相比,它只在 select 和 resultMap 属性上有所不同。

属性 描述
property 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。
typeHandler 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。
3.3.3.1 嵌套 Select 查询
属性 描述
column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column=“{prop1=col1,prop2=col2}” 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
select 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column=“{prop1=col1,prop2=col2}” 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
fetchType 可选的。有效值为 lazy 和 eager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。
3.3.3.2 嵌套结果映射
属性 描述
resultMap 结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的 ResultSet 有部分数据是重复的。 为了将结果集正确地映射到嵌套的对象树中, MyBatis 允许你“串联”结果映射,以便解决嵌套结果集的问题。使用嵌套结果映射的一个例子在表格以后。
columnPrefix 当连接多个表时,你可能会不得不使用列别名来避免在 ResultSet 中产生重复的列名。指定 columnPrefix 列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。 详细说明请参考后面的例子。
notNullColumn 默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。
autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。注意,本属性对外部的结果映射无效,所以不能搭配 select 或 resultMap 元素使用。默认值:未设置(unset)。

例子:MyBatis关联映射查询

猜你喜欢

转载自blog.csdn.net/yandao/article/details/130213112