MyBatis实现关联表查询
一、一对一关联
1.1、提出需求
根据班级id查询班级信息(带老师的信息)
1.2、创建表和数据
创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关系。

CREATE TABLE teacher(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(20)
);
CREATE TABLE class(
c_id INT PRIMARY KEY AUTO_INCREMENT,
c_name VARCHAR(20),
teacher_id INT
);
ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id); INSERT INTO teacher(t_name) VALUES('teacher1');
INSERT INTO teacher(t_name) VALUES('teacher2'); INSERT INTO class(c_name, teacher_id) VALUES('class_a', 1);
INSERT INTO class(c_name, teacher_id) VALUES('class_b', 2);

表之间的关系如下:
1.3、定义实体类
1、Teacher类,Teacher类是teacher表对应的实体类。

package me.gacl.domain; /**
* @author gacl
* 定义teacher表对应的实体类
*/
public class Teacher { //定义实体类的属性,与teacher表中的字段对应
private int id; //id===>t_id
private String name; //name===>t_name public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + "]";
}
}

2、Classes类,Classes类是class表对应的实体类

package me.gacl.domain; /**
* @author gacl
* 定义class表对应的实体类
*/
public class Classes { //定义实体类的属性,与class表中的字段对应
private int id; //id===>c_id
private String name; //name===>c_name /**
* class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,
* 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的
*/
private Teacher teacher; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Teacher getTeacher() {
return teacher;
} public void setTeacher(Teacher teacher) {
this.teacher = teacher;
} @Override
public String toString() {
return "Classes [id=" + id + ", name=" + name + ", teacher=" + teacher+ "]";
}
}

1.4、定义sql映射文件classMapper.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,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的
例如namespace="me.gacl.mapping.classMapper"就是me.gacl.mapping(包名)+classMapper(classMapper.xml文件去除后缀)
-->
<mapper namespace="me.gacl.mapping.classMapper"> <!--
根据班级id查询班级信息(带老师的信息)
##1. 联表查询
SELECT * FROM class c,teacher t WHERE c.teacher_id=t.t_id AND c.c_id=1; ##2. 执行两次查询
SELECT * FROM class WHERE c_id=1; //teacher_id=1
SELECT * FROM teacher WHERE t_id=1;//使用上面得到的teacher_id
--> <!--
方式一:嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集
封装联表查询的数据(去除重复的数据)
select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=1
-->
<select id="getClass" parameterType="int" resultMap="ClassResultMap">
select * from class c, teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
</select>
<!-- 使用resultMap映射实体类和字段之间的一一对应关系 -->
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="me.gacl.domain.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap> <!--
方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1 是上一个查询得到的teacher_id的值
-->
<select id="getClass2" parameterType="int" resultMap="ClassResultMap2">
select * from class where c_id=#{id}
</select>
<!-- 使用resultMap映射实体类和字段之间的一一对应关系 -->
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="getTeacher"/>
</resultMap> <select id="getTeacher" parameterType="int" resultType="me.gacl.domain.Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select> </mapper>

在conf.xml文件中注册classMapper.xml
<mappers>
<!-- 注册classMapper.xml文件,
classMapper.xml位于me.gacl.mapping这个包下,所以resource写成me/gacl/mapping/classMapper.xml-->
<mapper resource="me/gacl/mapping/classMapper.xml"/>
</mappers>
1.5、编写单元测试代码

package me.gacl.test; import me.gacl.domain.Classes;
import me.gacl.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test; public class Test3 { @Test
public void testGetClass(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
/**
* 映射sql的标识字符串,
* me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
* getClass是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
*/
String statement = "me.gacl.mapping.classMapper.getClass";//映射sql的标识字符串
//执行查询操作,将查询结果自动封装成Classes对象返回
Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
//使用SqlSession执行完SQL之后需要关闭SqlSession
sqlSession.close();
System.out.println(clazz);//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]
} @Test
public void testGetClass2(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
/**
* 映射sql的标识字符串,
* me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
* getClass2是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
*/
String statement = "me.gacl.mapping.classMapper.getClass2";//映射sql的标识字符串
//执行查询操作,将查询结果自动封装成Classes对象返回
Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
//使用SqlSession执行完SQL之后需要关闭SqlSession
sqlSession.close();
System.out.println(clazz);//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1]]
}
}

1.6、MyBatis一对一关联查询总结
MyBatis中使用association标签来解决一对一的关联查询,association标签可用的属性如下:
- property:对象属性的名称
- javaType:对象属性的类型
- column:所对应的外键字段名称
- select:使用另一个查询封装的结果
二、一对多关联
2.1、提出需求
根据classId查询对应的班级信息,包括学生,老师
2.2、创建表和数据
在上面的一对一关联查询演示中,我们已经创建了班级表和教师表,因此这里再创建一张学生表

CREATE TABLE student(
s_id INT PRIMARY KEY AUTO_INCREMENT,
s_name VARCHAR(20),
class_id INT
);
INSERT INTO student(s_name, class_id) VALUES('student_A', 1);
INSERT INTO student(s_name, class_id) VALUES('student_B', 1);
INSERT INTO student(s_name, class_id) VALUES('student_C', 1);
INSERT INTO student(s_name, class_id) VALUES('student_D', 2);
INSERT INTO student(s_name, class_id) VALUES('student_E', 2);
INSERT INTO student(s_name, class_id) VALUES('student_F', 2);

2.3、定义实体类
1、Student类

package me.gacl.domain; /**
* @author gacl
* 定义student表所对应的实体类
*/
public class Student { //定义属性,和student表中的字段对应
private int id; //id===>s_id
private String name; //name===>s_name public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}

2、修改Classes类,添加一个List<Student> students属性,使用一个List<Student>集合属性表示班级拥有的学生,如下:

package me.gacl.domain; import java.util.List; /**
* @author gacl
* 定义class表对应的实体类
*/
public class Classes { //定义实体类的属性,与class表中的字段对应
private int id; //id===>c_id
private String name; //name===>c_name /**
* class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,
* 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的
*/
private Teacher teacher;
//使用一个List<Student>集合属性表示班级拥有的学生
private List<Student> students; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Teacher getTeacher() {
return teacher;
} public void setTeacher(Teacher teacher) {
this.teacher = teacher;
} public List<Student> getStudents() {
return students;
} public void setStudents(List<Student> students) {
this.students = students;
} @Override
public String toString() {
return "Classes [id=" + id + ", name=" + name + ", teacher=" + teacher
+ ", students=" + students + "]";
}
}

2.4、修改sql映射文件classMapper.xml
添加如下的SQL映射信息

<!--
根据classId查询对应的班级信息,包括学生,老师
-->
<!--
方式一: 嵌套结果: 使用嵌套结果映射来处理重复的联合结果的子集
SELECT * FROM class c, teacher t,student s WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND c.c_id=1
-->
<select id="getClass3" parameterType="int" resultMap="ClassResultMap3">
select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap3">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="me.gacl.domain.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<!-- ofType指定students集合中的对象类型 -->
<collection property="students" ofType="me.gacl.domain.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap> <!--
方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1 是上一个查询得到的teacher_id的值
SELECT * FROM student WHERE class_id=1 //1是第一个查询得到的c_id字段的值
-->
<select id="getClass4" parameterType="int" resultMap="ClassResultMap4">
select * from class where c_id=#{id}
</select>
<resultMap type="me.gacl.domain.Classes" id="ClassResultMap4">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="me.gacl.domain.Teacher" select="getTeacher2"></association>
<collection property="students" ofType="me.gacl.domain.Student" column="c_id" select="getStudent"></collection>
</resultMap> <select id="getTeacher2" parameterType="int" resultType="me.gacl.domain.Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select> <select id="getStudent" parameterType="int" resultType="me.gacl.domain.Student">
SELECT s_id id, s_name name FROM student WHERE class_id=#{id}
</select>

2.5、编写单元测试代码

package me.gacl.test; import me.gacl.domain.Classes;
import me.gacl.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test; public class Test4 { @Test
public void testGetClass3(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
/**
* 映射sql的标识字符串,
* me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
* getClass3是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
*/
String statement = "me.gacl.mapping.classMapper.getClass3";//映射sql的标识字符串
//执行查询操作,将查询结果自动封装成Classes对象返回
Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
//使用SqlSession执行完SQL之后需要关闭SqlSession
sqlSession.close();
//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]
System.out.println(clazz);
} @Test
public void testGetClass4(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
/**
* 映射sql的标识字符串,
* me.gacl.mapping.classMapper是classMapper.xml文件中mapper标签的namespace属性的值,
* getClass4是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
*/
String statement = "me.gacl.mapping.classMapper.getClass4";//映射sql的标识字符串
//执行查询操作,将查询结果自动封装成Classes对象返回
Classes clazz = sqlSession.selectOne(statement,1);//查询class表中id为1的记录
//使用SqlSession执行完SQL之后需要关闭SqlSession
sqlSession.close();
//打印结果:Classes [id=1, name=class_a, teacher=Teacher [id=1, name=teacher1], students=[Student [id=1, name=student_A], Student [id=2, name=student_B], Student [id=3, name=student_C]]]
System.out.println(clazz);
}
}

2.6、MyBatis一对多关联查询总结
MyBatis中使用collection标签来解决一对多的关联查询,ofType属性指定集合中元素的对象类型。
本代码参考自:http://www.cnblogs.com/xdp-gacl/p/4264440.html
测试完全可用。
MyBatis实现关联表查询的更多相关文章
- MyBatis——实现关联表查询
原文:http://www.cnblogs.com/xdp-gacl/p/4264440.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创 ...
- Mybatis系列(三):Mybatis实现关联表查询
原文链接:http://www.cnblogs.com/xdp-gacl/p/4264440.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 ...
- MyBatis—实现关联表查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
- mybatis一对一关联表查询
先创建一个表 CREATE TABLE teacher( t_id INT PRIMARY KEY AUTO_INCREMENT, t_name ) ); CREATE TABLE class( c_ ...
- Mybatis源码分析--关联表查询及延迟加载原理(二)
在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...
- 在MyBatis中查询数据、涉及多参数的数据访问操作、插入数据时获取数据自增长的id、关联表查询操作、动态SQL、关于配置MyBatis映射没有代码提示的解决方案
1. 单元测试 在单元测试中,每个测试方法都需要执行相同的前置代码和后置代码,则可以自定义2个方法,分别在这2个方法中执行前置代码和后置代码,并为这2个方法添加@Before和@After注解,然后, ...
- .NetCore中EFCore的使用整理(二)-关联表查询
EF常用处理关联加载的方式有3中:延迟加载(Lazy Loading).贪婪加载 (Eager Loading)以及显示加载. 一.EF Core 1.1 1.当前的版本,还不支持延迟加载(Lazy ...
- SpringBoot Data JPA 关联表查询的方法
SpringBoot Data JPA实现 一对多.多对一关联表查询 开发环境 IDEA 2017.1 Java1.8 SpringBoot 2.0 MySQL 5.X 功能需求 通过关联关系查询商店 ...
- 使用Mybatis进行连表查询、left join---https://blog.csdn.net/jinzhencs/article/details/51980518
使用Mybatis进行连表查询.left join https://blog.csdn.net/jinzhencs/article/details/51980518
随机推荐
- OpenVpn简单架设
首先Wget wget http://swupdate.openvpn.org/as/openvpn-as-2.0.24-CentOS6.i386.rpm wget http://swupdate.o ...
- video.js使用教程API
videojs就提供了这样一套解决方案,他是一个兼容html5的视频播放工具,早期版本兼容所有浏览器,方法是:提供三个后缀名的视频,并在不支持html5的浏览器下生成一个flash的版本. 最新的3. ...
- JMeter遇到的问题一:Error writing to server(转)
Java.io.IOException: Error writing to server异常:我测试500个并发时,系统没有问题:可当我把线程数加到800时,就出现错误了,在"查看结果树&q ...
- (分享) git详解
今天学了下git http://blog.jobbole.com/78960/ 他写的超级好的 之前也弄过,在eclipse上有个关于github的一个插件,可以通过github的仓库地址把ec ...
- 【转】Caffe初试(五)视觉层及参数
本文只讲解视觉层(Vision Layers)的参数,视觉层包括Convolution, Pooling, Local Response Normalization (LRN), im2col等层. ...
- VR系统的分类
转载请声明转载地址:http://www.cnblogs.com/Rodolfo/,违者必究. 根据用户参与和沉浸感的程度,通常把虚拟现实分为4大类:桌面虚拟现实系统.沉浸式虚拟现实系统.增强虚拟现实 ...
- 局部加权回归、欠拟合、过拟合(Locally Weighted Linear Regression、Underfitting、Overfitting)
欠拟合.过拟合 如下图中三个拟合模型.第一个是一个线性模型,对训练数据拟合不够好,损失函数取值较大.如图中第二个模型,如果我们在线性模型上加一个新特征项,拟合结果就会好一些.图中第三个是一个包含5阶多 ...
- 支付宝通知页面notify_url、返回页面return_url
返回页面(return_url文件)工作原理 即:商户系统请求/支付宝响应交互模式 1. 构造请求数据 商户通过提供的接口代码示例,通过代码示例的规则,程序构造与运算得到sign加密结果以及构造后的 ...
- js中时间戳转化成时间格式
function formatDate(timestamp){ var test = new Date(parseInt(timestamp) * 1000); var $year = test.ge ...
- 简述抽象和封装,对你学习Java有一些作用
作为一个Java 初学者,对Java的理解可能有些片面,甚至有些错误的理解,对于观看此处的您,希望您选择性观看!!! 天知道我为什么选择学习编程,我不爱编程,但是我既然学习了,还是会努力学习的,在此分 ...