前言
在实际开发中我们的数据表都是有关联关系的,我们查询也都是几张表联合起来进行查询的。例如,订单表和用户表,员工表和部门表等等吧,总之十分的重要,我们在进行多表查询的时候会介绍两种方式:联合查询和分表查询
一、多对一
1.联合查询
模拟多个员工对应一个部门的情况。
1.创建表:
2.实体类:
//员工实体类
public class Employee {
private Integer id;
private String name;
private String gender;
private Integer age;
private Department department;
}
//部门实体类
public class Department {
private Integer id;
private String name;
}
3.Mapper接口
public interface EmployMapper {
//根据员工编号获取员工信息和员工部门
public Employee getEmployeeById(Integer id);
}
4.映射文件:
<select id="getEmployeeById" resultMap="resultEmployMap" >
select e.*,d.* from employee e,department d where e_depart_id=d_id and e_id=#{id}
</select>
<resultMap id="resultEmployMap" type="Employee">
<id column="e_id" property="id"></id>
<result column="e_name" property="name"></result>
<result column="e_gender" property="gender"></result>
<result column="e_age" property="age"></result>
<association property="department" javaType="Department">
<id column="d_id" property="id"></id>
<result column="d_name" property="name"></result>
</association>
</resultMap>
</mapper>
简单解释:联合查询就是将表链接起来,使用约束条件进行筛选符合
条件的结果集。其中因为有复杂字段的参与,使用特殊的标签的进行
association表示联合。property表示复杂属性的属性名,javaType
表示负责属性的属性类型。其中的id和result就是复杂属性的属性和
数据表中的字段进行一一对应的关系。
2.分表查询
分表查询的思路就是我们要找员工的部门信息,首先就是要找到员工的部门编号,在使用这个员工编号去查询对应的部门信息,并将部门信息存储在员工类中的复杂属性中去。
首先我们要知道分表查询对应的sql语句是两条,那么就需要我们写两个接口,对应两个映射文件
1.Mapper接口
//员工接口
public interface EmployeeMapper{
//根据员工编号获取员工信息和员工部门
public Employee getEmployeeById(Integer id);
}
//部门接口
public interface DepartMapper{
//根据部门id找到对应的部门
public Department getDepartmentByIdT(Integer id);
}
2.映射文件:
Employee的映射文件
<select id="getEmployeeById" resultMap="huhuuhu" >
select e.*from employee e where e_id=#{id}
</select>
<resultMap id="huhuuhu" type="Employee">
<id column="e_id" property="id"></id>
<result column="e_name" property="name"></result>
<result column="e_gender" property="gender"></result>
<result column="e_age" property="age"></result>
<association property="department" javaType="Department"
column="e_depart_id" select="com.offcn.mapper.DepartMapper.getDepartmentByIdT"
>
</association>
</resultMap>
association 用于映射多对一关系对象的,
Property 关联对象的复杂属性
javaType: 复杂属性的类型
Column: 取结果集中指定类的值,作为 select 查询的输入值
Select: 指定另一次查询,使用全限名
DepertMapper的映射文件
<select id="getDepartmentByIdT" resultMap="DepartmentByIdTW">
select * from department where d_id=#{id}
</select>
<resultMap id="DepartmentByIdTW" type="Department">
<id column="d_id" property="id"></id>
<result column="d_name" property="name"></result>
</resultMap>
两种方式的比较:
查看生成的sql语句:
联合查询
分表查询
分表查询步骤繁琐,但是在效率上会更高一下!
二、一对多
这个在实际开发中也是比较使用的例如:一个部门可以对应多个员工,一个用可以拥有多个订单等……
1.联合查询
我们在定义实体类的时候,通过一个集合来代表实体类的中所对应的对象。这时候我们要用到collection标签。
数据表依旧采用员工部门表
1.对应的实体类
//员工类
public class Employee{
private Integer id;
private String name;
private String gender;
private Integer age;
}
//部门类
public class Department{
private Integer id;
private String name;
//这是使用集合的形式,将部门对应的多个员工进行存储。来实现一对多
public List<Employee> emps;
}
2.Mpper接口
public interface DepartMapper{
//根据部门Id找到对应的员工信息
public Department getDepartmentByDid(Integer did);
}
3.对应的xml映射
<select id="getDepartmentByDid" resultMap="OneToMang">
select e.*,d.* from department d,employee e where e.e_depart_id=d.d_id and d.d_id=#{did}
</select>
<resultMap id="OneToMang" type="Department">
<id column="d_id" property="id"></id>
<result column="d_name" property="name"></result>
<collection property="emps" ofType="Employee" >
<id column="e_id" property="id"></id>
<result column="e_name" property="name"></result>
<result column="e_gender" property="gender"></result>
<result column="e_age" property="age"></result>
</collection>
</resultMap>
</mapper>
collection:表示映射的是复杂属性集合
property:对应复杂属性的属性名
ofType:集合中的元素的类型
其中的id和result就表示映射的关联,column数据表中的字段,property就是映射的属性名(实体类中的属性名)column和property是一一对应的关系
3.测试类:
@Test
public void show16() {
SqlSession session = MyBatisUtils.getSession();
DepartMapper mapper = session.getMapper(DepartMapper.class);
Department depart = mapper.getDepartmentByDid(1);
List<Employee> emps = depart.getEmps();
for (Employee emp : emps) {
System.out.println(depart.getName()+"=="+emp);
}
MyBatisUtils.closeSession(session);
}
看一下生成的sql语句:
2.分表查询
分表查询的思路和上边多对一查询思路是一样的,但是就是标签使用的是不一样的,一个对象的话使用的association而一个集合的话使用的collection.
1.各自对应的Mapper接口
//部门接口
public interface DepartMapper{
public Department getDepartmentByIdTW(Integer id);
}
//员工接口
public interface EmployMapper{
public Employee getEmployeeById(Integer id);
}
2.主要是映射文件!!
DepartMapper对应的映射文件
<select id="getDepartmentByIdTW" resultMap="DepartmentByIdTW">
select * from department where d_id=#{id}
</select>
<resultMap id="DepartmentByIdTW" type="Department">
<id column="d_id" property="id"></id>
<result column="d_name" property="name"></result>
</resultMap>
<collection property="emps" ofType="Employee" column="d_id"
select="com.offcn.mapper.EmployMapper.getEmployeeById"
>
</collection>
EmployMapper对应的映射文件
<select id="getEmployeeById" resultMap="hahaha" >
select e.* from employee e where e_depart_id=#{id}
</select>
<resultMap id="hahaha" type="Employee">
<id column="e_id" property="id"></id>
<result column="e_name" property="name"></result>
<result column="e_gender" property="gender"></result>
<result column="e_age" property="age"></result>
</resultMap>
collection 用于映射一对多关系对象的,
Property 关联对象的复杂属性名
ofType: 集合中元素的类型
Column: 取结果集中指定列的值,作为 select 查询的输入值
Select: 指定另一次查询,使用全限名
测试类和上述一模一样,但是看一下查询的sql语句
此时有两条sql语句。这种方式更符合我们的思维习惯,查出一个结果作为另一个结果的输入,然后查询想要的结果。
三、多对多
举例:一个老师对应多个学生,一个学生可以有多个老师。
表间关系:
1.联合查询
对应的实体类:
//学生类
public class Teacher {
private Integer id;
private String name;
private List<StudentTeacher> studentTeacherList;
}
//中间表
public class StudentTeacher {
private Integer sid;
private Integer tid;
private Student student;
private Teacher teacher;
}
//教师类
public class Teacher {
private Integer id;
private String name;
private List<StudentTeacher> studentTeacherList;
}
需求:通过教师id找到对应的学生
<select id="getStudentByTid" resultMap="StudentResultMap">
select t.*,s.*,st.* from teacher t,student s,student_teacher st
where s.s_id = st.st_sid and st.st_tid=t.t_id and t.t_id = #{id}
</select>
<resultMap id="StudentResultMap" type="Teacher">
<id column="t_id" property="id"></id>
<result column="t_name" property="name"></result>
<collection property="studentTeacherList" ofType="StudentTeacher">
<result column="st_sid" property="sid"></result>
<result column="st_tid" property="tid"></result>
<association property="student" javaType="Student">
<id column="s_id" property="id"></id>
<result column="s_name" property="name"></result>
</association>
</collection>
</resultMap>
注意:此时我的中间表是使用两个字段作为主键的那么在映射的时候就不能使用id这个标签进行映射,要使用result标签
多对多查询可以看作是多对一和一对多的联合使用,我们可以看到collection标签里边嵌套了association标签。
测试类:
@Test
public void show17() {
SqlSession session = MyBatisUtils.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getStudentByTid(2);
for (StudentTeacher studentTeacher : teacher.getStudentTeacherList()) {
System.out.println("教师姓名:"+teacher.getName()+"==="+"学生姓名:"+studentTeacher.getStudent().getName());
}
MyBatisUtils.closeSession(session);
}
结果:
2.分表查询
1.Mapper接口
//教师实体类对应的接口
public interface TeacherMapper {
public Teacher getTeachersByTid(Integer tid);
}
//中间表对应的接口
public interface StudentTeacherMapper {
//返回学生对应的集合
public List<StudentTeacher> getStudentAndTeacher(Integer id);
}
//StudentMapper接口
public interface StudentMapper {
//根据学生id获取学生信息
public Student getStudentsBySid(Integer sid);
}
2.映射文件
<select id="getTeachersByTid" resultMap="TeacherResult">
select * from teacher where t_id = #{id}
</select>
<resultMap id="TeacherResult" type="Teacher">
<id column="t_id" property="id"></id>
<result column="t_name" property="name"></result>
<collection property="studentTeacherList" ofType="StudentTeacher"
column="t_id" select="com.offcn.mapper.StudentTeacherMapper.getStudentAndTeacher"
>
</collection>
</resultMap>
<select id="getStudentAndTeacher" resultMap="StudentTeacher2">
select * from student_teacher where st_tid=#{sid}
</select>
<resultMap id="StudentTeacher2" type="StudentTeacher">
<result column="st_sid" property="sid"></result>
<result column="st_did" property="did"></result>
<association property="student" javaType="Student"
column="st_sid" select="com.offcn.mapper.StudentMapper.getStudentsBySid"
>
</association>
</resultMap>
<select id="getStudentsBySid" resultMap="StudentreResultMap">
select * from student where s_id = #{id}
</select>
<resultMap id="StudentreResultMap" type="Student">
<id column="s_id" property="id"></id>
<result column="s_name" property="name"></result>
</resultMap>
测试类:
@Test
public void show18() {
SqlSession session = MyBatisUtils.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeachersByTid(1);
for (StudentTeacher studentTeacher : teacher.getStudentTeacherList()) {
System.out.println("教师姓名:"+teacher.getName()+"==="+"学生姓名:"+studentTeacher.getStudent().getName());
}
MyBatisUtils.closeSession(session);
}
}
结果:
我们可以看到是三条sql语句
四、一对一
现实生活中还有一种情况就是一一对应的关系,即一对一关系,例如身份证号和人。一对一的关系使用起来相对简单,写的时候就简写了……
1.联表查询
数据表准备:
1.实体类:
//Person类
public class Person{
private Integer id;
private String name;
private IdCard idCard;
}
//Idcard类
public class IdCard{
private int id;
private String card;
private Person person;
}
2.Mapper接口
public interface PersonMapper {
//通过个人id找到对应的身份证号
public Person getPersonByCard(Integer id);
}
3.映射文件
<select id="getPersonByCard" resultMap="PersonMapperResult">
SELECT p.*,c.* FROM person p,idcard c WHERE c.c_person_id=p.p_id AND p.p_id = #{id};
</select>
<resultMap id="PersonMapperResult" type="Person">
<id column="p_id" property="id"></id>
<result column="p_name" property="name"></result>
<association property="idCard" javaType="IdCard">
<id column="c_cardno" property="card"></id>
<result column="c_person_id" property="id"></result>
</association>
</resultMap>
测试类:
@Test
public void show5() {
SqlSession session = MyBatisUtils.getSession();
PersonMapper mapper = session.getMapper(PersonMapper.class);
Person person = mapper.getPersonByCard(1);
System.out.println(person.getName() + "=" + person.getIdCard().getCard());
}
结果:
2.分表查询
1.接口
public interface PersonMapper {
//通过人id找到身份账号
public Person getPersonByCardT(Integer id);
}
2.Mapper接口的映射文件
<select id="getPersonByCardT" resultMap="PersonByCardTResult">
select * from person where p_id=#{id}
</select>
<resultMap id="PersonByCardTResult" type="Person">
<id column="p_id" property="id"></id>
<result column="p_name" property="name"></result>
<association property="idCard" javaType="IdCard" column="p_id"
select="com.offcn.mapper.IdCardMapper.getIdCardById">
</association>
</resultMap>
下一次查询
<select id="getIdCardById" resultMap="IdCardByIdResult">
select * from idcard where c_person_id = #{id}
</select>
<resultMap id="IdCardByIdResult" type="IdCard">
<id column="c_cardno" property="card"></id>
<result column="c_person_id" property="id"></result>
</resultMap>
测试类和上述一致
结果:
总结
虽然MyBatis提供的关联查询功能比较强大,但是还是建议使用单表查询,然后将结果进行组装(上述介绍的分表查询,写多条sql语句而不仅仅是一条),这样可以提高查询效率的。