我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的。
多表模型分类 一对一:在任意一方建立外键,关联对方的主键。
一对多:在多的一方建立外键,关联一的一方的主键。
多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。
一对一模型: 人和身份证,一个人只有一个身份证
代码实现运行结果:
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;
}
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"/>
<result property="pid" column="cpid"/>
<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 属性:被包含对象的数据类型
一对多模型: 一对多模型:班级和学生,一个班级可以有多个学生。
代码实现
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; //学生年龄
}
public interface OneToManyDao {
public List findAll();
}
<mapper namespace="com.by.dao.OneToManyDao">
<resultMap id="oneToMany" type="classes">
<id column="cid" property="id"/>
<result column="name" property="name"/>
<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"/>
<association property="p" column="pid" javaType="person"
select="cn.zxy.mapper.PersonMapper.findById" fetchType="lazy">
association>
resultMap>
<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>
延迟加载的原理:
延迟加载主要是通过动态代理的形式实现,通过代理拦截到指定方法,执行数据加载。
多对多模型:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。
代码实现
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"/>
<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);
}
<resultMap>:配置字段和对象属性的映射关系标签。
id 属性:唯一标识
type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
column 属性:表中字段名称
property 属性: 实体对象变量名称
<association>:配置被包含对象的映射关系标签。
property 属性:被包含对象的变量名
javaType 属性:被包含对象的数据类型
<collection>:配置被包含集合对象的映射关系标签。
property 属性:被包含集合对象的变量名
ofType 属性:集合中保存的对象数据类型