MyBatis关联映射:一对多

在实际项目开发中,一对多是非常常见的关系,比如,一个班级可以有多个学生,一个学生只能属于一个班级,班级和学生第一对多的关系,而学生和班级是多对一的关系,数据库中的一对多关系通常使用主外键关联,外键列应该在多方,即多方维护关系.
示例:OneToManyTest
1.建表
2.模型
public class Student implements Serializable
{
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    private Integer clazz_id;

    //无参数构造器
    public Student()
    {
        super();
    }
...
public class Clazz implements Serializable
{
    private Integer id;
    private String code;
    private String name;
    //班级和学生是一对多的关系,即一个班级可以有多个学生
    private List<Student> student;

    //无参数构造器
    public Clazz()
    {
        super();
    }
...
学生和班级第多对一的关系,即一个学生只属于一个班级.在Student类当中定义了一个clazz属性,该属性是一个Clazz类型,用来映射多对一的关联关系,表示该学生所属的班级.
3.映射
ClazzMapper.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="org.fkit.mapper.ClazzMapper">
    <!-- 根据ID查询班级信息 返回resultMap -->
    <select id="selectClazzById" parameterType="int" resultMap="clazzResultMap">
        SELECT * FROM tb_clazz WHERE id=#{id}
    </select>
    <!-- 映射Clazz对象的resultMap -->
    <resultMap type="org.fkit.domain.Clazz" id="clazzResultMap">
        <id property="id" column="id" />
        <result property="code" column="CODE" />
        <result property="name" column="NAME" />
        <!-- 一对多关联映射:collectin fetchType="lazy" 表示懒加载 -->
        <collection property="student" javaType="ArrayList" column="id" ofType="org.fkit.domain.Student" select="org.fkit.mapper.StudentMapper.selectStudentByClazzId" fetchType="eager">
            <id property="id" column="ID" />
            <result property="name" column="NAME" />
            <result property="sex" column="SEX" />
            <result property="age" column="AGE" />
        </collection>
    </resultMap>
</mapper>
ClazzMapper.xml中定义了一个<select…/>,其根据id查询班级信息,由于clazz类除了简单的属性id,code,name外,还有一个关联对象student,所以返回的是一个名为clazzResultMap的resultMap.由于student是一个List集合,所以clazzResultMap中使用了<collection…/>元素映射一对多的关联关系.select属性表示会使用column属性的id值做为参数执行StudentMapper中定义的selectStudentByClazzId查询该班级对应的所有学生数据,查询出的数据将被封装到property表示的Student对象当中.
之外还使用了一个新的属性fetchType,该属性的取值有eager和lazy,eager表示立即加载,即查询Clazz对象的时候,会立即执行关联的selectStudentByClazzId中定义的SQL语句去查询班级的所有学生;lazy表示懒加载,其不会立即发送SQL语句去查询班级的所有学生,而是等到需要使用班级的student属性时才会发送SQL语句去查询班级的所有学生;fetch机制更多的是为了性能考虑,如果查询班级时确定会访问班级的所有学生,则该属性应该设置为eager;如果查询班级时只是查询班级信息,有可能不会访问班级的所有学生,则该属性应该设置为lazy,正常情况下,一对多所关联的集合对象,都应该被设置成lazy.
4.增加配置项
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
lazyLoadingEnabled属性表示延迟加载全局开关,当开启时,所有关联对象都会延迟加载,默认为false
eggressiveLazyLoading属性启用时,会使带有延迟加载属性的对象立即加载,反之每种属性将会按需加载,默认为true
5.映射
studentMapper
<?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="org.fkit.mapper.StudentMapper">
    <!-- 根据ID查询学生信息 多表连接 返回resultMap -->
    <select id="selectStudentById" parameterType="int" resultMap="studentResultMap">
        SELECT * FROM tb_clazz c,tb_student s WHERE c.id=s.clazz_id AND s.id=#{id}
    </select>
    <!-- 根据班级id查询学生信息,返回resultMap -->
    <select id="selectStudentByClazzId" parameterType="int" resultMap="studentResultMap">
        SELECT * FROM tb_student WHERE clazz_id=#{id}
    </select>
    <!-- 映射student对象的resultMap -->
    <resultMap type="org.fkit.domain.Student" id="studentResultMap">
        <id property="id" column="ID" />
        <result property="name" column="NAME" />
        <result property="sex" column="SEX" />
        <result property="age" column="AGE" />
        <result property="clazz_id" column="CLAZZ_ID" />
    </resultMap>
</mapper>
其中定义了一个<select id=”selectStudentById”…/>,其会根据学生id查询学生信息,由于student类除了简单的属性id,name,sex和age之外,还有一个关联对象clazz,所以他返回的是一个名为clazzResultMap的resultMap.
6.映射
public interface ClazzMapper
{
    /**
     * 根据ID查询Clazz
     * 方法名和参数必须与XML文件中的同名<select.../>一致
     * @param id
     * @return Person
     */
    Clazz selectClazzById(Integer id);
}

public interface StudentMapper
{
    /**
     * 根据ID查询Student
     * 方法名和参数必须与XML文件中的同名<select.../>一致
     * @param id
     * @return Person
     */
    Student selectStudentById(Integer id);
}
7.控制器
    @GetMapping("/mysql/OneToManyTest")
    public void OneToManyTest()
    {
        //获取SqlSession实例
        SqlSession sqlSession = FKSqlSessionFactory.getSqlSession();
        //获得ClazzMapper接口的代理对象
        ClazzMapper cm = sqlSession.getMapper(ClazzMapper.class);
        //调用selectClazzById方法
        Clazz clazz = cm.selectClazzById(1);
        //查看查询到的Clazz对象信息
        System.out.println(clazz.getId()+":NAME="+clazz.getName());

        System.out.println(clazz.getStudent());

        sqlSession.commit();
        sqlSession.close();

    }