1 MyBatis的多表操作
1.1 多表模型介绍
我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的。
-
多表模型分类 一对一:在任意一方建立外键,关联对方的主键。
-
一对多:在多的一方建立外键,关联一的一方的主键。
-
多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。
1.2 多表模型一对一操作
-
一对一模型: 人和身份证,一个人只有一个身份证
-
代码实现运行结果:
- 步骤一: sql语句准备
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
age INT);
INSERT INTO person VALUES (NULL,'张三',23);
INSERT INTO person VALUES (NULL,'李四',24);
INSERT INTO person VALUES (NULL,'王五',25);
CREATE TABLE card(
id INT PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(30),
pid INT,
CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id));
INSERT INTO card VALUES (NULL,'12345',1);INSERT INTO card VALUES (NULL,'23456',2);INSERT INTO card VALUES (NULL,'34567',3);
创建实体对象
@Data
public class Card {
private int id;
private int number;
private int pid; // 可以不写
private Person p;
}
@Data
public class Person {
private int id;
private String name;
private int age;
}
步骤二:编写dao接口
public interface OneToOneDao {
//查询全部card数据
public List<Card> findAll();
}
步骤三:配置文件
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zxy.dao.OneToOneDao">
<!--配置字段和实体对象属性的映射关系-->
<resultMap id="oneToOne" type="card">
<id column="cid" property="id"/>
<result property="number" column="number"/>
<!--实体类里面不包含 pid属性的话不需要这一句话-->
<result property="pid" column="cpid"/>
<!--
association:配置被包含对象的映射关系
property:被包含对象的变量名
javaType:被包含对象的数据类型
-->
<association property="p" javaType="person">
<id column="pid" property="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
</association>
</resultMap>
<!-- 查询数据-->
<select id="findAll" resultMap="oneToOne">
// 实体没有pid属性的时候使用以下语句
select c.id cid,number,pid,name,age from card c,person p where c.pid=p.id
//包含pid属性的时候使用以下语句
select c.id cid,c.pid cpid,number,pid,name,age from card c , person p where c.pid=p.id
</select>
</mapper>
步骤四:配置核心配置文件
<mapper resource="cn/zxy/dao/onetoOneDao.xml"/>
步骤五:测试类
@Test
public void onetest(){
SqlSession sqlSession = MybatisUtils.getSqlSession(true);
//获取UserDao接口实现类对象
OneToOneDao mapper = sqlSession.getMapper(OneToOneDao.class);
List<Card> list = mapper.findAll();
for (Card card : list) {
System.out.println(card);
}
MybatisUtils.closeSqlSession(sqlSession);
}
运行结果:
一对一配置总结
<resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称
<association>:配置被包含对象的映射关系标签。
property 属性:被包含对象的变量名
javaType 属性:被包含对象的数据类型
1.3 多表模型一对多操作
-
一对多模型: 一对多模型:班级和学生,一个班级可以有多个学生。
-
代码实现
- 步骤一: sql语句准备
CREATE TABLE classes(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO classes VALUES (NULL,'211一等班级');
INSERT INTO classes VALUES (NULL,'211二等班级');
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT,
cid INT,
CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
);
INSERT INTO student VALUES (NULL,'张三',23,1);
INSERT INTO student VALUES (NULL,'李四',24,1);
INSERT INTO student VALUES (NULL,'王五',25,2);
INSERT INTO student VALUES (NULL,'赵六',26,2);
实体类准备
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Classes {
private Integer id; //主键id
private String name; //班级名称
private List<Student> students; //班级中所有学生对象
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id; //主键id
private String name; //学生姓名
private Integer age; //学生年龄
}
步骤二:添加dao接口对象
public interface OneToManyDao {
public List<Classes> findAll();
}
步骤三:配置文件
<mapper namespace="com.by.dao.OneToManyDao">
<resultMap id="oneToMany" type="classes">
<id column="cid" property="id"/>
<result column="name" property="name"/>
<!--
collection:配置被包含的集合对象映射关系
property:被包含对象的变量名
ofType:被包含对象的实际数据类型
-->
<collection property="students" ofType="student">
<id column="sid" property="id"></id>
<result column="name" property="name"/>
<result column="age" property="age"/>
</collection>
</resultMap>
<select id="findAll" resultMap="oneToMany">
select c.id cid,c.name,s.id sid,s.name,s.age from classes c,student s where c.id=s.cid
</select>
</mapper>
步骤四:配置核心文件
<mapper resource="com/by/dao/OneToManyDao.xml"/>
步骤五:测试类
@Test
public void onemanytest(){
SqlSession sqlSession = MybatisUtils.getSqlSession(true);
//获取UserDao接口实现类对象
OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
List<Classes> list = mapper.findAll();
for (Classes classes : list) {
System.out.println(classes);
}
MybatisUtils.closeSqlSession(sqlSession);
}
一对多配置文件总结:
<resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型
什么是延迟加载?
问题
在开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的订单信息,此时就是我们所说的延迟加载
举个例子
*在一对多中,当我们有一个用户,它有个100个订单
在查询用户的时候,要不要把关联的订单查出来?
在查询订单的时候,要不要把关联的用户查出来?
* 回答
在查询用户时,用户下的订单应该是,什么时候用,什么时候查询。
在查询订单时,订单所属的用户信息应该是随着订单一起查询出来。
延迟加载
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据
延迟加载也称懒加载。
优点:
先从单表查询,需要时再从关联表去关联查询,⼤⼤提⾼数据库性能,因为查询单表要比关联查询多张表速度要快。
缺点:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成⽤户等待时间变长,造成用户体验下降。
在多表中:
一对多,多对多:通常情况下采用延迟加载
一对一(多对一):通常情况下采用立即加载
注意:
延迟加载是基于嵌套查询来实现的
局部延迟加载实现:
一对一延迟加载:
创建Personmapper接口
public interface PersonMapper {
public List<Person> findById(Integer id);
}
创建mapper映射文件:
<mapper namespace="cn.zxy.mapper.PersonMapper">
<select id="findById" resultType="person">
select * from person where id=#{id}
</select>
</mapper>
修改一对一映射文件:
<mapper namespace="cn.zxy.mapper.OneToOneMapper">
<!--配置字段和实体的关系 映射关系-->
<resultMap id="OneToOne" type="card">
<id property="id" column="id"/>
<result property="number" column="number"/>
<result property="pid" column="pid"/>
<!--
fetchType 延迟加载 lazy 指的是懒加载
eager 指的是默认立即加载
select 是查询关键字 查询mapper接口里面的findById
如果使用全局的延迟加载 那么就把fetchType 去掉,然后全局配置必须是true才开启延迟加载
-->
<association property="p" column="pid" javaType="person"
select="cn.zxy.mapper.PersonMapper.findById" fetchType="lazy">
</association>
</resultMap>
<!--需要两个mapper映射文件配合使用-->
<select id="findAll" resultMap="OneToOne">
select * from card
</select>
</mapper>
一对多延迟加载
创建StuMapper接口:
public interface StudentMapper {
public List<Student> findByCid(Integer id);
}
创建StuMapper映射文件:
<mapper namespace="cn.zxy.mapper.StudentMapper">
<select id="findByCid" resultType="student">
select * from student where cid=#{id}
</select>
</mapper>
修改一对多映射文件:
<mapper namespace="cn.zxy.mapper.OneToManyMapper">
<resultMap id="oneToMany" type="classes">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--延迟加载 使用全局的延迟模式-->
<collection property="stu" column="id" ofType="student"
select="cn.zxy.mapper.StudentMapper.findByCid" fetchType="eager">
</collection>
</resultMap>
<select id="findAll" resultMap="oneToMany">
select * from classes
</select>
</mapper>
延迟加载的原理:
延迟加载主要是通过动态代理的形式实现,通过代理拦截到指定方法,执行数据加载。
1.4 多表模型多对多操作
多对多模型:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。
代码实现
- 步骤一: sql语句准备
CREATE TABLE course(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO course VALUES (NULL,'语文');
INSERT INTO course VALUES (NULL,'数学');
CREATE TABLE stu_cr(
id INT PRIMARY KEY AUTO_INCREMENT,
sid INT,
cid INT,
CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);
INSERT INTO stu_cr VALUES (NULL,1,1);
INSERT INTO stu_cr VALUES (NULL,1,2);
INSERT INTO stu_cr VALUES (NULL,2,1);
INSERT INTO stu_cr VALUES (NULL,2,2);
- 步骤二:实体类准备
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
private Integer id; //主键id
private String name; //课程名称
}
student类里面添加 课程集合
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id; //主键id
private String name; //学生姓名
private Integer age; //学生年龄
private List<Course> courses; // 学生所选择的课程集合
}
步骤三:创建dao接口
public interface ManyToManyDao {
//查询学生全部数据 得到课表和班级
public List<Student> findAll();
}
步骤四:配置文件
- 数据库字段分析:
select sc.sid scid,s.id sid,s.name,s.age,c.id cid,c.name cname ,sc.id from student s, course c, stu_cr sc where sc.sid=s.id and s.cid=c.id
- 映射文件编写
<mapper namespace="cn.zxy.dao.ManyToManyDao">
<resultMap id="manyToMany" type="student">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="sage" property="age"/>
<!--
property 指的是student里面定义的集合变量名
ofType 指的是实体类对象
-->
<collection property="courses" ofType="course">
<id column="cid" property="id"/>
<result column="cname" property="name"/>
</collection>
</resultMap>
<select id="findAll" resultMap="manyToMany">
select sc.sid,s.name sname, s.age sage,c.name cname,sc.cid from student s,course c,stu_cr sc where sc.sid=s.id and sc.cid=c.id
</select>
</mapper>
步骤五:测试类
@Test
public void manytoTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession(true);
//获取UserDao接口实现类对象
ManyToManyDao mapper = sqlSession.getMapper(ManyToManyDao.class);
List<Student> manylist = mapper.findAll();
for (Student student : manylist) {
System.out.println(student);
}
MybatisUtils.closeSqlSession(sqlSession);
}
1.5 多表模型操作总结
<resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称
<association>:配置被包含对象的映射关系标签。
property 属性:被包含对象的变量名
javaType 属性:被包含对象的数据类型
<collection>:配置被包含集合对象的映射关系标签。
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型