mybatis 学习记录(4.2)—— 级联查询(有 association 和 collection)

版权声明:版权声明:尊重他人劳动成果,转载请注明出处:
http://blog.csdn.net/czd3355/article/details/72599681

1. 项目介绍

本文主要介绍在级联操作中 association,collection 的使用。如果对最基本的级联操作还不是很了解的话,可以参考这篇文章。mybatis 学习记录(4.1)—— 级联操作(无 association 和 collection)

1.1 数据库关系:

  • one_grade 表有 id,grade,s_id 字段.其中 s_id 为外键字段。

这里写图片描述

  • stu 表有 id,name,t_id 字段。其中 t_id 为外键字段。

这里写图片描述

  • teacher 表有 id,name 字段

这里写图片描述

1.2 bean 代码

bean 之间的关系:
一名学生对应一门课程成绩(stu 表和 one_grade 表)
一名班主任可以有多名学生(stu 表和 teacher 表)

  • Student.java
public class Student {
    private Integer id;
    private String name;
    private Teacher teacher;
    // 省略 get/set 方法
}
  • Grade.java
public class Grade {
    private int id;
    private int grade;
    private Student student;
     // 省略 get/set 方法
  • Teacher.java
public class Teacher {
    private String name;
    private Integer id;
    private List studentList;
    // 省略 get/set 方法
}

项目准备已经完成了,现在开始进入正题 —— 级联操作。

2. mybatis 级联操作

首先我们先来看下 association 和 collection 的应用场景分别是什么样的,如下所示。每种关联都可以有两种方式去实现。

  • association:一对一的关联
    • 关联结果查询(resultMap)
    • 关联嵌套查询(select)
  • collection:一对多的关联
    • 关联结果查询(resultMap)
    • 关联嵌套查询(select)

注意:在使用 association 时,property 属性值是一个 POJO 对象。而在使用 collection 时,property 属性值是一个 List 集合

每一种操作方法我都写上了对应的关键代码,下面我们来具体看一下这两种操作的用法。

3. association 的使用

3.1 关联结果查询(resultMap)

  • 映射文件(gradeMapper.xml)
id="gradeMap" type="com.czd.mybatis02.bean.Grade">
    <id property="id" column="g_id"/>
    <result property="grade" column="grade"/>
    property="student" resultMap="stuMap">
    


id="stuMap" type="com.czd.mybatis02.bean.Student">
    <id property="id" column="id"/>
    <result property="name" column="name"/>



  • 测试类
/**
 * 根据学生名字,查询该学生的课程成绩
 *
 * @param name 学生名字
 * @throws Exception
 */
public Grade testFindGradeByStuName(String name) throws Exception {
    SqlSession sqlSession = getSession().openSession();
    Grade grade = sqlSession.selectOne(gradeDaoNameSpace + ".testFindGradeByStuName", name);
    sqlSession.close();
    return grade;
}

@Test
public void main() throws Exception {
    OneToManyDao oneToManyDao = new OneToManyDao();

    /*
    * 输出结果:
    * 该学生名字是 czd2,其成绩是 88
    * */
    Grade grade = oneToManyDao.testFindGradeByStuName("czd2");
    System.out.println("该学生名字是 " + grade.getStudent().getName() +
        ",其成绩是 " + grade.getGrade());
}

3.2 关联嵌套查询(select)

  • 测试类,输出结果均同上
  • 映射文件(gradeMapper.xml)
"gradeMap" type="com.czd.mybatis02.bean.Grade">
    property="id" column="g_id"/>
    property="grade" column="grade"/>
    property="student" column="g_id" select="selectStu">
    


<select id="selectStu" resultType="com.czd.mybatis02.bean.Student">
      SELECT s.id,s.name
      FROM stu s
      WHERE s.id = #{g_id}
select>

<select id="testFindGradeByStuName" parameterType="string" resultMap="gradeMap">
    SELECT s.id ,s.name,g.id g_id,g.grade
    FROM stu s INNER JOIN one_grade g
    ON g.s_id = s.id
    WHERE s.name = #{name}
select>

属性解释:
中。
column="g_id" 表示将 column 的值(g_id)作为参数,传递给 id 为 selectStu 的 select 标签,即为标签中的 #{g_id}。并且程序此时执行标签内容。
介绍完了 association,趁热打铁,我们来看下 collection 的使用吧。

4. collection 的使用

4.1 关联结果查询(resultMap)

  • 映射文件(teacherMapper.xml)
-- 
id="teacherMap" type="com.czd.mybatis02.bean.Teacher">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    property="studentList" javaType="ArrayList"
                 ofType="com.czd.mybatis02.bean.Student" resultMap="stuMap">
    


id="stuMap" type="com.czd.mybatis02.bean.Student">
    <id property="id" column="s_id"/>
    <result property="name" column="s_name"/>

