[MyBatis]-resultMap结果映射集详解

article/2025/10/7 8:23:41

resultMap结果映射集详解


resultmap是mybatis中最复杂的元素之一,它描述如何从结果集中加载对象,主要作用是定义映射规则、级联的更新、定制类型转化器。

resultmap构成元素

元素子元素作用
constructoridArg 、arg用于配置构造器方法
id将结果集标记为id,以方便全局调用
result配置POJO到数据库列名映射关系
association级联使用代表一对一关系
collection级联使用代表一对多关系
discriminator级联使用鉴别器 根据实际选择实例,可以通过特定条件确定结果集

1.id和result元素

以User类为例:

class User{private int userId;private String name;public User(long userId,String name){this.userId = userId;this.name = name;}
}

id、result是最简单的映射,id为主键映射;result其他基本数据库表字段到实体类属性的映射。resultmap的xml如下:

<resultMap type="User" id="userMap">  <id  property="UserId" column="user_id" javaType="int" jdbcType="int"/>  <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
</resultMap>

id、result语句属性配置细节:

属性描述
property需要映射到JavaBean 的属性名称。
column数据表的列名或者标签别名。
javaType一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。然后,如果你是要映射到一个HashMap,那你需要指定javaType 要达到的目的。
jdbcType数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。
typeHandler使用这个属性可以覆写类型处理器。这项值可以是一个完整的类名,也可以是一个类型别名。

2. CONSTRUCTOR 构造器

在resultMap中,通常使用id、result子元素把Java实体类的属性映射到数据库表的字段上。但是如果在遇到JavaBean没有无参构造函数时,我还需要使用构造器元素实现一个JavaBean的实例化和数据注入。
再以User为例,那么对应的resultmap就需要添加构造器:

<constructor><idArg column="user_id" javaType="long"/><arg column="name" javaType="String"/>
</constructor>

定义java实体类的属性映射到数据库表的字段上。我们也可以使用实体类的构造方法来实现值的映射,这是通过构造方法参数的书写的顺序来进行赋值的。
这样MyBatis就知道需要用这个构造方法构造了。

3.结果集处理方法

1. 使用map储存结果集

一般情况下,所有select语句都可以使用map储存,但是使用map就意味着可读性的下井,所以这不是推荐的方式。

<select id="findUserById" parameterType="long" resultType="map">select user_id ,name form user where user_id=#{userId}
</select>
2. 使用POJO储存结果集(推荐

一般我们都使用POJO储存查询结果。我们可以使用select自动映射,还可以使用select语句中的resultMap属性配置映射集合,不过需要提前定义resultMap。
那我们就可以将之前的select语句修改:

<select id="findUserById" parameterType="long" resultMap="userMap">select user_id ,name form user where user_id=#{userId}
</select>

4.级联

在数据库中包含着一对多、一对一的关系。比如说一个人和他的身份证就是一对一的关系,但是他和他的银行卡就是一对多的关系。我们的生活中存在着很多这样的场景。我们也希望在获取这个人的信息的同时也可以把他的身份证信息一同查出,这样的情况我们就要使用级联。在级联中存在3种对应关系。
- 一对一的关系
- 一对多的关系
- 多对多的关系(这种情况由于比较复杂,我们通常会使用双向一对多的关系来降低复杂度)

1.association 一对一级联

我们继续使用User类,同时为其增加一个Card类,人和他的身份证就行成了一对一的关系。我们再创建一个Card类。

@Data
@Alias("card")
public class Card {private Long id;private Long userId;private String name;private String address;
}

这是需要在User中添加属性Card,这样就形成了一对一的级联。

@Data
@Alias("user")
public class User {
private Long id;
private String username;
private String password;
private String email;
private Card card;

}
这里使用了Lombok的Data注解省去了get和set等方法。使用Alias注解添加其别名。

Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO)。它通过注解实现这一目的。以后会添加其使用方法的文章。

这时需要CardMapper提供findCardByUserId(Long userId)方法,定义其映射器如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.changzhen.mybatis.mapper.CardMapper"><resultMap id="cardMap" type="card"><id property="id" column="id"></id><result property="userId" column="user_id"/><result property="name" column="name"/><result property="address" column="address"/></resultMap><select id="findCardByUserId" parameterType="long" resultMap="cardMap">SELECT id, user_id, name, address FROM card WHERE user_id = #{userId}</select>
</mapper>

