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

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunnyYoona/article/details/50670732 1. 一对一映射 1.1 第一种方式 每一个学生都有一个与之关联的地址信息。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunnyYoona/article/details/50670732

1. 一对一映射

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

Student表的数据如下:


Address表的数据如下:


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表的数据如下:


Course表的数据如下:



我们可以从上述两个表中看出:小明开讲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:1213745031@qq.com   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', '小明', '1213745031@qq.com', '18392881300');
INSERT INTO `teacher` VALUES ('2', '卡特', '1203745032@qq.com', '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》

下载:点击打开链接





相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL 缓存 Java
mybatis 一对多查询
mybatis 一对多查询
52 0
|
27天前
|
SQL XML Java
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
后端数据库开发JDBC编程Mybatis之用基于XML文件的方式映射SQL语句实操
33 3
若依修改,集成mybatisplus报错,若依集成mybatisplus,总是找不到映射是怎么回事只要是用mp的方法就找报,改成mybatisPlus配置一定要改
若依修改,集成mybatisplus报错,若依集成mybatisplus,总是找不到映射是怎么回事只要是用mp的方法就找报,改成mybatisPlus配置一定要改
|
26天前
|
Java 数据库连接 mybatis
Mybatis基于注解的一对一和一对多查询
Mybatis基于注解的一对一和一对多查询
25 0
|
26天前
|
SQL Java 数据库连接
Mybatis中一对多mapper配置
Mybatis中一对多mapper配置
20 0
|
2月前
|
算法 BI 数据库
MyBatisPlus查询条件设置、映射匹配兼容性、id生成策略、多数据操作
MyBatisPlus查询条件设置、映射匹配兼容性、id生成策略、多数据操作
76 3
|
2月前
|
SQL Java 数据库连接
15:MyBatis对象关系与映射结构-Java Spring
15:MyBatis对象关系与映射结构-Java Spring
57 4
|
2月前
|
Java 数据库连接 mybatis
mybatis的一对多
mybatis的一对多
|
2月前
|
算法 Java 数据库连接
Spring+MySQL+数据结构+集合,Alibaba珍藏版mybatis手写文档
Spring+MySQL+数据结构+集合,Alibaba珍藏版mybatis手写文档
|
2月前
|
Java 数据库连接 Spring
Spring 整合mybatis
Spring 整合mybatis
30 2