target
掌握resultMap元素的结构
掌握使用resultMap处理类表字段不一致的问题
掌握一对一的级联查询
掌握一对多的级联查询
掌握多对多的级联查询
元素表示结果映射集,是 MyBatis 中最重要也是最强大的元素,主要用来定义映射规则、级联的更新以及定义类型转化器等。
1. 元素的结构
元素包含了一些子元素,结构如下:
-
元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
- 子元素
用于配置构造方法(当 POJO 未定义无参数的构造方法时使用)。
- 子元素
用于表示哪个列是主键。
- 子元素
用于表示POJO和数据表普通列的映射关系。
- 子元素
、
和
用在级联的情况下。
2. 处理类表字段名不一致
有的开发者喜欢使用 POJO 的方式存储结果集,一方面可以使用自动映射,例如使用 resultType 属性,但有时候需要更为复杂的映射或级联或者类表字段名不一致,这时候就需要使用 resultMap 属性配置映射集合。具体步骤如下:
1)创建 POJO 类
MapUser 类的代码如下:
public class MapUser {
private Integer m_uid;
private String m_uname;
private String m_usex;
// 此处省略setter和getter方法
}
2)配置 元素
在 SQL 映射文件 UserMapper.xml 中配置 元素,其属性 type 引用 POJO 类。具体配置如下:
3)配置元素
在 SQL 映射文件 UserMapper.xml 中配置 元素,其属性 resultMap 引用了
元素的 id。具体配置如下:
select * from user
4)添加接口方法
在 com.dao.UserDao 接口中添加以下接口方法:
public List selectResultMap();
5)调用接口方法
在 com.controller 包的 UserController 类中调用接口方法,具体代码如下:
// 使用resultMap映射结果集
List listResultMap = userDao.selectResultMap();
for (MapUser myUser : listResultMap) {
System.out.println(myUser);
}
3. 级联查询
级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。
级联的优点是获取关联数据十分方便,但是级联过多会增加数据库系统的复杂度,同时降低系统的性能。
在实际开发中要根据实际情况判断是否需要使用级联。更新和删除的级联关系很简单,由数据库内在机制即可完成。本节只讲述级联查询的相关实现。
如果表 A 中有一个外键引用了表 B 的主键,A 表就是子表,B 表就是父表。当查询表 A 的数据时,通过表 A 的外键将表 B 的相关记录返回,这就是级联查询。例如,当查询一个人的信息时,同时根据外键(身份证号)将他的身份证信息返回。
1)一对一关联查询
一对一级联关系在现实生活中是十分常见的,例如一个大学生只有一张一卡通,一张一卡通只属于一个学生。再如人与身份证的关系也是一对一的级联关系。
MyBatis 如何处理一对一级联查询呢?在 MyBatis 中,通过 元素的子元素
处理这种一对一级联关系。
在 元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
下面以个人与身份证之间的关系为例讲解一对一级联查询的处理过程,读者只需参考该实例即可学会一对一级联查询的 MyBatis 实现。
① 创建数据表
CREATE TABLE idcard(
id int NOT NULL AUTO_INCREMENT,
code varchar(30) DEFAULT NULL,
PRIMARY KEY (id)
);
CREATE TABLE person(
id int NOT NULL,
name varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
age int(11) DEFAULT NULL,
idcard_id INT(11) DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT idcard_id FOREIGN KEY (idcard_id) REFERENCES idcard(id)
);
② 创建持久化类
在com.mybatis.pojo 包中创建数据表对应的持久化类 Idcard 和 Person。
Idcard 的代码如下:
public class Idcard {
private Integer id;
private String code;
// 省略setter和getter方法
}
Person 的代码如下:
public class Person {
private Integer id;
private String name;
private Integer age;
// 个人身份证关联
private Idcard card;
// 省略setter和getter方法
}
③ 创建配置文件
首先,在 MyBatis 的核心配置文件 mybatis-config.xml中打开延迟加载开关,加载映射文件,代码如下:
然后,在 com.mybatis.mapper 中创建两张表对应的映射文件 IdCardMapper.xml 和 PersonMapper.xml。
IdCardMapper.xml 的代码如下:
select * from idcard where id=#{id}
PersonMapper.xml 的代码如下:
select * from person where id=#{id}
④ 创建数据操作接口
IdCardMapper 的代码如下:
public interface IdCardMapper {
public Idcard selectCodeById(Integer i);
}
PersonMapper 的代码如下:
public interface PersonMapper {
public Person selectPersonById (Integer id);
}
⑤ 调用接口方法及测试
package com.mybatis.test;
public class MyBatisTest {
SqlSession sqlSession = null;
@BeforeEach
public void before() throws IOException {
// 读取配置文件 mybatis-config.xml
InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
// 通过 SqlSessionFactory 创建 SqlSession
sqlSession = ssf.openSession();
}
@Test
public void selectPersonById(){
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person p = personMapper.selectPersonById(1);
System.out.println(p);
}
@AfterEach
public void after() {
// 提交事务
sqlSession.commit();
// 关闭 SqlSession
sqlSession.close();
}
}
需要注意:
由于开启了懒加载(需要在mybatis配置文件中开启),只有在调用的时候才会加载相应的SQL语句。
比如只查询学生的名字:
@Test
public void selectPersonById(){
PersonMapper personMapper = session.getMapper(PersonMapper.class);
Person p = personMapper.selectPersonById1(1);
System.out.println(p.getName());
}
只会调用一条SQL语句:

