实体与实体之间的关系有:一对一、一对多、多对多
但是对于某一具体的实例(某个具体的对象),它与别的对象的关系只有一对一和一对多两种,MyBatis 使用的就是这种思想。
首先,建表语句如下(老师与学生,暂且认为老师与学生的关系为一对一):
CREATE TABLE `Student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`tid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `sid_tid_idx` (`tid`),
CONSTRAINT `sid_tid` FOREIGN KEY (`tid`) REFERENCES `Teacher` (`tid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `Teacher` (
`tid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
一、一对一的两种方式:
老师类:(普通配置)
package cn.yufeng.test.model;
public class Teacher {
int id;
String name;
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;
}
}
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">
<mapper namespace="cn.yufeng.test.model.TeacherMapper">
<resultMap type="cn.yufeng.test.model.Teacher" id="teacherMapper">
<id property="id" column="tid" />
<result property="name" column="name" />
</resultMap>
<select id="load" resultMap="teacherMapper" >
select * from Teacher where tid=#{tid}
</select>
</mapper>
学生类:
package cn.yufeng.test.model;
public class Student {
int id;
String name;
Teacher tid;
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 Teacher getTid() {
return tid;
}
public void setTid(Teacher tid) {
this.tid = tid;
}
}
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">
<mapper namespace="cn.yufeng.test.model.StudentMapper">
<resultMap type="cn.yufeng.test.model.Student" id="studentMapper">
<id property="id" column="id" />
<result property="name" column="name" />
<association column="tid" property="tid" javaType="cn.yufeng.test.model.Teacher" select="cn.yufeng.test.model.TeacherMapper.load" ></association>
</resultMap>
</mapper>
StudentMapper.java
package cn.yufeng.test.model;
import java.util.List;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Select;
public interface StudentMapper {
@Select("select * from Student")
@ResultMap("studentMapper")
public List<Student> list();
}
(1)嵌套查询(不推荐,因为会再次查询数据库):
在StudentMapper中配置resultMap,其中使用association
<resultMap type="cn.yufeng.test.model.Student" id="studentMapper">
<id property="id" column="id" />
<result property="name" column="name" />
<association column="tid" property="tid" javaType="cn.yufeng.test.model.Teacher" select="cn.yufeng.test.model.TeacherMapper.load" ></association>
</resultMap>
注:
1.column 为数据库中列的名字,property为类中属性的名字,javaType说明属性类型,
2.select指定选择器(即mapper.xml 中select 的ID)
3.在这里,先查询Student会查出tid这一列,然后建立再通过cn.yufeng.test.model.TeacherMapper.load的select进行查找,将结果注入到属性tid中
4.在这里数据库表中Student中有tid这一列,当没有这一列(如通过老师找学生),就需要使用连接表( join 或者 作笛卡尔积 )产生关联列
(2)结果集映射:
这种方式只是一般的映射,将查询到的结果集映射到对应的属性 , 当出现重复列名只会选第一次的列。
在原来的基础上,为教师添加学生属性,假设现在学生与教师是一对一关联。
package cn.yufeng.test.model;
public class Teacher {
int id;
String name;
Student student;
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 Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + ", student=" + student + "]";
}
}
查询:
@Select("select t.tid,t.name tname,s.id,s.name from teacher t left join student s on s.tid=t.tid where t.tid=#{id} ")
@ResultMap("withStudent")
public Teacher load(int id);
结果集映射:
<resultMap type="cn.yufeng.test.model.Teacher" id="withStudent" >
<id property="id" column="tid" />
<result property="name" column="tname" />
<association property="student" javaType="cn.yufeng.test.model.Student" >
<id property="id" column="id" />
<result property="name" column="name" />
</association>
</resultMap>
两种方式对比:
1.方式1可以通过使用延迟加载,通过增加查询数据库的次数减少传输数据的总量
2.方式2要求不出现重名的列(出现重名只取第一列),自己写查询语句,一次性查询所有的数据,不管数据在之后是否需要(没使用延迟加载),传输的数据量>=方式1,访问数据库次数小于等于方式1
推荐使用方式2,具体情况情况可以具体处理
二、一对多的两种方式
1.直接关联
之前老师与学生的关系并不太恰当,应该是一对多,在多的一方(学生)不需要再修改,因为一个学生对应一个老师,在一的一方(教师)需要有所修改:
首先修改属性,添加学生属性集合:
public class Teacher {
int id;
String name;
Student student;//为了测试
List<Student> students;
}
修改映射文件,添加Collection:
<resultMap type="cn.yufeng.test.model.Teacher" id="withStudent" >
<id property="id" column="tid" />
<result property="name" column="tname" />
<association property="student" javaType="cn.yufeng.test.model.Student" >
<id property="id" column="id" />
<result property="name" column="name" />
</association>
<collection property="students" javaType="ArrayList" ofType="cn.yufeng.test.model.Student" >
<id property="id" column="id" />
<result property="name" column="name" />
</collection>
</resultMap>
注:
a.association与collection是有顺序的,必须association在前
b.collection中property为类中属性名,javaType为集合类型,ofType为集合中每个元素的类型其他配合association一致,注意事项也一致
如果使用嵌套查询,配置方法如下:
<resultMap type="cn.yufeng.test.model.Teacher" id="withStudent2" >
<id property="id" column="tid" />
<result property="name" column="tname" />
<collection property="students" javaType="ArrayList" ofType="cn.yufeng.test.model.Student" column="tid" select="cn.yufeng.test.model.StudentMapper.selectForTeacher" >
</collection>
</resultMap>
<select id="get" resultMap="withStudent2" >
select * from teacher t where t.tid = #{id}
</select>
StudentMapper.xml中添加
<select id="selectForTeacher" resultMap="studentMapper">
select * from student where tid=#{tid}
</select>
即通过老师ID查找学生
2.间接关联
这种方式一般用于多对多的情况两张表需要借助第三张表来实现关联,使用的还是1的方法,两张表分别对第三张表使用一对多映射即可