前言
在前面我们已经了解了,mybatis 的基本用法,动态SQL,学会使用mybatis 来操作数据库。但这些主要操作还是针对 单表实现的。在实际的开发中,对数据库的操作,常常涉及多张表。
因此本篇博客的目标:通过mybatis 提供的关联映射,建立表与表之间的关系,实现多表的数据操作。
关联映射的概述
在关系型数据库中,表与表之间存在3 种关联映射关系,分别是 一对一,一对多/多对一,和多对多
1. 一对一(One-to-One)
一对一关系是指一个表中的每个记录与另一个表中的一个记录相关联,且这种关联是唯一的。例如:
一个学生只能有一个学生证,一个学生证只属于一个学生。
一个用户只能有一个个人资料,一个个人资料只属于一个用户。
在数据库设计中,一对一关系可以通过以下方式实现:
主键关联:将一个表的主键作为外键放在另一个表中。
联合主键:将两个表的主键合并为一个联合主键,存储在一张表中。
2. 一对多/多对一(One-to-Many/Many-to-One)
一对多关系是指一个表中的一个记录可以与另一个表中的多个记录相关联,而另一个表中的每个记录只能与第一个表中的一个记录相关联。例如:
一个部门可以有多个员工,但每个员工只能属于一个部门。
一个作者可以写多本书,但每本书只能由一个作者创作。
在数据库设计中,一对多关系通常通过外键来实现:
在“多”的一方的表中添加一个外键字段,指向“一”的一方的主键字段。
3. 多对多(Many-to-Many)
多对多关系是指一个表中的多个记录可以与另一个表中的多个记录相关联。例如:
一个学生可以选修多门课程,一门课程也可以被多个学生选修。
一个作者可以写多本书,一本书也可以由多个作者共同创作。
注意
在数据库设计中,多对多关系通常通过一个**关联表(中间表)**来实现:
关联表包含两个表的主键作为外键字段,用于建立多对多关系。
例如,对于学生和课程的多对多关系,可以创建一个选课表,包含学生ID和课程ID作为外键字段。
一对一查询
应用场景
例如 表示 一个人 只能有一个身份证,同时一个身份证也只对应一个人
重点
在学习 一对一查询 时,核心是学习使用
属性说明property用于指定映射到的实体类的属性,与表字段一一对应Column用于指定表中的对应的字段javaType用于指定映射到实体对象的属性jdbcType用于指定数据表中对应的字段类型fetchType用于指定在关联查询时是否启用延迟加载。fetchType属性 有lazy,eager两个属性值,默认为lazy(默认关联映射延迟加载)select用于指定引入嵌套查询的子SQL语句,该属性用于关联映射的前提查询autoMapper用于指定是否自动映射typeHander用于指定一个类型处理器
嵌套查询方式,嵌套结果方式的区别
我理解 嵌套查询方式是多步走,而不是一步到位。例如 你写一个 复合sql语句【相当于sql 嵌套着其他的sql 语句】,去查表中的数据,现在是把这个sql 语句拆开,通过表之间的关系 ,一个个去查,最好得到结果。
样例
select="fs.mapper.IdCardMapper.findCodeById"/> 嵌套结果方式,则是一步到位。通过使用一个复合的sql 语句【相当于sql 嵌套着其他的sql 语句】得到最终结果。 样例
demo(案例)
项目准备
数据库中 tb_person 表,tb_idcard 表
实体类 IdCard 类,Person 类
mybatis -config 配置文件
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
问题:当输入 id=1时,查询person 人的具体信息包括个人身份证信息
PersonMapper 接口
// 根据id查询
Person findPersonById(Integer id);
Person findPersonById2(Integer id);
PersonMapper.xml 映射文件
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
// 需要针对 数据库做的操作【查询,修改,删除,插入】
嵌套查询方式【多步到位】
select * from tb_person where id = #{id}
select="fs.mapper.IdCardMapper.findCodeById"/> 嵌套查询方式 接口的findCodeById 方法和IdCardMapper.xml映射文件 接口的findCodeById 方法 IdCardMapper.xml映射文件 嵌套结果方式【一步到位】 select p.*, c.code from tb_person p , tb_idcard c where p.id = #{id} and p.card_id = c.id
一对多查询
应用场景
与一对一的关联相比,更多关联关系是一对多(或多对一)例如 一个用户 可以有多个订单,多个订单也可以归一个用户所有。
重点使用
1
ofType 与javaType属性相对应,用于指定实体类对象中集合类属性所包含的元素类型【集合中存储的实体类对象类型】
2 与
demo(案例)
项目准备
数据库 中 tb_user用户表 ,tb_order 订单表
实体类 User 用户 类,Orders订单类
问题:当输入用户 id=1,时获得 该用户所有的订单情况
UserMapper接口
package fs.mapper;
import fs.pojo.User;
public interface UserMapper {
User findUserWithOrders(Integer id);
}
UserMapper.xml 映射文件
嵌套结果查询方式【一步到位】
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select u.*,o.id as orders_id,o.number from tb_user u, tb_orders o
where u.id = o.user_id and u.id = #{id}
多对多查询
应用场景
多对多查询 和一对多查询,在现实生活也是非常常见的。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多种订单中,订单和商品就是典型的多对多的关系。
重点使用
demo(案例)
项目准备
数据库中 tb_orders 订单表 ,tb_product 商品表
中间表 tb_ordersitem
实体类 Product用户 类,Orders订单类
问题:当输入 订单 id=1 时,查询 该订单中所有的商品信息
OrderMapper 接口
package fs.mapper;
import fs.pojo.Orders;
public interface OrdersMapper {
Orders findOrdersWithProduct(Integer id);
Orders findOrdersWithProduct1(Integer id);
}
OrderMapper 映射文件
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
// 各自数据库的操作
嵌套查询方式【多步到位】
select * from tb_orders where id=#{id};
嵌套查询方式 接口的findProductById 方法和ProductMapper.xml映射文件
ProductMapper 接口
ProductMapper.xml 映射文件
嵌套结果方式【一步到位】
没有给 tb_product 表的 id 字段 添加别名为 pid 之前
产生问题
当两者关联的表存在相同的字段时,在执行sql 查询 后,会因为tb_order表的id 字段和tb_product 的 id字段 相同,导致 查询结果在映射到product 实体类对象时,后面的product 对象始终会把前面的product 对象覆盖掉。本来应该查询多个 product对象信息,但最好打印只有 1个【最好一个product对象信息】
运行截图
如果给 tb_product 表的 id 字段 添加别名 或者 给 tb_orders 表 的 id 字段添加别名
在这里我是给 tb_product 表的 id 字段 添加别名 为 pid
运行截图
最好确实出现了3 个product 对象的具体信息