[MyBatis日记](6)一对一与一对多映射


1. 一对一映射

1.1 第一种方式
每一个学生都有一个与之关联的地址信息。表Student有一个addressID列,是Address表的外键。

Student表的数据如下:
[MyBatis日记](6)一对一与一对多映射_第1张图片
QQ截图20160215170355.png
Address表的数据如下:
[MyBatis日记](6)一对一与一对多映射_第2张图片
QQ截图20160215171134.png
Student表和Address表数据是一对一的关系。

Address实体类定义如下:
    
    
    
    
package com.sjf.bean;
/**
* Address实体类
* @author sjf0115
*
*/
public class Address {
private int ID;
private String country;
private String province;
private String city;
public int getID() {
return ID;
}
public void setID(int addressID) {
this.ID = addressID;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "[ ID:" + ID + " country:" + country + " province:" + province + " city:" + city +" ]";
}
}
Student实体类定义如下:
    
    
    
    
package com.sjf.bean;
/**
* Student实体类
* @author sjf0115
*
*/
public class Student {
private int ID;
private String name;
private int age;
private String school;
private Address address;
public Student() {
}
public Student(int id, String name, int age, String school) {
ID = id;
this.name = name;
this.age = age;
this.school = school;
}
public int getID() {
return ID;
}
public void setID(int stuID) {
this.ID = stuID;
}
public String getName() {
return name;
}
public void setName(String stuName) {
this.name = stuName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "ID:" + ID + " name:" + name + " age:" + age + " school:" + school + " address:" + (address!=null ? address.toString() : "null");
}
}

映射器Mapper.xml文件定义如下:
    
    
    
    
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sjf.mapper.StudentMapper">
<resultMap id="StudentWithAddressResultMap" type="com.sjf.bean.Student" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="school" column="school"/>
<result property="address.ID" column="addressID"/>
<result property="address.country" column="country"/>
<result property="address.province" column="province"/>
<result property="address.city" column="city"/>
<result property="address.street" column="street"/>
</resultMap>
<select id="getStudentWithAddressByID" parameterType="int" resultMap="StudentWithAddressResultMap">
SELECT S.ID,name,age,school,A.ID as addressID,country,province,city,street
FROM Student S
LEFT OUTER JOIN Address A
ON S.addressID = A.ID
WHERE S.ID = #{ID}
</select>
 
</mapper>
我们可以使用圆点记法为内嵌的对象的属性赋值。在上述的resultMap中,Student的address属性使用了圆点记法被赋上address对应列的值。同样的,我们可以访问任意深度的内嵌对象的属性。

我们通过创建一个 映射器Mapper接口以类型安全的方式调用,映射器接口如下面代码所示:
    
    
    
    
package com.sjf.mapper;
 
import com.sjf.bean.Student;
 
/**
* Student映射器接口
* @author sjf0115
*
*/
public interface StudentMapper {
/**
* 根据学生ID获取学生信息(带有地址信息)
* @param ID
* @return
*/
Student getStudentWithAddressByID(int ID);
}
我们通过如下代码调用:
    
    
    
    
package com.sjf.service;
 
import org.apache.ibatis.session.SqlSession;
 
import com.sjf.bean.MyBatisSqlSessionFactory;
import com.sjf.bean.Student;
import com.sjf.mapper.StudentMapper;
 
/**
* Student服务类
* @author sjf0115
*
*/
public class StudentService {
/**
* 根据ID获取学生信息(包含地址信息)
* @return
*/
public Student getStudentWithAddressByID(int ID){
SqlSession session = MyBatisSqlSessionFactory.getSqlSession();
try{
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
Student stu = studentMapper.getStudentWithAddressByID(ID);
return stu;
}
finally{
if(session != null){
session.close();
}//if
}//finally
}
}

运行结果:

ID:1   name:yoona   age:24   school:西安电子科技大学 address:[ ID:1   country:中国   province:山东   city:淄博   street:青岛路106号 ]  

上述样例展示了一对一关系映射的一种方法。然而,使用这种方式映射,如果address结果需要在其他的SELECT映射语句中映射成Address对象,我们需要为每一个语句重复这种映射关系。MyBatis提供了更好的实现一对一关系映射的方法: 嵌套结果ResultMap和嵌套SELECT查询语句

注意:

我们Student表有一个ID属性,Address表也有一个ID属性。这样的话,我们在配置映射器Mapper.xml中就会出现冲突:<id property="ID" column="ID"/> 和<result property="address.ID" column="ID"/>出现冲突,列名称都是ID,导致address.ID变为student.ID。解决方法是:在构造SQL语句时为address的ID属性起个别名addressID(address.ID as addressID)从而在配置文件中使用<result property="address.ID" column="addressID"/>
1.2 嵌套结果ResultMap方式
我们可以使用一个嵌套结果ResultMap方式来获取Student以及Address信息,代码如下:
    
    
    
    
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sjf.mapper.StudentMapper">
 
<resultMap id = "AddressResultMap" type="com.sjf.bean.Address" >
<id property="ID" column="addressID"/>
<result property="country" column="country"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="street" column="street"/>
</resultMap>
<resultMap id="StudentWithAddressResultMap" type="com.sjf.bean.Student" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="school" column="school"/>
<association property = "address" resultMap = "AddressResultMap"/>
</resultMap>
<select id="getStudentWithAddressByID" parameterType="int" resultMap="StudentWithAddressResultMap">
SELECT S.ID,name,age,school,A.ID as addressID,country,province,city,street
FROM Student S
LEFT OUTER JOIN Address A
ON S.addressID = A.ID
WHERE S.ID = #{ID}
</select>
</mapper>
元素<association>被用来导入"有一个"(has-one)类型的关联。在这个例子中,我们使用 <association>元素引用了另外一个在同一个XML文件中定义的<resultMap>
我们还可以使用<association>定义内嵌的resultMap,代码如下:
    
    
    
    
<resultMap id="StudentWithAddressResultMap2" type="com.sjf.bean.Student" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="school" column="school"/>
<association property = "address" javaType = "com.sjf.bean.Address">
<id property="ID" column="addressID"/>
<result property="country" column="country"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="street" column="street"/>
</association>
</resultMap>

程序地址: 点击打开链接
1.2 嵌套查询方式
我们还可以通过使用嵌套select查询方式来获取Student信息和Address信息。
    
    
    
    
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sjf.mapper.StudentMapper">
 
<resultMap id = "AddressResultMap" type="com.sjf.bean.Address" >
<id property="ID" column="ID"/>
<result property="country" column="country"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="street" column="street"/>
</resultMap>
<select id = "getAddressByID" parameterType="int" resultMap="AddressResultMap">
SELECT * FROM Address WHERE ID = #{ID}
</select>
<resultMap id="StudentWithAddressResultMap" type="com.sjf.bean.Student" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="school" column="school"/>
<association property="address" column="addressID" select = "getAddressByID"/>
</resultMap>
<select id="getStudentWithAddressByID" parameterType="int" resultMap="StudentWithAddressResultMap">
SELECT * FROM Student WHERE ID = #{ID}
</select>
</mapper>

在此方式中,<  association  >元素的 property属性 设置为JavaBean的 address 属性来,标示这是一个Addess对象; select属性 设置成了id为getAddressByID的语句; column属性设置为对应数据库中 addressID 列(Student表),标示 addressID 这一列值作为传入参数传递给 getAddressByID

在这里, 两个分开的SQL语句将会在数据库中先后执行 第一个调用getStudentWithAddressByID加载Student信息(包括addressID) ,然后调用 getAddressByID ,上一个调用获取的 addressID 这一列值作为传入参数传递给 getAddressByID

程序地址:点击打开链接
1.3 测试
    
    
    
    
package com.sjf.bean;
 
import com.sjf.bean.Student;
import com.sjf.service.StudentService;
 
 
public class Test {
 
public static void main(String[] args) {
StudentService service = new StudentService();
Student student = service.getStudentWithAddressByID(2);
System.out.println(student.toString());
}
}

运行结果:

  ID:2   name:sunny   age:20   school:西安电子科技大学 address:[ ID:2   country:中国   province:陕西   city:西安   street:上海路16号 ]

2. 一对多映射

一个老师(Teacher)可以开讲一门课程(Course),也可以开讲多门课程,但是一门课程只能有一个老师,所以老师(Teacher)与课程(Course)之间是一对多的映射关系。我们使用<collection>元素将一对多类型的结果映射到一个对象集合上。

Teacher表的数据如下:
QQ截图20160215214133.png

