【场景】在实际开发中,实体类之间有一对一、一对多、多对多的关系,所以需要正确配置它们对应关系,Mybatis通过配置文件能够从数据库中获取列数据后自动封装成对象。
1. 一对一映射
以每个学生有一个地址为例:
public class Address
{
private Integer addrId;
private String street;
private String city;
private String state;
private String zip;
private String country;
// setters & getters
}public class Student
{
private Integer studId;
private String name;
private String email;
private PhoneNumber phone;
private Address address;
//setters & getters
}
需要在对应的maper.xml文件里定义它们的映射关系。常用的几种方式如下:
1.1 继承ResultMap,使用 . 表示嵌套对象的引用
这种方式就是定义一个继承自Student类型的ResultMap,然后将对应需要包含的address信息给映射进去。原有Student的映射定义如下:
<resultMap type="Student" id="StudentResult">
<id property="studId" column="stud_id"/>
<result property="name" column="name" />
<result property="email" column="email"/>
<result property="phone" column="phone"/>
</resultMap>
继承Student的定义如下:
<resultMap type="Student" id="StudentWithAddressExtResult" extends="StudentResult">
<result property="address.addrId" column="addr_id"/>
<result property="address.street" column="street"/>
<result property="address.city" column="city"/>
<result property="address.state" column="state"/>
<result property="address.zip" column="zip"/>
<result property="address.country" column="country"/>
</resultMap>
相当于把address的对应属性和表里的字段映射给单独定义在一个地方映射起来。查找语句可以根据如下定义:
<select id="selectStudentWithAddress" parameterType="int" resultMap="StudentWithAddressExtResult">
select stud_id, name, email,phone, a.addr_id, street, city, state, zip, country
FROM STUDENTS s left outer join ADDRESSES a on s.addr_id=a.addr_id
where stud_id=#{studId}
</select>
对应的mapper接口定义如下:
public interface StudentMapper {
Student selectStudentWithAddress(int id);
}
StudentService里定义相关的方法如下:
public Student findStudentWithAddressById(int id) {
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
try {
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
return studentMapper.selectStudentWithAddress(id);
} finally {
sqlSession.close();
}
}
验证:
public static void main( String[] args ) {
StudentService studService = new StudentService();
Student student1 = studService.findStudentWithAddressById(1);
System.out.println(student1);
}
输出结果:
Student [studId=1, name=Timothy, email=timothy@gmail.com, phone=123-123-1234, address=Address [addrId=3, street=710 N Cable Rd, city=Lima, state=OH, zip=45825, country=Allen]]
在一些情况下,我们已经定义了Address的映射ResultMap,如果再在这里重新定义一遍,就显得比较冗余了。
1.2 association直接关联
如果我们已经定义好了Address的映射,假设它的定义如下:
<mapper namespace="com.yunzero.map.mappers.AddressMapper">
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id"/>
<result property="street" column="street"/>
<result property="city" column="city"/>
<result property="state" column="state"/>
<result property="zip" column="zip"/>
<result property="country" column="country"/>
</resultMap>
<select id="selectAddressById" parameterType="int" resultMap="AddressResult">
select * from ADDRESSES where addr_id=#{addrId}
</select>
</mapper>
对应的Student ResultMap就需要修改如下:
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<association property="address" resultMap="com.yunzero.map.mappers.AddressMapper.AddressResult"/>
</resultMap>
定义了一个association的属性,然后将对应的resultMap指向对应的AddressResult。这里定义的AddressResult是定义在另外一个文件以及命名空间里,所以需要引用它的全名。
1.3 内嵌select关联
<resultMap type="Student" id="StudentWithAddressNestedSelect">
<id property="studId" column="stud_id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<association property="address" column="addr_id" select="com.yunzero.map.mappers.AddressMapper.selectAddressById"/>
</resultMap>
在association里指定column字段,它作为嵌入select语句里对应的参数。
1.4 ResultMap嵌套
<resultMap type="Student" id="StudentWithAddressNestedResultMap">
<id property="studId" column="stud_id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<association property="address" javaType="Address">
<id property="addrId" column="addr_id"/>
<result property="street" column="street"/>
<result property="city" column="city"/>
<result property="state" column="state"/>
<result property="zip" column="zip"/>
<result property="country" column="country"/>
</association>
</resultMap>
2. 一对多映射
以一个教师有多门课程为例:
public class Course
{
private Integer courseId;
private String name;
private String description;
private Date startDate;
private Date endDate;
private Integer tutorId;
//setters & getters
}
public class Tutor
{
private Integer tutorId;
private String name;
private String email;
private Address address;
private List<Course> courses;
// setters & getters
}
需要在对应的maper.xml文件里定义它们的映射关系。常用的几种方式如下:
2.1 嵌入ResultMap
关于课程的配置如下:
<mapper namespace="com.yunzero.map.mappers.CourseMapper">
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId"/>
<result column="name" property="name"/>
<result column="description" property="description"/>
<result column="start_date" property="startDate"/>
<result column="end_date" property="endDate"/>
</resultMap>
<select id="selectCoursesByTutor" parameterType="int" resultMap="CourseResult">
select * from COURSES where tutor_id=#{tutorId}
</select>
</mapper>
关于教师的配置如下:
<resultMap type="Tutor" id="TutorWithCoursesNestedResult">
<id column="tutor_id" property="tutorId"/>
<result column="tutor_name" property="name"/>
<result column="email" property="email"/>
<association property="address" resultMap="com.yunzero.map.mappers.AddressMapper.AddressResult"/>
<collection property="courses" resultMap="com.yunzero.map.mappers.CourseMapper.CourseResult" />
</resultMap>
对于多个course,它需要使用collection属性,并指定对应的CourseResult。
对应的查询语句如下:
<select id="selectTutorById" parameterType="int" resultMap="TutorWithCoursesNestedResult">
SELECT t.tutor_id, t.name as tutor_name, email, a.addr_id, street, city, state, zip, country,
course_id, c.name, description, start_date, end_date
FROM TUTORS t left outer join ADDRESSES a on t.addr_id=a.addr_id
left outer join COURSES c on t.tutor_id=c.tutor_id
where t.tutor_id=#{tutorId}
</select>
对应的mapper接口和TutorService的实现如下:
public interface TutorMapper {
Tutor selectTutorById(int tutorId);
}
public Tutor findTutorById(int tutorId) {
SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory().openSession();
try {
TutorMapper mapper = sqlSession.getMapper(TutorMapper.class);
return mapper.selectTutorById(tutorId);
}
finally {
sqlSession.close();
}
}
2.2 嵌入select
<resultMap type="Tutor" id="TutorWithCoursesNestedSelect">
<id column="tutor_id" property="tutorId"/>
<result column="tutor_name" property="name"/>
<result column="email" property="email"/>
<association property="address" resultMap="com.yunzero.map.mappers.AddressMapper.AddressResult"/>
<collection property="courses" column="tutor_id" select="com.yunzero.map.mappers.CourseMapper.selectCoursesByTutor"/>
</resultMap>
<select id="selectTutorWithCourses" parameterType="int" resultMap="TutorWithCoursesNestedSelect">
SELECT t.tutor_id, t.name as tutor_name, email, a.addr_id, street, city, state, zip, country
FROM TUTORS t left outer join ADDRESSES a on t.addr_id=a.addr_id
where t.tutor_id=#{tutorId}
</select>
这种方式和前面那种方式的差别在于它将查找对应course的方法委派给对应course的映射定义里,所以这里值需要考虑它和address的映射就可以了。而前面通过两个连接操作相当于将所有结果都返回来了,就不需要再去利用别的查询。当然,因为是一对多的映射牵涉到返回多个结果,这种方式可能需要执行多次查询,有可能导致性能的问题。
参考:
http://shmilyaw-hotmail-com.iteye.com/blog/2357862