如果需要查询学生证信息的时候,才会调用学生证相关的SQL:
@Test
public void selectPersonById(){
PersonMapper personMapper = session.getMapper(PersonMapper.class);
Person p = personMapper.selectPersonById1(1);
System.out.println(p);
}

其他做法
此外,还有第二种方式:
PersonMapper.xml可以写成:
select p.*,ic.code
from person p, idcard ic
where p.idcard_id=ic.id and p.id=#{id}
此种方式只会执行1条SQL语句。
此外,还有第三种方式:
创建一个TO类,用来封装查询出来的字段:
package com.mybatis.pojo;
public class PersonTO {
private Integer id;
private String name;
private Integer age;
private String code;
}
PersonMapper.xml文件为:
select p.*,ic.code
from person p, idcard ic
where p.idcard_id = ic.id and p.id=#{id}
接口也要做相应的改变:
package com.mybatis.mapper;
public interface PersonMapper {
public PersonTO selectPersonById(Integer id);
}
2)一对多关联映射
MyBatis 又是如何处理一对多级联查询的呢?在实际生活中一对多级联关系有许多,例如一个用户可以有多个订单,而一个订单只属于一个用户。
下面以用户和订单之间的关系为例讲解一对多级联查询(实现“根据 uid 查询用户及其关联的订单信息”的功能)的处理过程,读者只需参考该实例即可学会一对多级联查询的 MyBatis 实现。
① 创建数据表
本实例需要两张数据表,一张是用户表 user,一张是订单表 order,这两张表具有一对多的级联关系。
CREATE TABLE `t_order`(
id int NOT NULL AUTO_INCREMENT,
order_message varchar(20) DEFAULT NULL,
user_id int DEFAULT NULL,
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user`(
uid int(11) NOT NULL AUTO_INCREMENT,
uname varchar(30) DEFAULT NULL,
usex varchar(20) DEFAULT NULL,
PRIMARY KEY (uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
② 创建持久化类
在应用的 com.mybatis.po 包中创建数据表 t_order 对应的持久化类 Order,user 表对应的持久化类 User 。
User 类的代码如下:
public class User {
private int id;
private String name;
private String sex;
// 一对多级联查询,用户关联的订单
private List ordersList;
}
Orders 类的代码如下:
public class Order {
private Integer id;
private String orderMessage;
}
③ 创建配置文件
首先,在 MyBatis 的核心配置文件 mybatis-config.xml中打开延迟加载开关,加载映射文件,代码如下:
在应用的 com.mybatis.mapper 中创建两张表对应的映射文件 UserMapper.xml 和 OrderMapper.xml。
UserMapper.xml 的配置代码如下:
select * from user where uid = #{id}
OrderMapper.xml 的配置代码如下:
select id,order_message orderMessage,user_id from t_order where user_id=#{id}
④ 创建数据操作接口
在应用的 com.dao 包中创建第 3 步中映射文件对应的数据操作接口 OrderMapper 和 UserMapper。
UserMapper 的代码如下:
public interface UserMapper {
public User selectUserOrdersById(Integer id);
}
OrderMapper 的代码如下:
public interface OrderMapper {
public List selectOrdersById(Integer id);
}
⑤ 调用接口方法及测试
package com.mybatis.test;
public class MyBatisTest {
SqlSession sqlSession = null;
@BeforeEach
public void before() throws IOException {
// 读取配置文件 mybatis-config.xml
InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
// 通过 SqlSessionFactory 创建 SqlSession
sqlSession = ssf.openSession();
}
@Test
public void selectUserOrdersById(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User u = userMapper.selectUserOrdersById(1);
System.out.println(u);
}
@AfterEach
public void after() {
// 提交事务
sqlSession.commit();
// 关闭 SqlSession
sqlSession.close();
}
}
扩展
和标签中的column,是负责给property属性赋值的。如果有标签,则会给把值作为参数传递给select,如果给select传递多个值呢?
column= {key1 = column1 ,key2 = column2}
其他做法
此外,还有第二种方式:
UserMapper.xml可以写成:
select u.*,o.id, o.order_message from user u, t_order o
where u.uid = o.user_id and u.uid=#{id}
此种方式只会执行1条SQL语句。
此外,还有第三种方式:
创建一个TO类,用来封装查询出来的字段:
package com.mybatis.pojo;
public class UserTO {
private Integer id;
private String name;
private String sex;
private Integer orderId;
private String orderMessage;
}
UserMapper.java:
package com.mybatis.mapper;
public interface UserMapper {
public List selectUserOrdersById(Integer uid);
}
UserMapper.xml:
select u.uid id,u.uname name,u.usex sex, o.id orderId, o.order_message orderMessage
from user u, t_order o where u.uid = o.user_id and u.uid=#{id}
此种方式可以避免使用resultMap。
3)多对多关联映射
其实,MyBatis 没有实现多对多级联,这是因为多对多级联可以通过两个一对多级联进行替换。
例如,一个订单可以有多种商品,一种商品可以对应多个订单,订单与商品就是多对多的级联关系,使用一个中间表(订单记录表)就可以将多对多级联转换成两个一对多的关系。
下面以订单和商品为例讲解多对多级联查询。
① 创建数据表
CREATE TABLE `product`(
`id` int NOT NULL,
`name` varchar(50) DEFAULT NULL,
`price` double DEFAULT NULL,
PRIMARY KEY (`id`)
)
CREATE TABLE `t_order`(
id int NOT NULL AUTO_INCREMENT,
order_message varchar(20) DEFAULT NULL,
user_id int DEFAULT NULL,
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `orders_detail`(
`id` int NOT NULL AUTO_INCREMENT,
`orders_id` int DEFAULT NULL,
`product_id` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `orders_id` (`orders_id`),
KEY `product_id` (`product_id`),
CONSTRAINT `orders_id` FOREIGN KEY (`orders_id`) REFERENCES `t_order` (`id`),
CONSTRAINT `product_id` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`)
);
② 创建持久化类
com.mybatis.po 包中创建数据表 product 对应的持久化类 Product,而中间表 orders_detail 不需要持久化类,订单表 t_order 对应的持久化类 Order 。
Product 的代码如下:
public class Product {
private Integer id;
private String name;
private Double price;
// 多对多中的一个一对多
private List orders;
}
Order 的代码如下:
public class Order {
private Integer id;
private String orderMessage;
private List products;
}
③ 创建映射文件
ProductMapper.xml:
SELECT o.id,o.order_message,p.`name`,p.price
FROM t_order o ,product p,orders_detail op
WHERE o.id = op.orders_id AND p.id = op.product_id
AND p.id = #{id}
OrderMapper.xml:
SELECT o.id,o.order_message,p.`name`,p.price
FROM t_order o ,product p,orders_detail op
WHERE o.id = op.orders_id AND p.id = op.product_id
AND o.id = #{id}
Mybatis-config.xml 中加入mapper:
④ 添加数据操作接口方法
ProductMapper.java
public interface ProductMapper {
public List selectOrdersByProduct(Integer id);
}
OrderMapper.java
public interface OrderMapper {
public List selectProductsByOrder(Integer id);
}
⑤ 调用接口方法及测试
public class MyBatisTest {
SqlSession session = null;
@Before
public void init() {
// 读取配置文件 mybatis-config.xml
InputStream config = null;
try {
config = Resources.getResourceAsStream("mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
// 通过 SqlSessionFactory 创建 SqlSession
session = ssf.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test//根据订单号查询商品
public void selectProductsByOrder(){
OrderMapper orderMapper = session.getMapper(OrderMapper.class);
List products = orderMapper.selectProductsByOrder(1001);
System.out.println(products);
}
@Test//根据商品号查询订单
public void selectOrdersByProduct(){
ProductMapper productMapper = session.getMapper(ProductMapper.class);
List orders = productMapper.selectOrdersByProduct(1);
System.out.println(orders);
}
@After
public void destory() {
if (session != null) {
session.close();
}
}
}