Course表的数据如下:
QQ截图20160215214215.png
[MyBatis日记](6)一对一与一对多映射_第3张图片

我们可以从上述两个表中看出:小明开讲MyBatis,Maven和Git三门课程,卡特开讲Java SE和Spring两门课程。

Teacher实体类定义如下:
    
    
    
    
package com.sjf.bean;
 
import java.util.List;
 
/**
* Teacher实体类
* @author sjf0115
*
*/
public class Teacher {
private int ID;
private String name;
private String email;
private String phone;
private List<Course> courses;
public int getID() {
return ID;
}
 
public void setID(int iD) {
ID = iD;
}
 
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
 
public String getEmail() {
return email;
}
 
public void setEmail(String email) {
this.email = email;
}
 
public String getPhone() {
return phone;
}
 
public void setPhone(String phone) {
this.phone = phone;
}
public List<Course> getCourses() {
return courses;
}
 
public void setCourses(List<Course> courses) {
this.courses = courses;
}
 
@Override
public String toString() {
return "教师 ID:" + ID + " email:" + email + " phone:" + phone;
}
}

Course实体类如下:
    
    
    
    
package com.sjf.bean;
/**
* Course实体类
* @author sjf0115
*
*/
public class Course {
private int ID;
private String name;
private String desc;
private int teacherID;
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getTeacherID() {
return teacherID;
}
public void setTeacherID(int teacherID) {
this.teacherID = teacherID;
}
@Override
public String toString() {
return "[ ID:" + ID + " name:" + name + " desc:" + desc + " ]";
}
}
我们通过创建一个 映射器Mapper接口以类型安全的方式调用,映射器接口如下面代码所示:
    
    
    
    
package com.sjf.mapper;
 
import com.sjf.bean.Teacher;
 
/**
* Teacher映射器接口
* @author sjf0115
*
*/
public interface TeacherMapper {
/**
* 根据教师ID获取教师信息(带有课程信息)
* @param ID
* @return
*/
Teacher getTeacherWithCourseByID(int ID);
}
我们通过如下代码调用:
    
    
    
    
package com.sjf.service;
 
import org.apache.ibatis.session.SqlSession;
 
import com.sjf.bean.MyBatisSqlSessionFactory;
import com.sjf.bean.Teacher;
import com.sjf.mapper.TeacherMapper;
 
/**
* Teacher服务类
* @author sjf0115
*
*/
public class TeacherService {
/**
* 根据教师ID获取教师信息(包含课程信息)
* @return
*/
public Teacher getTeacherWithCourseByID(int ID){
SqlSession session = MyBatisSqlSessionFactory.getSqlSession();
try{
TeacherMapper studentMapper = session.getMapper(TeacherMapper.class);
Teacher teacher = studentMapper.getTeacherWithCourseByID(ID);
return teacher;
}
finally{
if(session != null){
session.close();
}//if
}//finally
}
}

现在我们看看如何通过配置文件获取教师信息以及所开讲的课程列表信息。
我们使用<collection>元素来将多个课程结果映射成一个课程Course对象的一个集合。一对多映射可以有两种实现方式: 嵌套结果ResultMap和嵌套Select语句两种实现方式。
2.1 嵌套结果ResultMap
我们可以使用一个嵌套结果ResultMap方式来获取Teacher以及Course信息,代码如下:
    
    
    
    
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sjf.mapper.TeacherMapper">
 
<resultMap id = "CourseResultMap" type="com.sjf.bean.Course" >
<id property="ID" column="courseID"/>
<result property="name" column="courseName"/>
<result property="desc" column="description"/>
<result property="teacherID" column="ID"/>
</resultMap>
<resultMap id="TeacherWithCourseResultMap" type="com.sjf.bean.Teacher" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<collection property = "courses" resultMap = "CourseResultMap"/>
</resultMap>
<select id="getTeacherWithCourseByID" parameterType="int" resultMap="TeacherWithCourseResultMap">
SELECT T.ID, T.name, email, phone, C.ID as courseID, C.name as courseName, description
FROM Teacher T
LEFT OUTER JOIN Course C
ON T.ID = C.teacherID
WHERE T.ID = #{ID}
</select>
</mapper>
<collection>元素的resultMap属性设置成了CourseResultMap, CourseResultMap包含了Course对象属性与列名之间的映射。<collection>元素表示了courses属性是一个Course对象的集合,集合中对象之间的映射关系按照CourseResultMap进行映射。

