目录
表之间的关联关系
实体类
映射文件
映射接口
测试类
一对多关联关系
多对一关联关系
一对一关联
实体类
多对多关联
映射文件
映射接口
测试类
一对多关联关系
多对一关联关系
一对一关联
实体类
多对多关联
测试类
一对多关联关系
多对一关联关系
一对一关联
实体类
多对多关联
多对一关联关系
一对一关联
实体类
多对多关联
实体类
多对多关联
表之间的关联关系
表与表之间一般存在三种关系,即一对一,一对多,多对多关系。 关联关系是有方向的
1)一对多关联:一个老师可以教多个学生,多个学生只有一个老师来教,站在老师方,就是一对多关联.
2)多对一关联:一个老师可以教多个学生,多个学生只有一个老师来教,站在学生方,就是多对一关联.
3)一对一关联:一个老师辅导一个学生,一个学生只请教一个老师.学生和老师是一对一.
4)多对多关联:园区划线的车位和园区的每一辆车.任意一个车位可以停任意一辆车.任意一车辆车可以停在任意一个车位上。
在实际项目开发中,一对多是非常常见的关系,比如,一个班级可以有多个学生,一个学生只能属于一个班级,班级和学生是一对多的关系,而学生和班级是多对一的关系。数据库中一对多关系通常使用主外键关联,外键列应该在多方,即多方维护关系。下面我们就用一个简单示例来看看MyBatis怎么处理一对多关系。首先,给在数据库创建两个表tb_clazz 和tb_student,并插入测试数据。SQL脚本如下:
drop table if exists tb_clazz;
CREATE TABLE tb_clazz(
id INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAr(18),
NAME VARCHAr(18)
);
INSERT INTO tb_clazz(CODE,NAME) VALUES('201803','计算机专业');
drop table if exists tb_student;
CREATE TABLE tb_student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAr(18),
sex VARCHAr(18),
age INT,
clazz_id INT,
FOREIGN KEY(clazz_id) REFERENCES tb_clazz(id)
);
INSERT INTO tb_student (NAME,sex,age,clazz_id) VALUES('小马','男',23,1) ;
INSERT INTO tb_student (NAME,sex,age,clazz_id) VALUES('小李','女',23,1) ;
INSERT INTO tb_student (NAME,sex,age,clazz_id) VALUES('小明','男',23,1) ;
INSERT INTO tb_student (NAME,sex,age,clazz_id) VALUES('小红','女',23,1) ;
提示:
tb_student表的clazz_id作为外键参照tb_clazz表的主键id。
实体类
在数据库中执行SQL脚本,完成创建数据库和表的操作。接下来,创建一个Clazz对象和一个Student对象分别映射tb_clazz和tb_student表。
班级和学生是一对多的关系,即一个班级可以有多个学生。在Clazz类当中定义了一个students属性,该属性是一个List集合,用来映射一对多的关联关系,表示一个班级有多个学生。
import java.io.Serializable;
import java.util.List;
public class Clazz implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id; // 班级id,主键
private String code; // 班级编号
private String name; // 班级名称
// 班级和学生是一对多的关系,即一个班级可以有多个学生
private List students;
public Clazz()
{
super();
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getCode()
{
return code;
}
public void setCode(String code)
{
this.code = code;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public List getStudents()
{
return students;
}
public void setStudents(List students)
{
this.students = students;
}
@Override
public String toString()
{
return "Clazz [id=" + id + ", code=" + code + ", name=" + name + "]";
}
}
班级和学生是一对多的关系,即一个班级可以有多个学生。在Clazz类当中定义了一个students属性,该属性是一个List集合,用来映射一对多的关联关系,表示一个班级有多个学生。
import java.io.Serializable;
public class Student implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id; // 学生id,主键
private String name; // 姓名
private String sex; // 性别
private Integer age; // 年龄
// 学生和班级是多对一的关系,即一个学生只属于一个班级
private Clazz clazz;
public Student()
{
super();
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSex()
{
return sex;
}
public void setSex(String sex)
{
this.sex = sex;
}
public Integer getAge()
{
return age;
}
public void setAge(Integer age)
{
this.age = age;
}
public Clazz getClazz()
{
return clazz;
}
public void setClazz(Clazz clazz)
{
this.clazz = clazz;
}
@Override
public String toString()
{
return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
学生和班级是多对一的关系,即一个学生只属于一个班级。在Student类当中定义了一个clazz属性,该属性是一个Clazz类型,用来映射多对一的关联关系,表示该学生所属的班级。
映射文件
ClazzMapper.xml中定义了一个
由于student是一个List集合,所以clazzResultMap中使用了
还使用了一个新的属性fetchType,该属性的取值有eager和lazy,eager表示立即加载,即查询Clazz对象的时候,会立即执行关联的selectStudentByClazzId中定义的SQL语句去查询班级的所有学生;lazy表示懒加载,其不会立即发送SQL语句去查询班级的所有学生,而是等到需要使用到班级的students属性时,才会发送SQL语句去查询班级的所有学生。fetch机制更多的是为了性能考虑,如果查询班级时确定会访问班级的所有学生,则该属性应该设置为eager;如果查询班级时只是查询班级信息,有可能不会访问班级的所有学生,则该属性应该设置为lazy。正常情况下,一对多所关联的集合对象,都应该被设置成lazy。
使用懒加载还需要在mybatis-config.xm 中增加如下配置:
lazyLoadingEnabled属性表示延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认为false。
agressiveLazyLoading属性启用时,会使带有延迟加载属性的对象立即加载; 反之,每种属性将会按需加载。默认为true,所以这里需要设置成false。
StudentMapper.xm 中定义了一个
在实际开发中,一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载; 而多对一只是关联到一个对象,所以通常使用多表连接直接把数据提取出来。
StudentMapper.xml 中还定义了一个
映射接口
import cn.mybatis.mydemo2.domain.Clazz;
public interface ClazzMapper
{
// 根据id查询班级信息
Clazz selectClazzById(Integer id);
}
import cn.mybatis.mydemo2.domain.Student;
public interface StudentMapper
{
// 根据id查询学生信息
Student selectStudentById(Integer id);
}
测试类
public class App
{
public static void main(String[] args) throws Exception
{
// 读取mybatis-config.xml文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 初始化mybatis,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建Session实例
SqlSession session = sqlSessionFactory.openSession();
App app = new App();
app.testSelectClazzById(session);
// t.testSelectStudentById(session);
// 提交事务
session.commit();
// 关闭Session
session.close();
}
// 测试一对多,查询班级Clazz(一)的时候级联查询学生Student(多)
public void testSelectClazzById(SqlSession session)
{
// 获得ClazzMapper接口的代理对象
ClazzMapper cm = session.getMapper(ClazzMapper.class);
// 调用selectClazzById方法
Clazz clazz = cm.selectClazzById(1);
// 查看查询到的clazz对象信息
System.out.println(clazz.getId() + " " + clazz.getCode() + " " + clazz.getName());
// 查看clazz对象关联的学生信息
List students = clazz.getStudents();
for (Student stu : students)
{
System.out.println(stu);
}
}
// 测试多对一,查询学生Student(多)的时候级联查询 班级Clazz(一)
public void testSelectStudentById(SqlSession session)
{
// 获得StudentMapper接口的代理对象
StudentMapper sm = session.getMapper(StudentMapper.class);
// 调用selectStudentById方法
Student stu = sm.selectStudentById(1);
// 查看查询到的Student对象信息
System.out.println(stu);
// 查看Student对象关联的班级信息
System.out.println(stu.getClazz());
}
}
一对多关联关系
public class App
{
public static void main(String[] args) throws Exception
{
// 读取mybatis-config.xml文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 初始化mybatis,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建Session实例
SqlSession session = sqlSessionFactory.openSession();
App app = new App();
app.testSelectClazzById(session);
// t.testSelectStudentById(session);
// 提交事务
session.commit();
// 关闭Session
session.close();
}
// 测试一对多,查询班级Clazz(一)的时候级联查询学生Student(多)
public void testSelectClazzById(SqlSession session)
{
// 获得ClazzMapper接口的代理对象
ClazzMapper cm = session.getMapper(ClazzMapper.class);
// 调用selectClazzById方法
Clazz clazz = cm.selectClazzById(1);
// 查看查询到的clazz对象信息
System.out.println(clazz.getId() + " " + clazz.getCode() + " " + clazz.getName());
// 查看clazz对象关联的学生信息
List students = clazz.getStudents();
for (Student stu : students)
{
System.out.println(stu);
}
}
// 测试多对一,查询学生Student(多)的时候级联查询 班级Clazz(一)
public void testSelectStudentById(SqlSession session)
{
// 获得StudentMapper接口的代理对象
StudentMapper sm = session.getMapper(StudentMapper.class);
// 调用selectStudentById方法
Student stu = sm.selectStudentById(1);
// 查看查询到的Student对象信息
System.out.println(stu);
// 查看Student对象关联的班级信息
System.out.println(stu.getClazz());
}
}
一对多关联关系
客户和订单就是典型的一对多关联关系。
一个客户名下可以有多个订单。
客户表是一方,订单表是多方.客户一中持有订单的集合。
使用一对多的关联关系,可以满足查询客户的同时查询该客户名下的所有订单。
select c.id cid,name,age,o.id oid,orderNumber,orderPrice,customer_id from customer c left join orders o on c.id = o.customer_id where c.id=#{id}
多对一关联关系
订单和客户就是多对一关联。
站在订单的方向查询订单的同时将客户信息查出。
订单是多方,会持有一方的对象,客户是一方。
select o.id oid,orderNumber,orderPrice,customer_id,c.id cid,name,age from orders o inner join customer c on o.customer_id = c.id where o.id=#{id}
一对一关联
在实际项目开发中,经常存在一对一关系,比如一个人只能有一个身份证,一个身份证只能给一个人使用,这就是一对一的关系。一对一关系推荐使用唯一主外键关联,即两张表使用外键关联,由于是一对一关联,因此还需要给外键列增加unique唯一约束。
drop table if exists tb_card;
create table tb_card (
id int primary key auto_increment,
code varchar(18)
);
insert into tb_card(code) values ('432801198009191038');
drop table if exists tb_person;
create table tb_person (
id int primary key auto_increment,
name varchar(18),
sex varchar(18),
age int,
card_id int unique,
foreign key(card_id) peferences tb_card(id)
);
insert into tb_person(name, sex, age, card_id) values ('jack','男',23,1) ;
提示:
tb_person表的card_id作为外键,其参照tb_card表的主键id,因为是一对一关系,即一个card只能让一个person使用,所以card_id做成了唯一键约束。如此一来,当一个person使用了一个card之后,其他的person就不能使用该card了。
接下来,创建一个Card对象和一个Person对象分别映射tb_card和tb_person表。
package cn.mybatis.mydemo.domain;
import java.io.Serializable;
public class Card implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id; // 主键id
private String code; // 身份证编号
public Card()
{
super();
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getCode()
{
return code;
}
public void setCode(String code)
{
this.code = code;
}
@Override
public String toString()
{
return "Card [id=" + id + ", code=" + code + "]";
}
}
package cn.mybatis.mydemo.domain;
import java.io.Serializable;
public class Person implements Serializable
{
private static final long serialVersionUID = 1L;
private Integer id; // 主键id
private String name; // 姓名
private String sex; // 性别
private Integer age; // 年龄
// 人和身份证是一对一的关系,即一个人只有一个身份证
private Card card;
public Person()
{
super();
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSex()
{
return sex;
}
public void setSex(String sex)
{
this.sex = sex;
}
public Integer getAge()
{
return age;
}
public void setAge(Integer age)
{
this.age = age;
}
public Card getCard()
{
return card;
}
public void setCard(Card card)
{
this.card = card;
}
@Override
public String toString()
{
return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}
select * from tb_card where id = #{id} select * from tb_person where id = #{id}
public class App
{
public static void main(String[] args) throws Exception
{
// 读取mybatis-config.xml文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 初始化mybatis,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建Session实例
SqlSession session = sqlSessionFactory.openSession();
Person p = session.selectOne("cn.mybatis.mydemo.mapper.PersonMapper.selectPersonById", 1);
System.out.println(p);
System.out.println(p.getCard().getCode());
// 获得mapper接口的代理对象
PersonMapper pm = session.getMapper(PersonMapper.class);
// 直接调用接口的方法,查询id为1的Peson数据
Person p2 = pm.selectPersonById(1);
// 打印Peson对象
System.out.println(p2);
// 打印Person对象关联的Card对象
System.out.println(p2.getCard());
// 提交事务
session.commit();
// 关闭Session
session.close();
}
}
多对多关联
重点是中间表,中间表对关系进行描述。意味着实体类中可能包含对方的集合。
总结:无论是什么关联关系,如果某方持有另一方的集合,则使用