 -->

-- 说明:上面两段代码等价于下面这一段代码。-->
--    唯一的不同点在于上面的代码将 collection 连接的 bean 封装起来了。-->
--    如果实际需求中需要重复使用该 bean 的话,推荐使用上面的代码 -->
id="teacherMap" type="com.czd.mybatis02.bean.Teacher">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    property="studentList" javaType="ArrayList"
                ofType="com.czd.mybatis02.bean.Student">
        <id property="id" column="s_id"/>
        <result property="name" column="s_name"/>
    


属性解释:
1. ofType 属性表示 studentList 集合元素的类型。
2. 这里考虑到可能有部分读者对上面的 SQL 不大理解,我在这里也稍作解释一下:
3. 首先先解释 where t.id in (...) AND t.id=s.t_id 这段 SQL。这段 SQL 执行后便形成了 t.id in (1) AND t.id=s.t_id 。之后它放在 where 后表示待会的查询结果范围是在 s 表 t.id in (1) AND t.id = s.t_id 中的。
4. 所以整句话的意思是,使用笛卡尔积,将 t.id in (1) AND t.id=s.t_id 的 s 表数据以及连接到的 t 表数据查询出来
5. 笛卡尔积是什么意思呢?其实一句话就是查询 s2 表的每行数据以及对应 t2 表的每行数据
假设集合A={a, b},集合B={0, 1, 2},
则两个集合的笛卡尔积 A×B = {(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。一共 2 X 3 = 6 个结果

另外这里有个坑:SELECT t.id,t.name,s.name s_name,s.id s_id以及 对应的 column 属性值也改成了别名。
这里我们为什么要给表字段取别名呢?
因为两张表有同名字段,所以其中一张表取别名避免同名冲突,否则数据会映射错误。

  • 测试类
/**
 * 根据学生名字,查询该学生的班主任和该班主任的其他学生名字
 * @param name 学生名字
 */
 public Teacher testFindByStuName(String name) throws Exception {
        SqlSession sqlSession = getSession().openSession();
        Teacher teacher = sqlSession.selectOne(teacherDaoNameSpace + ".testFindByStuName", name);
        sqlSession.close();
        return teacher;
}

@Test
public void main() throws Exception {
    OneToManyDao oneToManyDao = new OneToManyDao();
    /*
    * 查询输出结果:
    * teacherName:t1
    * t1班主任有2名学生,他们的名字分别是:czd1, czd2,
    * */
    Teacher teacher = oneToManyDao.testFindByStuName("czd1");
    System.out.println("teacherName:" + teacher.getName());
    int num = teacher.getStudentList().size();
    System.out.print("t1老师有" + num + "名学生,他们的名字分别是:");
    for (Student student : teacher.getStudentList()) {
        System.out.print(student.getName()+", ");
}

4.2 关联嵌套查询(select)

  • 映射文件(teacherMapper.xml)
id="teacherMap" type="com.czd.mybatis02.bean.Teacher">
    <id property="id" column="t_id"/>
    <result property="name" column="t_name"/>
    property="studentList" javaType="ArrayList"
                column="name" select="selectStu">
    





  • 测试类
public List testFindByStuName02(String name) throws Exception {
    SqlSession sqlSession = getSession().openSession();
    List teacherList = sqlSession.selectList(teacherDaoNameSpace + ".testFindByStuName", name);
    sqlSession.close();
    return teacherList;
}

@Test
public void main() throws Exception {
    OneToManyDao oneToManyDao = new OneToManyDao();
    /*
    * 输出结果:
    * t1 的学生名字是 czd1
    * t1 的学生名字是 czd2
    * Teacher{name='t1', id=1, studentList=[Student{id=1, name='czd1', teacher=null}]}
    * Teacher{name='t1', id=1, studentList=[Student{id=2, name='czd2', teacher=null}]}
    * */
    List teacherList = oneToManyDao.testFindByStuName02("czd1");
    for (int i = 0; i < teacherList.size(); i++) {
        Teacher teacher = teacherList.get(i);
        System.out.println(teacher.toString());
        System.out.print(teacher.getName() + " 的学生名字是 ");
        for (Student student : teacher.getStudentList()) {
            System.out.println(student.getName());
        }
    }
}

虽然两者都能达到同样的目的,但其中还是稍有区别的:
1. 便捷性:关联结果查询(resultMap)劣于关联嵌套查询(select)
2. 效率:关联结果查询(resultMap)优于关联嵌套查询(select)——因为后者会执行关联 SQL,增加一定的查询(即 N+1 问题)

水平有限,如有疏漏错误之处,还望不吝赐教,共勉。

你可能感兴趣的:(javaweb,mybatis,Mybatis,学习笔记)