多对一和一对多的处理
1、环境搭建
一对多和多对一的理解是相互的,譬如:一个班级里有一个班主任,一个班主任带多个学生:在学生的角度而言:为多个学生对应一个老师:即多对一;在老师的角度而言:为一个老师对应多个学生:即一对多。
环境搭建步骤:
- 创建数据库表:student表和teacher表,并插入数据,对应的sql语句如下:
# 创建teacher表 字段包括id和name
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
#插入数据
INSERT INTO teacher(`id`, `name`) VALUES (1, '王老师');
# 创建student表,id,name和tid(student和teacher表进行关联,teacher表中的id作为student表中tid的外键)
CREATE TABLE `student` (
`id` INT(20) NOT NULL,
`name` VARCHAR(50) DEFAULT NULL,
`tid` INT(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
#给student表插入数据
INSERT INTO `student` (`id`, `name`, `tid`) VALUES
('1', '张三', '1'),
('2', '李四', '1'),
('3', '王五', '1'),
('4', '赵六', '1'),
('5', '田七', '1');
- 创建maven的子模块:在pom文件中引入Lombok的maven依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
- 新建student和teacher实体类
package com.kevin.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : kevin ding
* @date : 2022/1/15 15:08
* @description : 对应于数据库表student的实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
// 关联老师(直接关联老师的对象)
private Teacher teacher;
}
package com.kevin.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : kevin ding
* @date : 2022/1/15 15:07
* @description : 对应数据库中的实体类Teacher
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
- 编写实体类对应的StudentMapper,TeacherMapper接口和对应的StudentMapper.xml,TeacherMapper.xml
StudentMapper接口:
package com.kevin.dao;
/**
* @author : kevin ding
* @date : 2022/1/15 15:06
* @description :
*/
public interface StudentMapper {
}
TeacherMapper接口:
package com.kevin.dao;
/**
* @author : kevin ding
* @date : 2022/1/15 15:06
* @description :
*/
public interface TeacherMapper {
}
StudentMapper.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.xml与那个接口相关联(类似于UserDaoImpl中的implement实现哪个接口)-->
<mapper namespace="com.kevin.dao.StudentMapper">
</mapper>
TeacherMapper.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.xml与那个接口相关联(类似于UserDaoImpl中的implement实现哪个接口)-->
<mapper namespace="com.kevin.dao.TeacherMapper">
</mapper>
- 测试搭建环境是否能够运行:
- 直接通过注解方式,在TeacherMapper中编写对应的方法:
package com.kevin.dao;
import com.kevin.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* @author : kevin ding
* @date : 2022/1/15 15:06
* @description :
*/
public interface TeacherMapper {
@Select("select * from teacher where id = #{tid}")
Teacher getTeacher(@Param("tid") int id);
}
测试类:
package com.kevin.dao;
import com.kevin.pojo.Teacher;
import com.kevin.utils.MybatisUtils;
import com.mysql.cj.jdbc.MysqlDataSource;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
/**
* @author : kevin ding
* @date : 2022/1/15 15:15
* @description :
*/
public class MyTest {
@Test
public void test1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
sqlSession.close();
}
}
成功运行,项目环境搭建完毕,下面进行多对一和一对多的学习!
2、 多对一处理:
多对一的查询方法有两种:
- 按查询嵌套
- 按结果嵌套
2.1 按查询嵌套
多对一的查询:以查询全部学生信息为例:
在原始的查询方法中:
- StudentMapper接口中编写抽象方法:
// 查询所有学生信息
List<Student> getAllStudents();
- 创建StudentMapper.xml中编写select的sql语句:
<select id="getAllStudents" resultType="Student">
select * from student;
</select>
- 测试:
@Test
public void getAllStudents(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> allStudentsList = mapper.getAllStudents();
for (Student student : allStudentsList) {
System.out.println(student);
}
}
- 查询结果显示:数据中的teacher字段为null
当数据中出现其他引用对象时,需要进行关联属性:
- association: 关联对象属性类型
- collections:关联集合类型
按查询嵌套处理方法:(主要是对StudentMapper.xml进行修改)
- 先查询student表中的所有信息
- 对于student表的结果进行映射
- 根据student表中的tid去查询对应teacher表中的id值
编写的StudentMapper.xml中:
<!--1. 查询学生表-->
<select id="getAllStudents" resultMap="StudentAndTeacher">
select * from student;
</select>
<!--2. 对应的resultMap 类型为Student,进行student的结果集映射-->
<resultMap id="StudentAndTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--association关联属性 property属性名 column为在student表中的列名 javaType属性类型 select为根据tid要查询的语句-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeachers"/>
</resultMap>
<!--3. 根据查询学生表所得的tid,去老师表中查询对应的信息-->
<select id="getTeachers" resultType="Teacher">
select * from teacher where id = #{id};
</select>
查询结果:成功将teacher属性以对象的形式显示
2.2 按结果嵌套
按结果嵌套处理方法步骤:
- 首先编写连表查询的sql语句,将正确的需求查询出来
- 再根据查询的结果,对结果集通过ResultMap进行映射
- Mapper接口中添加查询所有学生信息的抽象方法:
// 第二种方法 按结果嵌套
List<Student> getAllStudents2();
- StudentMapper.xml中编写对应的sql查询和结果集映射
<!--方法2:(目前项目中较为常见的方法)按结果嵌套查询, 先通过sql连表查询出所需要的结果,再对结果进行映射-->
<select id="getAllStudents2" resultMap="StudentAndTeacher2">
select s.id as sid, s.name as sname, t.name as tname
from student as s, teacher as t
where s.tid = t.id
</select>
<!--对getAllStudents2的查询结果ResultMap进行结果集映射,类型为Student类型-->
<resultMap id="StudentAndTeacher2" type="Student">
<!--column中的值为查询语句中对列起的别名-->
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性 javaType为对应的java类型-->
<association property="teacher" javaType="Teacher">
<!--在association标签内部 再将其他属性和字段名进行对应-->
<result property="name" column="tname"/>
</association>
</resultMap>
- 测试:
@Test
public void getAllStudents2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> allStudentsList = mapper.getAllStudents2();
for (Student student : allStudentsList) {
System.out.println(student);
}
}
- 运行结果显示:
3、一对多处理:
同理,对于Teacher而言,一个老师带有多个学生,即一对多。
- 搭建环境:创建一个maven模块:于多对一的环境几乎一致,改动地方为:
对于Teacher和Student两个实体类进行改动:
Teacher类:一个老师关联多个学生
package com.kevin.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author : kevin ding
* @date : 2022/1/15 17:23
* @description : 对应数据库中的实体类Teacher
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
// 一个老师下面有多个学生,需要关联学生列表
private List<Student> students;
}
Student类中,一个学生对应一个老师,直接写属性就行
package com.kevin.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : kevin ding
* @date : 2022/1/15 17:20
* @description : 对应于数据库表student的实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
// 每个学生只有一个老师,只需写其tid属性
private int tid;
}
与多对一相同,一对多也有两种处理方法:
- 按查询嵌套处理
- 按结果嵌套处理
3.1 按查询嵌套处理
- TeacherMapper接口编写抽象方法:查询指定老师的信息:
// 第一种 按照查询进行处理
Teacher getTeacher1(@Param("id") int id);
- TeacherMapper.xml编写sql语句:
<!--1. 查询指定id的老师信息-->
<select id="getTeacher1" resultMap="TeacherAndStudent1">
select *
from teacher
where id = #{id};
</select>
<!--2. 结果集映射-->
<resultMap id="TeacherAndStudent1" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--collection中需要填写属性students的类型javaType:list, 并且list中的泛型也要表明:ofType表示 select标签表示要去执行哪个查询方法,column中的值为老师的id,并将其传给select标签中查询所需要的字段值-->
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentsByTeacherId" column="id"/>
</resultMap>
<!--3.根据Id查询学生信息-->
<select id="getStudentsByTeacherId" resultType="Student">
select *
from student
where tid = #{tid};
</select>
- 测试类编写:
@Test
public void getTeacher1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher1(1);
System.out.println(teacher);
sqlSession.close();
}
4. 结果:
![请添加图片描述](https://img-blog.csdnimg.cn/a2162fdf81fc44cc84af36c0be9b6d5d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAS2V2aW4tRGluZw==,size_20,color_FFFFFF,t_70,g_se,x_16)
### 3.2 按结果嵌套处理
1. TeacherMapper接口编写抽象方法:查询指定老师的信息:
```java
// 第二种 按照结果进行嵌套处理
Teacher getTeacher2(@Param("id") int id);
- TeacherMapper.xml编写sql语句:
<!--根据结果进行嵌套查询-->
<!--1. 首先连表查询出所需要的结果-->
<select id="getTeacher2" resultMap="TeacherAndStudent2">
select s.id as sid, s.name as sname, t.id as tid, t.name as tname
from student as s, teacher as t
where s.tid = t.id and t.id = #{id}
</select>
<!--进行结果集映射
对于集合的话,使用collection!
collection标签中的JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型
ofType指定的是映射到list集合属性中pojo的类型。
-->
<resultMap id="TeacherAndStudent2" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<!--collection中将student的属性映射出来-->
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
- 测试类编写:
@Test
public void getTeacher2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher);
sqlSession.close();
}
- 运行结果:
4、总结
- 结果集映射中,关联属性-association,关联集合-collection
- association是用于一对一和多对一,而collection是用于一对多的关系
- javaType和ofType都是用来指定对象类型的,javaType是用来指定pojo中属性的类型,ofType指定的是映射到list集合属性中pojo的类型。
需要注意的几点:
-
保证SQL的可读性,尽量通俗易懂
-
根据实际要求,尽量编写性能更高的SQL语句
-
注意属性名和字段不一致的问题
-
注意一对多和多对一 中:字段和属性对应的问题
-
尽量使用Log4j,通过日志来查看自己的错误