Table of Contents
● 表之间的关系有⼏种: ⼀对多 ,多对⼀, ⼀对⼀, 多对多
Mybatis 多表查询
● 表之间的关系有⼏种: ⼀对多 ,多对⼀, ⼀对⼀, 多对多
举例:
- ⼈和身份证号就是⼀对⼀ ⼀个⼈只能有⼀个身份证号 ⼀个身份证号只能属于⼀个⼈
- ⽤户和订单就是⼀对多 订单和⽤户就是多对⼀,⼀个⽤户可以下多个订单 ⼀个订单属于同⼀个⽤户
- ⽼师和学⽣之间就是多对多 ⼀个学⽣可以被多个⽼师教过 ⼀个⽼师可以教多个学⽣
- 一对一关系推荐使用唯一主外键关联,即两张表使用外键关联,由于是一对一关联,因此还需要给外键列增加unique唯一约束。
- 一对多时,表的创建是,外键设在多的一方
- 多对多时,需要借助一个新的关联表,两个表的主键放在关联表即可
01 ⼀对⼀
一个学生只能有一个学号,一个学号只能给一个学生使用,这就是一对一的关系。一对一关系推荐使用唯一主外键关联,即两张表使用外键关联,由于是一对一关联,因此还需要给外键列增加unique唯一约束。下面我们就用一个简单示例来看看MyBatis怎么处理一对一关系。
1.1 SQL脚本:
- 学号表
-- ----------------------------
-- Table structure for identity
-- ----------------------------
DROP TABLE IF EXISTS `identity`;
CREATE TABLE `identity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`number` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of identity
-- ----------------------------
INSERT INTO `identity` VALUES (1, '20191216');
INSERT INTO `identity` VALUES (2, '20202020');
SET FOREIGN_KEY_CHECKS = 1;
- 学生表:
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生名字',
`sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生性别',
`identityID` int(11) NULL DEFAULT NULL COMMENT '身份证表对应的ID号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `identityFkey`(`identityID`) USING BTREE,
CONSTRAINT `identityFkey` FOREIGN KEY (`identityID`) REFERENCES `identity` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 'Tom', '男', 1);
INSERT INTO `student` VALUES (2, 'Jerry', '女', 2);
SET FOREIGN_KEY_CHECKS = 1;
tips:
student表的identityID作为外键,其参照identity表的主键id,因为是一对一关系,即一个identity只能让一个student使用,所以identityID做成了唯一键约束。如此一来,当一个student使用了一个identity之后,其他的student就不能使用该identity了。
1.2 实体类
- Student.class:
public class Student {
int id;
String name;
String sex;
int identityID;
public int getId() {
return id;
}
public void setId(int 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 int getIdentityID() {
return identityID;
}
public void setIdentityID(int identityID) {
this.identityID = identityID;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", identityID=" + identityID +
'}';
}
}
- Identity.class:
public class Identity {
int id;
String number;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
@Override
public String toString() {
return "Identity{" +
"id=" + id +
", number='" + number + '\'' +
'}';
}
}
1.3 映射接口:
- Mybatis官方手册建议通过mapper接口的代理对象访问mybatis,该对象关联了SqlSession对象,开发者可以通过该对象直接调用方法操作数据库。
- 官方映射接口取名是xxxMapper,这里个人习惯取xxxDao,都是一样,只要映射接口 和 xml映射文件 的文件名相同,目录相同 即可
- IStudentDao.class:
public interface IStudentDao {
/**
* 查找所有学生,同时获取学生所属identity
* @return
*/
public List<Student> findAll();
}
- IIdentityDao.class
public interface IIdentityDao {
/**
* 根据id查找学号
* @param id
* @return
*/
public IIdentityDao findIdentityById(int id);
}
1.4 xml映射文件:
注意:
映射接口、xml映射文件 文件名要相同,目录也要相同:
- IStudentDao.xml:(这里用的是嵌套查询,没用嵌套结果)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IStudentDao">
<!-- 映射Student对象的resultMap -->
<resultMap id="studentMap" type="mybatis.demo.domain.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<!-- 这里使用的是 嵌套查询-->
<association property="identity" javaType="mybatis.demo.domain.Identity" column="id" select="mybatis.demo.dao.IIdentityDao.findIdentityById"/>
</resultMap>
<!-- 查找所有学生,同时获取学生所属identity,结果类型返回resultMap而不是resultType-->
<select id="findAll" resultMap="studentMap">
select * from Student
</select>
</mapper>
- IIdentityDao.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IIdentityDao">
<!-- 查找identity,结果类型返回resultType而不是resultMap-->
<select id="findIdentityById" resultType="mybatis.demo.domain.Identity">
select * from identity where id=#{id}
</select>
</mapper>
说明;
命名空间(Namespaces)的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。
命名空间的作用有两个,一个是利用更长的完全限定名来将不同的语句隔离开来,同时也实现了对应接口绑定。
<select id ="">中的id属性,对应接口中某个方法名
<association >:用于映射一对一的关联关系,JavaType映射到一个 JavaBean 的数据类型,select=“” 嵌套一个查询,column是传给嵌套查询的条件参数
而有个<collection> 是⽤于建⽴⼀对多中集合属性的对应关系,ofType⽤于指定集合元素的 数据类型<即泛型>
1.5 最后配置XML映射文件
在MyBatis的XML配置中,配置映射接口 和 映射文件的绑定,因为需要告诉 MyBatis 到哪里去找到这些SQL语句,用来让MyBatis实现映射接口,最后使用代理对象操作这些接口中的方法,从而操作数据库
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties
>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--
<mapper> 用于配置映射文件
class属性用于注解的方式
resource属性用于XML文件的方式
-->
<mapper resource="mybatis.demo.dao/IIdentityDao.xml"/>
<mapper resource="mybatis.demo.dao/IStudentDao.xml"/>
</mappers>
</configuration>
1.6 创建测试类:
test.java:
public class test {
InputStream inputStream;
SqlSession session;
SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
//1.读取配置⽂件
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory的构建者对象,使⽤构建者创建⼯⼚对象SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.使⽤SqlSessionFactory⽣产SqlSession对象
session = sqlSessionFactory.openSession();
}
@After
public void destroy() throws IOException{
//6.释放资源
session.close();
inputStream.close();
}
@Test
public void test() throws IOException {
//4.使⽤SqlSession创建dao接⼝的代理对象
IStudentDao dao = session.getMapper(IStudentDao.class);
//5.使⽤代理对象执⾏查询所有⽅法
List<Student> students = dao.findAll();
for (Student student : students){
System.out.println(student);
}
}
}
- 测试结果:
1.7 小结:
由于Student类除了简单的属性id、name、sex之外,还有一个关联对象identity,所以返回的是一个名为studentMap的resultMap。studentMap中使用了<association .../>元素映射一对一的关联关系。其中,select属性表示会使用column属性的id值作为参数执行IIdentityDao.xml中定义的selectCardById语句,查询对应的 Identity 数据(即嵌套查询),查询出的数据将被封装到property表示的identity对象当中。
嵌套查询、嵌套结果后面再总结,官方教程地址:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps
02 一对多
一对多是非常常见的关系,比如,一个班级可以有多个学生,一个学生只能属于一个班级,班级和学生是一对多的关系,而学生和班级是多对一的关系。数据库中一对多关系通常使用主外键关联,外键列应该在多方,即多方维护关系。
2.1 SQL脚本
- 班级表:
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`className` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '班级名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of class
-- ----------------------------
INSERT INTO `class` VALUES (1, '16软件1班');
INSERT INTO `class` VALUES (2, '16软件2班');
SET FOREIGN_KEY_CHECKS = 1;
- 学生表:
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生名字',
`sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生性别',
`classID` int(11) NULL DEFAULT NULL COMMENT '身份证表对应的ID号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `classFk`(`classID`) USING BTREE,
CONSTRAINT `classFk` FOREIGN KEY (`classID`) REFERENCES `class` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 'Tom', '男', 1);
INSERT INTO `student` VALUES (2, 'Jerry', '女', 2);
INSERT INTO `student` VALUES (3, 'Jack', '男', 1);
SET FOREIGN_KEY_CHECKS = 1;
student表的classID作为外键参照class表的主键id。
2.2 实体类
创建一个Class对象和一个Student对象分别映射class和student表。
- Student.class:
package mybatis.demo.domain;
public class Student {
int id;
String name;
String sex;
int classID;
public int getId() {
return id;
}
public void setId(int 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 int getClassID() {
return classID;
}
public void setClassID(int classID) {
this.classID = classID;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", classID=" + classID +
'}';
}
}
- Class.class:
班级和学生是一对多的关系,即一个班级可以有多个学生。在Class类当中定义了一个students属性,该属性是一个List集合,用来映射一对多的关联关系,表示一个班级有多个学生。
public class Class {
private int id; //对应数据库表Class中的id字段
private String className; //对应数据库表Class中的className字段
private List<Student> students; //该属性是一个List集合,用来映射一对多的关联关系,表示一个班级有多个学生。
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "Class{" +
"id=" + id +
", className='" + className + '\'' +
", students=" + students +
'}';
}
}
2.3 映射接口
- IClassDao.class:
public interface IClassDao {
/**
* 查找所有班级,并查询旗下包含的学生
* @return
*/
public List<Class> findAllClass();
}
- IStudentDao.class:
public interface IStudentDao {
/**
* 根据班级id查询学生信息
* @param id
* @return
*/
public Student findStudentByClassId(int id);
}
2.4 xml映射文件
- IClassDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IClassDao">
<!--查找所有班级-->
<select id="findAllClass" resultMap="classMap">
select * from class
</select>
<!-- 映射Class对象的resultMap -->
<resultMap id="classMap" type="mybatis.demo.domain.Class">
<id property="id" column="id"/>
<result property="className" column="className"/>
<!-- 一对多关联映射:collection ,students是List集合,ofType是List中的<泛型> ,
select是嵌套select查询,column是传给select的参数 fetchType="lazy"延迟加载 -->
<collection property="students" ofType="mybatis.demo.domain.Student" column="id" select="mybatis.demo.dao.IStudentDao.selectStudentByClazzId" fetchType="lazy"/>
</resultMap>
</mapper>
注意
- collection中的column=“id”是 班级的id,因为此resultMap的type=“mybatis.demo.domain.Class”
- 一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载;
- IStudentDao.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IStudentDao">
<!-- 根据班级id查询学生信息,多表连接,返回resultMap -->
<select id="selectStudentByClazzId" parameterType="int" resultType="mybatis.demo.domain.Student">
SELECT * FROM student where classID=#{id}
</select>
</mapper>
2.5最后配置XML映射文件和懒加载
在MyBatis的XML配置中,配置映射接口 和 映射文件的绑定,因为需要告诉 MyBatis 到哪里去找到这些SQL语句,用来让MyBatis实现映射接口,最后使用代理对象操作这些接口中的方法,从而操作数据库
在实际开发中,一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载; 而多对一只是关联到一个对象,所以通常使用多表连接直接把数据提取出来。
mybatis-config.xml:
......
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
......
<mappers>
<!--
<mapper> 用于配置映射文件
class属性用于注解的方式
resource属性用于XML文件的方式
-->
<mapper resource="mybatis.demo.dao/IClassDao.xml"/>
<mapper resource="mybatis.demo.dao/IStudentDao.xml"/>
</mappers>
lazyLoadingEnabled属性表示延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认为false。
agressiveLazyLoading属性启用时,会使带有延迟加载属性的对象立即加载; 反之,每种属性将会按需加载。默认为true,所以这里需要设置成false。
2.6 测试(测试一对多以及懒加载)
- 测试类
public class test {
...
其余代码略,和上面一样的
...
@Test
public void test() throws IOException {
//4.使⽤SqlSession创建dao接⼝的代理对象
IStudentDao studentDao = session.getMapper(IStudentDao.class);
IClassDao classDao=session.getMapper(IClassDao.class);
//5.使⽤代理对象执⾏查询所有⽅法
//一对多
List<Class> classes = classDao.findAllClass();
for (Class cla:classes) {
System.out.println("className="+cla.getClassName());
}
}
}
- 测试结果
可以看到,MyBatis只是执行了查询班级的SQL语句,由<colletion fethType=lazy">使用的是懒加载,因此,当没有使用到关联的学生对象时,并没有发送查询学生的SQL语句。
- 修改测试代码:
@Test
public void test() throws IOException {
//4.使⽤SqlSession创建dao接⼝的代理对象
IStudentDao studentDao = session.getMapper(IStudentDao.class);
IClassDao classDao=session.getMapper(IClassDao.class);
//5.使⽤代理对象执⾏查询所有⽅法
//一对多
List<Class> classes = classDao.findAllClass();
for (Class cla:classes) {
System.out.println("className="+cla.getClassName() + "students=["+cla.getStudents()+"]");
}
}
测试结果:
可以看到,MyBatis执行了查询班级的SQL语句之后,又执行了根据class_id 查询学生信息的SQL语句。这就是所谓的懒加载,关联对象,用到才会加载,执行相应的SQL语句,而不是一下全部查询
2.7 小结:
- 一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载;
- 多对一只是关联到一个对象,所以通常使用多表连接 直接把数据提取出来。
- 懒加载,关联对象用到才会加载,执行相应的SQL语句,而不是一下全部查询
多对一,和一对一差不多,都是用到<association>标签,反正,
多的一方(集合)用<collection>,
一的一方(仅仅是一个pojo/javaBean 对象)用<association>标签
所以多对一不做笔记了
03 多对多
对于数据库中多对多关系建议使用一个中间表来维护关系
table1(主键id1)和table2(主键id2)为多对多
中间表为t1_t2 :(table1_id,table2_id)为主键,且table1_id 作为外键参照table1的主键id1,table2_id 作为外键参照table2的主键id2
示例:⽤户和⻆⾊ ⼀个⽤户可以有多个⻆⾊ ⼀个⻆⾊可以赋予多个⽤户 步骤: 1、建⽴两张表: ⽤户表,⻆⾊表 让⽤户表和⻆⾊表具有多对多的关系。需要使⽤中间表,中间表中包含各⾃的主 键,在中间表中是外键。 2、建⽴两个实体类:⽤户实体类和⻆⾊实体类 让⽤户和⻆⾊的实体类 能体现出来多对多的关系 各⾃包含对⽅⼀个集合引⽤ 3、建⽴两个配置⽂件 ⽤户的配置⽂件 ⻆⾊ 的配置⽂件 4、实现配置: 当我们查询⽤户时,可以同时得到⽤户所包含的⻆⾊信息 当我们查询 ⻆⾊时,可以同时得到⻆⾊的所赋予的⽤户信息
3.1 SQL脚本
- student表:
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生名字',
`sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生性别',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 'Tom', '男');
INSERT INTO `student` VALUES (2, 'Jerry', '女');
INSERT INTO `student` VALUES (3, 'Jack', '男');
INSERT INTO `student` VALUES (4, 'Marry', '女');
SET FOREIGN_KEY_CHECKS = 1;
- role表:
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`roleName` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '学生');
INSERT INTO `role` VALUES (2, '班长');
INSERT INTO `role` VALUES (3, '学习委员');
SET FOREIGN_KEY_CHECKS = 1;
- role_student表:
DROP TABLE IF EXISTS `role_student`;
CREATE TABLE `role_student` (
`role_id` int(11) NOT NULL,
`student_id` int(11) NOT NULL,
PRIMARY KEY (`role_id`, `student_id`) USING BTREE,
INDEX `stuFkey`(`student_id`) USING BTREE,
CONSTRAINT `roleFkey` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `stuFkey` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role_student
-- ----------------------------
INSERT INTO `role_student` VALUES (1, 1);
INSERT INTO `role_student` VALUES (2, 1);
INSERT INTO `role_student` VALUES (1, 2);
INSERT INTO `role_student` VALUES (1, 3);
INSERT INTO `role_student` VALUES (1, 4);
INSERT INTO `role_student` VALUES (3, 4);
SET FOREIGN_KEY_CHECKS = 1;
3.2 实体类:
- Role.class
在Role类中,定义了一个students集合,用来映射多对多的关联关系,表示一个角色可以对应多个学生
public class Role {
private int id;
private String roleName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleName='" + roleName + '\'' +
'}';
}
}
- Student.class:
在Student 类中定义了一个roles 属性,该属性是一个List集合,用来映射多对多的关联关系,表示该学生关联的多个角色。
public class Student {
int id;
String name;
String sex;
List<Role> roles;//该学生拥有的所有角色
public int getId() {
return id;
}
public void setId(int 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 List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", roles=" + roles +
'}';
}
}
3.3 映射接口
- IStudentDao.class:
public interface IStudentDao {
/**
* 查询所有学生信息
* @return
*/
public List<Student> findAllStudent();
}
3.4 xml映射文件:
IStudentDao.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace命名空间写当前你想映射的接⼝-->
<mapper namespace="mybatis.demo.dao.IStudentDao">
<!-- 查询学生信息,返回resultMap -->
<select id="findAllStudent" parameterType="int" resultMap="studentMap">
SELECT student.id as SID, student.name as SName,student.sex , role.*
FROM student
left join role_student on student.id=role_student.student_id
left join role on role_student.role_id=role.id
</select>
<resultMap id="studentMap" type="mybatis.demo.domain.Student">
<id property="id" column="SID"/>
<result property="name" column="SName"/>
<result property="sex" column="sex"/>
<!-- 这里用的是嵌套结果,并没有用嵌套查询 -->
<collection property="roles" ofType="mybatis.demo.domain.Role">
<id property="id" column="id"/>
<result property="roleName" column="roleName"/>
</collection>
</resultMap>
</mapper>
注意:
- <resultMap>
<id property="id" column="SID"/>
<result property="name" column="SName"/>
<result property="sex" column="sex"/> 中的column的值,是根据SQL语句中的字段值,如果select语句中为字段设置了别名,column就是别名的值,没别名就是数据库的字段值
- 设置别名,就是防止字段名冲突
3.5 测试
- 测试类:
@Test
public void test() throws IOException {
//4.使⽤SqlSession创建dao接⼝的代理对象
IStudentDao studentDao = session.getMapper(IStudentDao.class);
//5.使⽤代理对象执⾏查询所有⽅法
List<Student> allStudent = studentDao.findAllStudent();
for (Student student:allStudent) {
System.out.println(student);
}
}
- 测试结果:
这里命名只有4条显示,为什么Total=条结果了,因为SQL语句的结果就是6条:
而这里用的是嵌套结果,学生里嵌套了角色,所以一个学生的多个角色都嵌套进了同一个学生里,而单纯从数据库直接执行sql语句,并没嵌套,所以6条都显示了(注意看,Tom,Marry都出现了2次了)
04 总结
4.1 一对一、多对一、多对多理解
多对一,一对一中,
表示 一(关联的是一个JavaBean或pojo对象)用的是<association>标签,
多 (关联的是集合)用的是<collection>标
多对多,需要借助中间表,中间表来维护关系
table1(主键id1)和table2(主键id2)为多对多
中间表为t1_t2 :(table1_id,table2_id)为主键,且table1_id 作为外键参照table1的主键id1,table2_id 作为外键参照table2的主键id2
(借助中间表,相当拆分成了 多对一对多,可以这么去理解)
4.2 个人对于懒加载 和嵌套查询、嵌套结果理解
- 懒加载,关联对象用到才会加载,执行相应的SQL语句,而不是一下全部查询
- 一对多关系通常映射为集合对象,而由于多方的数据量可能很大,所以通常使用懒加载; 而多对一只是关联到一个对象,所以通常使用多表连接直接把数据提取出来。
- 个人理解,嵌套查询的懒加载才会生效,因为是嵌套的select是分离的,而嵌套结果是一次性查找所有结果
4.3 column取值依据
- <resultMap>
<id property="id" column="SID"/>
<result property="name" column="SName"/>
<result property="sex" column="sex"/> 中的column的值,是根据SQL语句中的字段值,如果select语句中为字段设置了别名,column就是别名的值,没别名就是数据库的字段值
- 设置别名,就是防止字段名混淆