MyBatis系列的上一篇博客发表时,笑笑还没有出生。转眼间八个月过去了,他已经是个大宝宝了。这么长时间未更新MyBatis系列的博客,想来真是罪过。不过有了宝宝之后,的确会分散自己很大一部分精力。
今天的示例是多对多关联的查询,这是在上一篇博客(MyBatis多对多保存示例)的基础上完成的,仍然是处理学生与课程之间的多对多关联(一个学生可以选修多门课程,一门课程可以被多个学生选修),相关的实体类和表结构信息请参考上篇博客。
从本篇博客起,示例工程就不再用ant组织,而改用eclipse(示例工程源码及数据库脚本下载地址:http://down.51cto.com/data/1143560)。
首先实现学生端功能,即根据id查询出学生及其选修的课程。步骤如下:
1、在StudentMapper.xml中编写id为“studentResultMap”的resultMap元素,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<!-- 查询学生的结果映射,只映射简单属性 -->
<
resultMap
id
=
"simpleStudent"
type
=
"Student"
>
<
id
property
=
"id"
column
=
"s_id"
/>
<
result
property
=
"name"
column
=
"s_name"
/>
<
result
property
=
"gender"
column
=
"s_gender"
/>
<
result
property
=
"major"
column
=
"s_major"
/>
<
result
property
=
"grade"
column
=
"s_grade"
/>
</
resultMap
>
<!-- 查询学生的结果映射,含指导教师、选修课程等复杂属性的映射,从simpleStudent继承而来,提高resultMap的灵活性和重用性 -->
<
resultMap
id
=
"studentResultMap"
type
=
"Student"
extends
=
"simpleStudent"
>
<!--association的嵌套的结果映射方式。 -->
<
association
property
=
"supervisor"
javaType
=
"Teacher"
resultMap
=
"com.abc.mapper.TeacherMapper.simpleTeacher"
>
</
association
>
<!-- 嵌入的select查询方式,查询学生选修的课程。采用了CourseMapper.xml文件中的id为getByStudentId的select元素,这里的com.abc.mapper.CourseMapper是其命名空间名 -->
<
collection
property
=
"courses"
ofType
=
"Course"
select
=
"com.abc.mapper.CourseMapper.getByStudentId"
column
=
"s_id"
>
</
collection
>
</
resultMap
>
|
这里的关键点在于,为了查询学生选修的课程,用到了collection元素,其查询方式是嵌套的select方式。其select语句采用了CourseMapper.xml文件中的id为getByStudentId的select元素,这里的com.abc.mapper.CourseMapper是其命名空间名(关于collection元素的嵌套select语句的方式,请参考本系列的博文:MyBatis collection的两种形式)。注意这里用到了resultMap元素的继承,提高resultMap元素的灵活性和重用性。
2、在CourseMapper.xml文件中相应的select元素及结果映射如下所示:
1
2
3
4
5
6
7
8
9
10
11
|
<!--课程实体映射-->
<
resultMap
id
=
"simpleCourse"
type
=
"Course"
>
<
id
property
=
"id"
column
=
"course_id"
/>
<
result
property
=
"courseCode"
column
=
"course_code"
/>
<
result
property
=
"courseName"
column
=
"course_name"
/>
</
resultMap
>
<
select
id
=
"getByStudentId"
parameterType
=
"int"
resultMap
=
"simpleCourse"
>
select c.id course_id,course_code,course_name
from course c,student_course sc where sc.student_id=#{id} and sc.course_id = c.id
</
select
>
|
测试类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package
com.demo;
import
java.util.List;
import
org.springframework.context.ApplicationContext;
import
com.abc.service.CourseService;
import
com.abc.service.StudentService;
import
com.abc.domain.Course;
import
com.abc.domain.Student;
import
com.abc.domain.Teacher;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public
class
ManyToManyQuery
{
private
static
ApplicationContext ctx;
static
{
//在类路径下寻找spring主配置文件,启动spring容器
ctx =
new
ClassPathXmlApplicationContext(
"classpath:applicationContext.xml"
);
}
public
static
void
main(String[] args)
{
int
i =
0
, length =
0
;
List<Course> list =
null
;
StudentService studentService = (StudentService)ctx.getBean(
"studentService"
);
Student student = studentService.getById(
7
);
//获取该学生选修的课程
list = student.getCourses();
StringBuilder info =
new
StringBuilder(
"学生姓名:"
);
info.append(student.getName());
info.append(
" "
);
length = list.size();
while
(i < length)
{
info.append(
"所选课程名称:"
);
info.append(list.get(i).getCourseName());
info.append(
" "
);
i++;
}
System.out.println(info.toString());
}
}
|
注意,与前面的工程相比,本工程的文件布局和名称都有一些变化,新增了com.abc.service包,用到了更多的Spring的相关知识。具体内容请参看作者的公开课:http://bbs.51cto.com/open/do/course/cid/65。
运行结果如下:
现在实现课程端功能,即根据id查询出课程及选修这门课程的学生。步骤如下:
1、在CourseMapper.java中声明方法getById,即根据id查询课程。代码如下:
1
|
public
Course getById(
int
id);
|
2、在CourseMapper.xml中编写对应的select语句,如下:
1
2
3
4
5
6
7
|
<!--根据id查询课程及选修的学生-->
<
select
id
=
"getById"
parameterType
=
"int"
resultMap
=
"courseResutMap"
>
select c.id course_id,c.course_code course_code,c.course_name course_name,
s.id s_id, s.name s_name, s.gender s_gender, s.grade s_grade, s.major s_major
from course c left join student_course sc on c.id = sc.course_id
left join student s on sc.student_id = s.id where c.id = #{id}
</
select
>
|
3、此select语句用到了id为courseResutMap的resultMap元素,如下:
1
2
3
4
5
6
7
8
9
10
|
<!--课程实体映射,映射简单属性-->
<
resultMap
id
=
"simpleCourse"
type
=
"Course"
>
<
id
property
=
"id"
column
=
"course_id"
/>
<
result
property
=
"courseCode"
column
=
"course_code"
/>
<
result
property
=
"courseName"
column
=
"course_name"
/>
</
resultMap
>
<!--课程实体映射,除映射简单属性,还包含students复杂属性映射-->
<
resultMap
id
=
"courseResutMap"
type
=
"Course"
extends
=
"simpleCourse"
>
<
collection
property
=
"students"
resultMap
=
"com.abc.mapper.StudentMapper.simpleStudent"
/>
</
resultMap
>
|
这里的关键点还是用到了collection元素,只是这次用到了嵌套的resultMap形式(关于collection元素的嵌套的resultMap形式,请参考本系列的博文:MyBatis collection的两种形式),而且在这里也同样用到了resultMap元素的继承。simpleStudent是StudentMapper.xml文件中的resultMap元素,com.abc.mapper.StudentMapper是其命名空间名。
请注意,id为“simpleStudent”和“simpleCourse”的两个resultMap元素都得到了重用。其中,StudentMapper.xml和CourseMapper.xml中各引用了simpleStudent一次,CourseMapper.xml中引用了simpleCourse两次。
测试类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package
com.demo;
import
java.util.List;
import
org.springframework.context.ApplicationContext;
import
com.abc.service.CourseService;
import
com.abc.service.StudentService;
import
com.abc.domain.Course;
import
com.abc.domain.Student;
import
com.abc.domain.Teacher;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public
class
ManyToManyQuery
{
private
static
ApplicationContext ctx;
static
{
//在类路径下寻找spring主配置文件,启动spring容器
ctx =
new
ClassPathXmlApplicationContext(
"classpath:applicationContext.xml"
);
}
public
static
void
main(String[] args)
{
int
i =
0
, length =
0
;
List<Student> list =
null
;
CourseService courseService = (CourseService)ctx.getBean(
"courseService"
);
Course course = courseService.getById(
1
);
//获取选修了此课程的学生
list = course.getStudents();
length = list.size();
StringBuilder info =
new
StringBuilder(
"课程名称:"
);
info.append(course.getCourseName());
info.append(
" 选修此课程的学生姓名:"
);
while
(i < length)
{
info.append(list.get(i).getName());
info.append(
" "
);
i++;
}
System.out.println(info.toString());
}
}
|
执行结果如下: