1、Mybatis 关联查询(一对一、一对多、多对多)
1.1 表功能介绍
用户表: 记录用户的基本信息。
订单表: 记录用户所创建的订单(购买商品的订单)。
订单详情表: 记录订单的详细信息,即购买商品的信息。
商品表: 记录商品的基本信息。
1.2 表之间的业务关系
用户表和订单表:
用户表 —> 订单表: 一个用户可以创建多个订单,一对多关系;
订单表 —> 用户表: 一个订单只由一个用户创建,一对一关系;
订单表和订单详情表:
订单表 —> 订单详情表: 一个订单可以包含多个订单详情,因为一个订单可以购买多个商品,每个
商品的购买信息在订单详情表中记录,一对多关系;
订单详情表 —> 订单表: 一个订单详情只能包括在一个订单中,一对一关系;
订单详情表和商品表:
订单详情表 —> 商品表: 一个订单详情只对应一个商品信息,一对一关系;
商品表 —> 订单详情表: 一个商品可以包括在多个订单详情,一对多关系;
订单表和商品表:
订单表 <—> 商品表: 一个订单中包含多个商品,一个商品可以添加在多个订单中,两者是通过订
单详情表建立关系,多对多关系;

注意:
如果两张表有主外键关联关系,那么他们的业务关系是一对一/一对多,或者是双向一对一(比如
用户表和用户详情表)。
如果两张表是双向一对多关系,那么他们是多对多关系,并且必然存在一张关系描述表作为中间
表。
1.3 表结构
用户表:
CREATE TABLE users (
id int(11) PRIMARY KEY AUTO_INCREMENT,
username varchar(20),
password varchar(50),
realname varchar(20)
);
INSERT INTO users VALUES (1, 'admin', '123456', '管理员');
INSERT INTO users VALUES (2, 'tom', '123', '汤姆');
INSERT INTO users VALUES (3, 'jerry', '456', '杰瑞');
INSERT INTO users VALUES (4, 'zhangsan', '111', '张三');
INSERT INTO users VALUES (5, 'lisi', '222', '李四');
订单表:
CREATE TABLE orders (
id int(11) PRIMARY KEY AUTO_INCREMENT,
order\_number varchar(30),
total\_price double,
status varchar(5),
user\_id int(11)
);
INSERT INTO orders VALUES (1, '202112290838001', 2535, '已评价', 2);
INSERT INTO orders VALUES (2, '202112290838002', 4704.6, '已签收', 2);
INSERT INTO orders VALUES (3, '202112290838003', 3620, '已支付', 2);
INSERT INTO orders VALUES (4, '202112290840001', 600, '已发货', 3);
INSERT INTO orders VALUES (5, '202112290840002', 280, '未支付', 3);
订单详情表:
CREATE TABLE orders\_detail (
id int(11) PRIMARY KEY AUTO_INCREMENT,
amount int(11),
goods\_id int(11),
orders\_id int(11)
);
INSERT INTO orders\_detail VALUES (1, 1, 1, 1);
INSERT INTO orders\_detail VALUES (2, 3, 8, 1);
INSERT INTO orders\_detail VALUES (3, 1, 2, 2);
INSERT INTO orders\_detail VALUES (4, 2, 7, 2);
INSERT INTO orders\_detail VALUES (5, 1, 3, 3);
INSERT INTO orders\_detail VALUES (6, 6, 6, 3);
INSERT INTO orders\_detail VALUES (7, 2, 4, 4);
INSERT INTO orders\_detail VALUES (8, 1, 5, 5);
商品表:
CREATE TABLE goods (
id int(11) PRIMARY KEY AUTO_INCREMENT,
goods\_name varchar(50),
description varchar(500),
price double
);
INSERT INTO goods VALUES (1, '手机', '手机', 2499);
INSERT INTO goods VALUES (2, '笔记本电脑', '笔记本电脑', 4699);
INSERT INTO goods VALUES (3, 'IPAD', 'IPAD', 3599);
INSERT INTO goods VALUES (4, '运动鞋', '运动鞋', 300);
INSERT INTO goods VALUES (5, '外套', '外套', 280);
INSERT INTO goods VALUES (6, '可乐', '可乐', 3.5);
INSERT INTO goods VALUES (7, '辣条', '辣条', 2.8);
INSERT INTO goods VALUES (8, '水杯', '水杯', 12);
2. 一对一查询
2.1 需求
查询订单信息。
关联如下:关联查询其相关用户信息。
2.2 通过resultType方式实现
实体类:
实体类Orders类不能映射全部字段,需要新创建的实体类,创建一个包括查询字段较多的实体类。
OrdersQuery中包含了Orders以及Users需要查询的属性。
/**
* OrdersQuery值对象,不是entity/po,因为它和数据库中表的字段不是对应关系
*/
public class OrdersQuery {
//订单属性
private Integer id;
private String orderNumber;
private Double totalPrice;
private String status;
private Integer userId;
//用户属性
private String username;
private String password;
private String realname;
}
mapper接口:
public interface OrdersMapper {
List<OrdersQuery> selectUseResultType();
}
mapper文件:
<?xml version="1.0" encoding="UTF-8" ?>
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.mapper.OrdersMapper">
select a.id, a.order_number, a.total_price, a.status, a.user_id,
b.username, b.password, b.realname from orders a, users b where a.user_id=b.id
</select>
</mapper>
测试:
public class QueryTest {
@Test
public void testOneToOneResultType() {
SqlSession sqlSession = MybatisUtil.getSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<OrdersQuery> list = ordersMapper.selectUseResultType();
for (OrdersQuery ordersQuery : list) {
System.out.println(ordersQuery);
}
sqlSession.close();
}
}
2.3 通过resultMap方式实现
用户表实体类:
public class Users {
private Integer id;
private String username;
private String password;
private String realname;
}
订单表实体类:
在Orders类中加入Users属性,Users属性用于存储关联查询的用户信息。
因为订单关联查询用户是一对一关系,所以这里使用单个Users对象存储关联查询的用户信息。
public class Orders {
private Integer id;
private String orderNumber;
private Double totalPrice;
private String status;
private Integer userId;
/**
* 一对一关系属性
*/
private Users users;
mapper接口
List<Orders> selectUseResultMap();
mapper文件
association标签: 一对一关系映射描述。
property: 关系属性名称。
javaType: 关系属性类型。
<resultMap id="selectResultMap" type="com.gs.entity.Orders">
<id column="id" property="id"/>
<result column="order\_number" property="orderNumber"/>
<result column="total\_price" property="totalPrice"/>
<result column="status" property="status"/>
<result column="user\_id" property="userId"/>
<association property="users" javaType="com.gs.entity.Users">
<id column="user\_id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="realname" property="realname"/>
</association>
</resultMap>
select a.id, a.order_number, a.total_price, a.status, a.user_id, b.username,
b.password, b.realname from orders a, users b where a.user_id=b.id
</select>
测试
@Test
public void testOneToOneResultMap() {
SqlSession sqlSession = MybatisUtil.getSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> list = ordersMapper.selectUseResultMap();
for (Orders orders : list) {
System.out.println(orders);
}
sqlSession.close();
}
2.4 resultType和resultMap实现一对一查询小结
resultType:使用resultType实现较为简单,如果实体类中没有包括查询出来的列名,需要增加列
名对应的属性,即可完成映射。如果查询结果没有特殊要求,建议使用resultType。
resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用
resultMap可以完成将关联查询映射到实体类的属性中。
resultMap可以实现延迟加载,resultType无法实现延迟加载。(懒加载):
懒加载:可以理解为按需加载,当调用到关联的数据时才与数据库交互否则不交互。目的是减少内存的浪费和减 轻系统负担。
例如:
比如user表和role表有关联关系,有这样一条语句:查询uesr的同时将user的某一列数据作为参数一并查询role表符合条件的数据,mybatis里叫做级联。只要执行这条语句,就会将这两张表符合需求的信息一起加载出来。而懒加载只会加载uesr表的数据出来不加载role表的数据。
在全局配置文件中开启。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<!-- true表示如果对具有懒加载特性的对象的任意调用会导致这个对象的完整加载,false表示每种属性按照需要加载。 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
3. 一对多查询
3.1 需求
查询订单信息。关联如下:
关联查询其相关用户信息。
关联查询其相关订单详情信息。
3.2 实体类
订单表实体类:
public class OrdersDetail {
private Integer id;
private Integer amount;
private Integer ordersId;
private Integer goodsId;
}
UTO订单类:
在Order类中加入 List detailList属性,details属性用于存储关联查询的订单详情。
因为订单关联查询订单详情是一对多关系,所以这里使用集合对象存储关联查询的订单详情信息。
public class Orders {
private Integer id;
private String orderNumber;
private Double totalPrice;
private String status;
private Integer userId;
/**
* 一对一关系属性
*/
private Users users;
/**
* 一对多关系属性
*/
private List<OrdersDetail> detailList;
}
mapper接口
List<Orders> selectOrdersAndDetail();
mapper文件
collection标签: 一对多关系映射描述。
property: 关系属性名称。
ofType: 关系属性是一个List集合,集合中存放的元素类型。
esultMap id="detailResultMap" type="com.gs.entity.Orders">
<id column="id" property="id"/>
<result column="order\_number" property="orderNumber"/>
<result column="total\_price" property="totalPrice"/>
<result column="status" property="status"/>
<result column="user\_id" property="userId"/>
<association property="users" javaType="com.gs.entity.Users">
<id column="user\_id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="realname" property="realname"/>
</association>
<collection property="detailList" ofType="com.gs.entity.OrdersDetail">
<id column="detail\_id" property="id"/>
<result column="amount" property="amount"/>
<result column="goods\_id" property="goodsId"/>
<result column="id" property="ordersId"/>
</collection>
</resultMap>
select a.id, a.order_number, a.total_price, a.status, a.user_id, b.username,
b.password, b.realname, c.id detail_id, c.amount,c.goods_id from orders a
join users b on a.user_id=b.id
join orders_detail c on a.id=c.orders_id
</select>
测试@Test
@Test
public void testOneToMany() {
SqlSession sqlSession = MybatisUtil.getSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> list = ordersMapper.selectOrdersAndDetail();
for (Orders orders : list) {
System.out.println(orders);
}
sqlSession.close();
}
4 多对多查询
4.1需求
查询订单信息。关联如下:
关联查询其相关用户信息。
关联查询其相关订单详情信息。
关联查询订单详情中的商品信息。
4.2 多表的实现多对多
将OrderDetail类中Integer类型的goods_id属性修改为Goods类型属性,goods属性用于存储关联查询
的商品信息。
订单与订单详情是一对多关系,订单详情与商品是一对一关系,反之商品与订单详情是一对多关系,订
单详情与订单是一对一关系,所以订单与商品为多对多关系。
商品表实体类:
public class Goods {
private Integer id;
private String goodsName;
private String description;
private Double price;
UTO订单详情表实体类:
public class OrdersDetail {
private Integer id;
private Integer amount;
private Integer ordersId;
private Integer goodsId;
/**
* 一对一关系
*/
private Goods goods;
}
mapper接口
List<Orders> selectOrdersAndGoods();
mapper文件
<resultMap id="goodsResultMap" type="com.gs.entity.Orders">
<id column="id" property="id"/>
<result column="order\_number" property="orderNumber"/>
<result column="total\_price" property="totalPrice"/>
<result column="status" property="status"/>
<result column="user\_id" property="userId"/>
<association property="users" javaType="com.gs.entity.Users">
<id column="user\_id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="realname" property="realname"/>
</association>
<collection property="detailList" ofType="com.gs.entity.OrdersDetail">
<id column="detail\_id" property="id"/>
<result column="amount" property="amount"/>
<result column="goods\_id" property="goodsId"/>
<result column="id" property="ordersId"/>
<association property="goods" javaType="com.gs.entity.Goods">
<id column="goods\_id" property="id"/>
<result column="goods\_name" property="goodsName"/>
<result column="description" property="description"/>
<result column="price" property="price"/>
</association>
</collection>
</resultMap>
select a.id, a.order_number, a.total_price, a.status, a.user_id, b.username,
b.password, b.realname, c.id detail_id, c.amount, c.goods_id, d.goods_name,
d.description, d.price from orders a
join users b on a.user_id=b.id
join orders_detail c on a.id=c.orders_id
join goods d on c.goods_id=d.id
</select>
测试
@Test
public void testManyToMany1() {
SqlSession sqlSession = MybatisUtil.getSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> list = ordersMapper.selectOrdersAndGoods();
for (Orders orders : list) {
System.out.println(orders);
}
sqlSession.close();
}
4.3 学生与课程 两表之间的多对多关系
4.3.1 需求
查询学生信息,并关联查询学生相应的课程信息。
4.3.2 表结构
课程表
CREATE TABLE course (
id int(11) PRIMARY KEY AUTO_INCREMENT,
cname varchar(20)
);
INSERT INTO course VALUES (1, '大学语文');
INSERT INTO course VALUES (2, '大学英语');
INSERT INTO course VALUES (3, '高等数学');
INSERT INTO course VALUES (4, 'JAVA语言');
INSERT INTO course VALUES (5, '网络维护');
INSERT INTO course VALUES (6, '通信原理');
学生表
CREATE TABLE student (
id int(11) PRIMARY KEY AUTO_INCREMENT,
name varchar(20),
gender varchar(20),
major varchar(20)
);
INSERT INTO student VALUES (1, '小明', '男', '软件工程');
INSERT INTO student VALUES (2, '小红', '女', '网络工程');
INSERT INTO student VALUES (3, '小丽', '女', '物联网');
学生课程关系表
CREATE TABLE student\_course (
id int(11) PRIMARY KEY AUTO_INCREMENT,
student\_id int(11),
course\_id int(11)
);
INSERT INTO student\_course VALUES (1, 1, 1);
INSERT INTO student\_course VALUES (2, 1, 3);
INSERT INTO student\_course VALUES (3, 1, 4);
INSERT INTO student\_course VALUES (4, 2, 1);
INSERT INTO student\_course VALUES (5, 2, 2);
INSERT INTO student\_course VALUES (6, 2, 5);
INSERT INTO student\_course VALUES (7, 3, 2);
INSERT INTO student\_course VALUES (8, 3, 3);
INSERT INTO student\_course VALUES (9, 3, 6);
4.3.3 表实体类
课程类:
public class Course {
private Integer id;
private String cname;
}
学生类:
public class Student {
private Integer id;
private String name;
private String gender;
private String major;
/**
* 一对多
*/
private List<Course> courseList;
}
mapper接口
public interface StudentMapper {
List<Student> select();
}
mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gs.mapper.StudentMapper">
<resultMap id="selectResultMap" type="com.gs.entity.Student">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="gender" property="gender"/>
<result column="major" property="major"/>
<collection property="courseList" ofType="com.gs.entity.Course">
<id column="course\_id" property="id"/>
<result column="cname" property="cname"/>
</collection>
</resultMap>
select a.id, a.name, a.gender, a.major, b.course_id, c.cname from student
a
join student_course b on a.id=b.student_id
join course c ON b.course_id=c.id
</select>
</mapper>
测试
@Test
public void testManyToMany2() {
SqlSession sqlSession = MybatisUtil.getSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = studentMapper.select();
for (Student student : list) {
System.out.println(student);
}
sqlSession.close();
}
5 关联查询总结
5.1 resultType
作用:将查询结果按照SQL列名与实体类属性名一致性映射到实体类对象中。
场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可
直接使用resultType将每一条记录映射到实体类中,在前端页面遍历list(list中是实体类)即可。
5.2 resultMap
使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。
5.3 association
作用:将关联查询信息映射到一个实体类对象中。
场合:为了方便查询关联信息可以使用association将关联信息映射为当前对象的一个属性,比如:查询
订单以及关联用户信息。
5.4 collection
作用:将关联查询信息映射到一个list集合中。
场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权
限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的
菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询
结果映射到list集合中。
5.5 resultMap的继承
resultMap标签可以通过extends属性来继承一个已有的或公共的resultMap,避免重复配置的出现,减少配置量。
例子如下:
<!-- 父resultMap标签-->
<resultMap id="baseResultMap" type="com.gs.entity.Orders">
<id column="id" property="id"/>
<result column="order\_number" property="orderNumber"/>
<result column="total\_price" property="totalPrice"/>
<result column="status" property="status"/>
<result column="user\_id" property="userId"/>
</resultMap>
<!-- 继承父resultMap标签中的配置,避免重复配置 -->
<resultMap id="subResultMap" type="com.gs.entity.Orders"
extends="baseResultMap">
<association property="users" javaType="com.gs.entity.Users">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="realname" property="realname"/>
</association>
</resultMap>

724

被折叠的 条评论
为什么被折叠?



