版权声明:版权声明:尊重他人劳动成果,转载请注明出处:
http://blog.csdn.net/czd3355/article/details/72599681
本文主要介绍在级联操作中 association,collection 的使用。如果对最基本的级联操作还不是很了解的话,可以参考这篇文章。mybatis 学习记录(4.1)—— 级联操作(无 association 和 collection)
bean 之间的关系:
一名学生对应一门课程成绩(stu 表和 one_grade 表)
一名班主任可以有多名学生(stu 表和 teacher 表)
public class Student {
private Integer id;
private String name;
private Teacher teacher;
// 省略 get/set 方法
}
public class Grade {
private int id;
private int grade;
private Student student;
// 省略 get/set 方法
public class Teacher {
private String name;
private Integer id;
private List studentList;
// 省略 get/set 方法
}
项目准备已经完成了,现在开始进入正题 —— 级联操作。
首先我们先来看下 association 和 collection 的应用场景分别是什么样的,如下所示。每种关联都可以有两种方式去实现。
注意:在使用 association 时,property 属性值是一个 POJO 对象。而在使用 collection 时,property 属性值是一个 List 集合
每一种操作方法我都写上了对应的关键代码,下面我们来具体看一下这两种操作的用法。
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());
}
"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 的使用吧。
--
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()+", ");
}
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 问题)
水平有限,如有疏漏错误之处,还望不吝赐教,共勉。