程序地址:点击打开链接
2.2 嵌套Select语句
我们可以使用一个嵌套查询方式来获取Teacher以及Course信息,代码如下:
    
    
    
    
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sjf.mapper.TeacherMapper">
 
<resultMap id = "CourseResultMap" type="com.sjf.bean.Course" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="desc" column="description"/>
</resultMap>
<select id="getCoursesByTeacherID" parameterType="int" resultMap="CourseResultMap">
SELECT * FROM Course WHERE teacherID = #{teacherID}
</select>
<resultMap id="TeacherWithCourseResultMap" type="com.sjf.bean.Teacher" >
<id property="ID" column="ID"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<collection property = "courses" column = "ID" select = "getCourseByID"/>
</resultMap>
<select id="getTeacherWithCourseByID" parameterType="int" resultMap="TeacherWithCourseResultMap">
SELECT * FROM Teacher WHERE ID = #{ID}
</select>
</mapper>

在此方式中,<collection>元素的 property属性设置为JavaBean的courses属性来标示Course对象的集合; select属性设置成了id为getCoursesByTeacherID的语句; column属性设置为对应数据库中ID列,标示ID这一列值作为传入参数传递给getCoursesByTeacherID;

在这里, 两个分开的SQL语句将会在数据库中先后执行 ,第一个调用getTeachertWithCourseByID加载Teacher信息,然后调用getCoursesByTeacherID,上一个调用获取的 ID这一列值作为传入参数传递给getCoursesByTeacherID。

程序地址: 点击打开链接
2.3 测试
    
    
    
    
package com.sjf.bean;
 
import java.util.List;
 
import com.sjf.service.TeacherService;
 
public class Test {
 
public static void main(String[] args) {
TeacherService service = new TeacherService();
Teacher teacher = service.getTeacherWithCourseByID(1);
System.out.println(teacher.toString());
List<Course> courses = teacher.getCourses();
for(Course course : courses){
System.out.println(" 课程:" + course.toString());
}//for
}
}

运行结果:

  教师 ID:1   email:[email protected]   phone:18392881300
   课程:[ ID:1   name:MyBatis   desc:MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。 ]
   课程:[ ID:3   name:Maven   desc:Maven是来管理项目的构建,报告和文档的软件项目 ]
   课程:[ ID:5   name:Git   desc:Git是一款免费、开源的分布式版本控制系统。 ]  

附录1:
    
    
    
    
/*
Navicat MySQL Data Transfer
 
Source Server : MySQL
Source Server Version : 50624
Source Host : localhost:3306
Source Database : test
 
Target Server Type : MYSQL
Target Server Version : 50624
File Encoding : 65001
 
Date: 2016-02-15 23:22:28
*/
 
SET FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for `teacher`
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 NOT NULL,
`email` varchar(20) CHARACTER SET utf8 NOT NULL,
`phone` varchar(20) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
 
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', '小明', '[email protected]', '18392881300');
INSERT INTO `teacher` VALUES ('2', '卡特', '[email protected]', '18392881301');


附录2:
    
    
    
    
/*
Navicat MySQL Data Transfer
 
Source Server : MySQL
Source Server Version : 50624
Source Host : localhost:3306
Source Database : test
 
Target Server Type : MYSQL
Target Server Version : 50624
File Encoding : 65001
 
Date: 2016-02-15 23:23:31
*/
 
SET FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8 NOT NULL,
`description` varchar(50) CHARACTER SET utf8 DEFAULT NULL,
`teacherID` int(11) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
 
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('1', 'MyBatis', 'MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。', '1');
INSERT INTO `course` VALUES ('2', 'Java SE', 'Java se是由Sun Microsystems公司推出的Java程序设计语言。', '2');
INSERT INTO `course` VALUES ('3', 'Maven', 'Maven是来管理项目的构建,报告和文档的软件项目', '1');
INSERT INTO `course` VALUES ('4', 'Spring', 'Spring是于2003 年兴起的一个轻量级的Java 开发框架。', '2');
INSERT INTO `course` VALUES ('5', 'Git', 'Git是一款免费、开源的分布式版本控制系统。', '1');



参考:《Java Persistence with MyBatis 3》

下载:点击打开链接





你可能感兴趣的:(MyBatis日记)