【场景】在实际开发中,实体类之间有一对一、一对多、多对多的关系,所以需要正确配置它们对应关系,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