有了CardMapper,我们将可以在UserMaper中使用 findCardByUserId 进行级联

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.changzhen.mybatis.mapper.UserMapper">
<select id="getUser" resultMap="userMap" parameterType="long">SELECT id,username,password,email FROM USER WHERE id=#{id}
</select>
<resultMap id="userMap" type="user"><id property="id" column="id"/><result property="password" column="password"/><result property="email" column="email"/><association property="card" column="id" select="com.changzhen.mybatis.mapper.CardMapper.findCardByUserId"/>
</resultMap>

注意在getUser的时候,一定要将数据库的id获取到,开始我以为在传入id的时候就已经把id传入findCardByUserId的userId了,结果怎么都获取不到Card。后来发现原来是根据返回的id进而级联查询。

通过关联处理,其中select元素指定的sql查询,而column则是指定传递给select的参数,是user对象id。当取出User的时候,MyBatis就知道下面的sql取出我们需要的级联信息。

 <association property="card" column="id" select="com.changzhen.mybatis.mapper.CardMapper.findCardByUserId"/>

其中参数是User的值,通过column配置,如果是多个参数则使用逗号隔开。

通过测试
后台运行结果
可以看出执行了两条sql分别查询了User和Card。
返回数据

2.collection 一对多级联

这一对多的级联,一个身份证可以办理多张银行卡。每个用户对应一个身份证,每个身份证对应多个银行卡。所以这两个级联,分别使用association和collection完成。
- 首先,创建BankCard类,为Card增加bankCardList属性

@Alias("bankCard")
@Data
public class BankCard {private Long id;private Long userId;private String bankName;private int type;
}@Data
@Alias("card")
public class Card {private Long id;private Long userId;private String name;private String address;private List<BankCard> bankCards;
}
  • 创建BankCardMapper

    @Mapper
    public interface BankCardMapper {
    public List findCreditCardsByUserId(Long userId);
    }

  • xml最简单的映射器

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.changzhen.mybatis.mapper.CreditCardMapper"><select id="findCreditCardsByUserId" parameterType="long" resultType="creditCard">SELECT id, user_id, bank_name, type FROM bank_card WHERE user_id = #{userId}</select></mapper>
  • 最后在Card映射器中添加collection,一对多级联。
 <mapper namespace="com.changzhen.mybatis.mapper.CardMapper"><resultMap id="cardMap" type="card"><id property="id" column="id"></id><result property="userId" column="user_id"/><result property="name" column="name"/><result property="address" column="address"/><collection property="creditCards" column="id" select="com.changzhen.mybatis.mapper.BankCardMapper.findBankCardsByUserId"/></resultMap><select id="findCardByUserId" parameterType="long" resultMap="cardMap">SELECT id, user_id, name, address FROM card WHERE user_id = #{userId}</select>
</mapper>

-通过测试
后台运行结果

可以看出共执行了三条sql语句。

后台返回数据

3.discriminator 鉴别器级联

鉴别器级联是在不公情况下使用不同的POJO。例如,在本例中我们每个人都有好多银行卡,但是银行卡的种类却不同,比如有借记卡(DebitCard)和信用卡(CreditCard),我们需要按需来创建不同的POJO。这时我们就需要在BankCard中添加types属性进行判断,确定使用哪个POJO。
接下来需要创建DebitCard和CreditCard两个继承BankCard的子类

@Data
public class CreditCard extends BankCard {
/*** 消费额度*/private String creditLine;
}
@Data
public class DebitCard extends BankCard {/*** 存款金额*/private String deposit;
}

通过BankCard中types的属性判断使用哪种银行卡,例如types等于1为借记卡,等于2为信用卡。接下来需要修改bankCard.xml,添加discriminator鉴别器,它相当于java中的switch语句。

bankCard.xml修改为如下:

<mapper namespace="com.changzhen.mybatis.mapper.BankCardMapper"><resultMap id="bankCardMapper" type="bankCard"><id property="id" column="id"/><result property="userId" column="user_id"/><result property="bankName" column="bank_name"/><result property="types" column="types"/><discriminator javaType="int" column="types"><case value="1" resultMap="debitCardMapper"></case><case value="2" resultMap="creditCardMapper"></case></discriminator>
</resultMap><select id="findBankCardsByUserId" parameterType="long" resultMap="bankCardMapper">SELECT id, user_id, bank_name, types FROM bank_card WHERE user_id = #{userId}
</select><resultMap id="debitCardMapper" type="debitCard" extends="bankCardMapper"/><resultMap id="creditCardMapper" type="creditCard" extends="bankCardMapper"/>


其中

    <discriminator javaType="int" column="types"><case value="1" resultMap="debitCardMapper"></case><case value="2" resultMap="creditCardMapper"></case></discriminator>

首先定义了 discriminator ,它对应的column为types,java类型(jdbcType)为int。
case配置了不同的resultMap,如value=1时,引入debitCardMapper,value=2时
,引入creditCardMapper他们都扩展了bankCardMapper。
就像继承关系一样,resultMap也可以继承,加入自己的属性。借记卡添加deposit属性,信用卡添加creditLine属性。
-通过运行测试
返回结果
由图可以看出,返回的数据已经根据types使用了不同的POJO。

后续会讨论性能问题、延迟加载和其他级联。


http://chatgpt.dhexx.cn/article/mN8yuTM6.shtml

相关文章

MyBatis中ResultMap详解

简介&#xff1a; MyBatis的每一个查询映射的返回类型都是ResultMap&#xff0c;只是当我们提供的返回类型属性是resultType的时候&#xff0c;MyBatis会自动的给我们把对应的值赋给resultType所指定对象的属性&#xff0c;而当我们提供的返回类型是resultMap的时候&#xff0…

MyBatis多表查询resultMap设定

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Result Maps 是什么 ResultMap 属性 一、Java数据访问接口 二、XML实现 1、*分步查询 延迟加载 三、Test 1.读入数据 总结 Result Maps 是什么 resultMap 元素是 M…

JS字符串替换指定字符

碰到一个后端数据同步过来的xml文本段&#xff0c;内容里写死的宽度 在1920*1080的分辨率屏幕上&#xff0c;显示有问题&#xff0c;由于数据量太多&#xff0c;前端单独拿数据再处理一遍 RegExp方法&#xff1a; function replaceAll(str, find, replace) {return str.repla…

shell内置字符串替换

shell变量赋值语法&#xff1a; 使用规则解释单引号所见即所得&#xff0c;即输出时会将单引号内的所有內容都原样输出&#xff0c;或者描述为单引号里面看到的是什么就会输出什么&#xff0c;这称为强引用双引号 (默认)输出双引号内的所有内容&#xff1b;如果内容中有命令(…

python字符串替换

str.replace(old, new[, max]) 参数 old – 将被替换的子字符串。 new – 新字符串&#xff0c;用于替换old子字符串。 max – 可选字符串, 替换不超过 max 次 返回值 返回字符串中的 old&#xff08;旧字符串&#xff09; 替换成 new(新字符串)后生成的新字符串&#xff0c;如…

C++ 字符串替换

好久没有更新了&#xff0c;最近有点忙&#xff0c;今天抽点时间给大家水 写一篇关于替换字符串字符的文章 替换函数 string类型的字符串可以用swap函数来替换字符&#xff0c;例如&#xff1a; 冒泡排序 #include <bits/stdc.h> using namespace std;int main () {i…

Python字符串替换方法replace

字符串替换方法replace str1 .replace(old_str&#xff0c;new_str&#xff0c;count)字符串的替换&#xff0c;将str1中的 old_str 替换成new_str old_str:将要被替换的字符串 new_str:新的字符串&#xff0c;替换成的字符串# count:替换的次数,默认是全部替换 返回值:得到一个…

Java字符串替换的方法

java中字符串替换方法主要有三种&#xff0c;分别是replace()、replaceAll()和replaceFirst()&#xff0c;这三种方法可以在三种不同情况应用&#xff0c;下面就由我来具体说明这三种方法的应用情况吧。 replace() replace的参数是char和CharSequence&#xff0c;即可以支持字…

sed 字符串替换

1. sed替换的基本语法为: sed s/原字符串/替换字符串/ 单引号里面,s表示替换,三根斜线中间是替换的样式,特殊字符需要使用反斜线”\”进行转义。 2. 单引号” ‘ ’”是没有办法用反斜线”\”转义的,这时候只要把命令中的单引号改为双引号就行了,格式如下&#xff1a; # 要…

字符串的替换

一、字符串“打劫&#xff01;我有枪”&#xff0c;“枪”是敏感词汇&#xff0c;替换为“*”&#xff0c;并输出打印 二、字符串“北京欢迎你”&#xff0c;替换字符为“郑州欢迎你们”&#xff0c;并输出打印 package work.twelve;/* 一、字符串“打劫&#xff01;我有枪”…

两种字符串替换的方法

1.语法&#xff1a; public String replace(CharSequence target,CharSquence replacement) 注&#xff1a;旧字符换成新字符 示例&#xff1a;店小二在菜单上写下一个馒头一文&#xff0c;“一个”二字容易被他人添上一笔变成“十个”&#xff0c;于是决定把“一”替换成汉字…

7-8 字符串替换 (15 分)

7-8 字符串替换 &#xff08;15 分&#xff09; 本题要求编写程序&#xff0c;将给定字符串中的大写英文字母按以下对应规则替换&#xff1a; 输入格式&#xff1a; 输入在一行中给出一个不超过80个字符、并以回车结束的字符串。 输出格式&#xff1a; 输出在一行中给出替…

全网最全Spring面试题之高级篇整理总结(共25题,附超详细解答)

1、什么是 Spring 框架&#xff1f;Spring 框架有哪些主要模块&#xff1f; Spring 框架是一个为 Java 应用程序的开发提供了综合、广泛的基础性支持的 Java 平台。Spring 帮助开发者解决了开发中基础性的问题&#xff0c;使得开发人员可以专注于应用程序的开发。 Spring 框架本…

spring常见面试题(2023最新)

目录 前言1.spring是什么2.spring的设计核心是什么3.IOC和AOP面试题4.spring的优点和缺点5.spring中bean的作用域6.spring中bean的注入方式7.BeanFactory 和 ApplicationContext有什么区别&#xff1f;8.循环依赖的情况&#xff0c;怎么解决&#xff1f;9.spring中单例Bean是线…

spring面试题及答案

关注公众号&#xff0c;获取更多面试题及答案。 69道Spring面试题 1.什么是spring? Spring是个java企业级应用的开源开发框架。Spring主要用来开发Java应用&#xff0c;但是有些扩展是针对构建J2EE平台的web应用。Spring框架目标是简化Java企业级应用开发&#xff0c;并通过…

【2022版】Spring面试题整理(含答案解析)

1、不同版本的 Spring Framework 有哪些主要功能&#xff1f; 2、什么是 Spring Framework&#xff1f; Spring 是一个开源应用框架&#xff0c;旨在降低应用程序开发的复杂度。它是轻量级、松散耦合的。它具有分层体系结构&#xff0c;允许用户选择组件&#xff0c;同时还为 …

Spring面试题大全含答案共79题(2022最全spring超级葵花宝典)

1. 什么是spring? Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用&#xff0c;但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发&#xff0c;并通过POJO为基础的编程模型促进良好的编程习惯。 2. 使用Spring框架的…

2022最新Spring面试题附完整答案

Spring面试题 一、单选题 1.Spring是年发布的( ) A.2022 B.2004 C.2006 D.2008 2.Spring中的对象的作用域不包括( ) A.session B.servletContext C.singleton D.proptotype 3.在Spring的配置文件中定义了如下代码&#xff0c;说法正确的是( ) <bean id”studen…

Spring面试题(2020最新版)

Java面试总结&#xff08;2021优化版&#xff09;已发布在个人微信公众号【技术人成长之路】&#xff0c;优化版首先修正了读者反馈的部分答案存在的错误&#xff0c;同时根据最新面试总结&#xff0c;删除了低频问题&#xff0c;添加了一些常见面试题&#xff0c;对文章进行了…

Spring面试题(2022版)

序列号内容链接1Java基础知识面试题&#xff08;2022版&#xff09;https://blog.csdn.net/qq_43061290/article/details/1240237972Java集合容器面试题&#xff08;2022版&#xff09;https://blog.csdn.net/qq_43061290/article/details/1240433633Java异常面试题&#xff08…