MyBatis学习笔记:一对一和一对多关联表查询
在前面的两篇博文的例子中,都是简单的实体类,都不存在包括其他实体类的引用。
例如:班级类Class
public class Classes {
private int id;
private String name;
//getter setter
}
这种情况下,如果你想根据班级的id来查班级的信息,则在sql映射文件ClassMapper.xml中写入如下的代码:
<select id="queryById" parameterType="int" resultType="com.wrh.entity.Classes">
SELECT * FROM class WHERE s_id=#{id}
</select>
并在相应的位置写类似于下面的代码就可以查出班级的信息了。
@Test
public void queryById(){
sqlSession = MybatisUtil.getSqlSession();
int id = 1;
try{
Classes classes = sqlSession.selectOne("mapper.ClassMapper.queryById",id);
System.out.println(classes);
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSession(sqlSession);
}
}
但是,一般情况下,一个班级都有一个班主任,是吧,如果现在实体类Classes有Teacher这个属性,则依靠上面的sql就不能查询出班级的信息(包括老师)了。
还有,一般情况下,一个班级有多个学生,是吧,如果现在实体类Classes有List这个属性,则就更复杂了
public class Classes {
private int id;
private String name;
private Teahcer teacher;
//getter setter
}
那么,具体该怎么做呢?这就是本篇博文要介绍的一对一和一对多关联表查询可以解决这个问题。
一对一关联表查询
1、数据库表的准备:class、teacher
其中class包括三个字段:c_id、c_name、teacher_id
teacher包括两个字段:t_id、t_name
具体如下:
2、class表和teacher对应的实体类:Classes、Teacher
Teacher.java
package com.wrh.entity;
/**
* @Author:wojiushimogui
* @Description:
* @Date:Created by 下午2:50 on 2017/9/3.
*/
public class Teacher {
private int id;
private String 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 + '\'' +
'}';
}
}
Classes.java
package com.wrh.entity;
/**
* @Author:wojiushimogui
* @Description:
* @Date:Created by 下午2:51 on 2017/9/3.
*/
public class Classes {
private int id;
private String 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 +
'}';
}
}
3、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="mapper.ClassMapper">
<!--
假设我们要干这样一件事情:根据class id查的 该班级的信息(包括teacher的信息)。
-->
<!-- 第一种方式:-->
<select id="queryClassById" parameterType="int" resultMap="classResultMap">
SELECT * FROM class c,teacher t where c.teacher_id = t.t_id AND c_id =#{id}
</select>
<resultMap id="classResultMap" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.wrh.entity.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
<!--第二种方式:嵌套查询
select * from class where c_id = #{id};
select * from teacher where t_id = 1;//其中 1为是上一个查询得到的teacher_id的值
-->
<select id="queryClassById2" parameterType="int" resultMap="classResultMap2">
SELECT * FROM class WHERE c_id = #{id}
</select>
<resultMap id="classResultMap2" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="getTeacher">
</association>
</resultMap>
<select id="getTeacher" parameterType="int" resultType="com.wrh.entity.Teacher">
SELECT t_id id,t_name name FROM teacher WHERE t_id = #{id}
</select>
</mapper>
4、注册:将ClassMapper.xml文件注册在mybatis-config.xml配置文件中
<mappers>
<mapper resource="mapper/ClassMapper.xml"/>
</mappers>
5、测试
public class ClassTest {
SqlSession sqlSession ;
@Test
public void queryClassById(){
sqlSession = MybatisUtil.getSqlSession();
int id = 2;
try{
Classes classes = sqlSession.selectOne("mapper.ClassMapper.queryClassById",id);
System.out.println(classes);//Classes{id=2, name='heihei', teacher=Teacher{id=1, name='wojiushimogui'}}
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSession(sqlSession);
}
}
@Test
public void queryClassById2(){
sqlSession = MybatisUtil.getSqlSession();
int id = 2;
try{
Classes classes = sqlSession.selectOne("mapper.ClassMapper.queryClassById2",id);
System.out.println(classes);//Classes{id=2, name='heihei', teacher=Teacher{id=1, name='wojiushimogui'}}
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSession(sqlSession);
}
}
}
一对多关联表查询
在一对一的基础上我们做如下的修改即可。
1、添加Student实体类
Student实体类
package com.wrh.entity;
/**
* @Author:wojiushimogui
* @Description:
* @Date:Created by 下午1:19 on 2017/9/3.
*/
public class Student {
private int id;
private String name;
private int age;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
此实体对应的表结构和内容如下:
2、修改Classes类
Classes实体类
package com.wrh.entity;
import java.util.List;
/**
* @Author:wojiushimogui
* @Description:
* @Date:Created by 下午2:51 on 2017/9/3.
*/
public class Classes {
private int id;
private String name;
/**
* class表中有一个teacher_id字段,所以在Classes类中定义一个teacher属性,
* 用于维护teacher和class之间的一对一关系,通过这个teacher属性就可以知道这个班级是由哪个老师负责的
*/
private Teacher teacher;
//一对多的关系
private List<Student> studentList;
public List<Student> getStudentList() {
return studentList;
}
public void setStudentList(List<Student> studentList) {
this.studentList = studentList;
}
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 +
", studentList=" + studentList +
'}';
}
}
3、sql映射文件ClassMapperOne2Many.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="mapper.ClassMapperOne2Many">
<!--
假设我们要干这样一件事情:根据class id查的 该班级的信息(包括teacher、student的信息)。
-->
<!-- 第一种方式:-->
<select id="queryClassById" parameterType="int" resultMap="classResultMap">
SELECT * FROM class c,teacher t ,student s where c.teacher_id = t.t_id and c.c_id = s.class_id AND c_id =#{id}
</select>
<resultMap id="classResultMap" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.wrh.entity.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<collection property="studentList" ofType="com.wrh.entity.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
<!--第二种方式:嵌套查询
select * from class where c_id = #{id};
select * from teacher where t_id = 1;//其中 1为是上一个查询得到的teacher_id的值
select * from student where class_id = 1;//其中 1为是上一个查询得到的c_id的值
-->
<select id="queryClassById2" parameterType="int" resultMap="classResultMap2">
SELECT * FROM class WHERE c_id = #{id}
</select>
<resultMap id="classResultMap2" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="getTeacher"/>
<collection property="studentList" ofType="com.wrh.entity.Student" column="c_id" select="getStudent"/>
</resultMap>
<select id="getTeacher" parameterType="int" resultType="com.wrh.entity.Teacher">
SELECT t_id id,t_name name FROM teacher WHERE t_id = #{id}
</select>
<select id="getStudent" parameterType="int" resultType="com.wrh.entity.Student">
SELECT s_id id,s_name name,s_age age from student where class_id = #{id}
</select>
</mapper>
4、将sql映射文件注册到mybatis-config.xml文件中
<mappers>
<mapper resource="mapper/ClassMapperOne2Many.xml"/>
</mappers>
5、测试
public class ClassTest {
SqlSession sqlSession ;
@Test
public void queryClassById(){
sqlSession = MybatisUtil.getSqlSession();
int id = 2;
try{
// Classes classes = sqlSession.selectOne("mapper.ClassMapper.queryClassById",id);
Classes classes = sqlSession.selectOne("mapper.ClassMapperOne2Many.queryClassById",id);
System.out.println(classes);//Classes{id=2, name='heihei', teacher=Teacher{id=1, name='wojiushimogui'}}
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSession(sqlSession);
}
}
@Test
public void queryClassById2(){
sqlSession = MybatisUtil.getSqlSession();
int id = 2;
try{
// Classes classes = sqlSession.selectOne("mapper.ClassMapper.queryClassById2",id);
Classes classes = sqlSession.selectOne("mapper.ClassMapperOne2Many.queryClassById2",id);
System.out.println(classes);//Classes{id=2, name='heihei', teacher=Teacher{id=1, name='wojiushimogui'}}
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSession(sqlSession);
}
}
}
测试结果符合预期。
在测试过程中,遇到了如下的问题:
产生的原因:ClassMapperOne2Many.xml文件中将 中的namespace的内容写成了mapper.ClassMapper。所以要细心。
总结
MyBatis中是使用association标签来解决一对一的关联查询,
形式为:
<select id="queryClassById" parameterType="int" resultMap="classResultMap">
SELECT * FROM class c,teacher t where c.teacher_id = t.t_id AND c_id =#{id}
</select>
<resultMap id="classResultMap" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.wrh.entity.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>
或者是
<select id="queryClassById2" parameterType="int" resultMap="classResultMap2">
SELECT * FROM class WHERE c_id = #{id}
</select>
<resultMap id="classResultMap2" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="getTeacher">
</association>
</resultMap>
<select id="getTeacher" parameterType="int" resultType="com.wrh.entity.Teacher">
SELECT t_id id,t_name name FROM teacher WHERE t_id = #{id}
</select>
其中,association标签可用的属性如下:
property:对象属性的名称
javaType:对象属性的类型
column:所对应的外键字段名称
select:使用另一个查询封装的结果
而MyBatis中是使用collection标签来解决一对多的关联查询, 形式如下:
<select id="queryClassById" parameterType="int" resultMap="classResultMap">
SELECT * FROM class c,teacher t ,student s where c.teacher_id = t.t_id and c.c_id = s.class_id AND c_id =#{id}
</select>
<resultMap id="classResultMap" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" javaType="com.wrh.entity.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<collection property="studentList" ofType="com.wrh.entity.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
或者是
<select id="queryClassById2" parameterType="int" resultMap="classResultMap2">
SELECT * FROM class WHERE c_id = #{id}
</select>
<resultMap id="classResultMap2" type="com.wrh.entity.Classes">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" select="getTeacher"/>
<collection property="studentList" ofType="com.wrh.entity.Student" column="c_id" select="getStudent"/>
</resultMap>
<select id="getTeacher" parameterType="int" resultType="com.wrh.entity.Teacher">
SELECT t_id id,t_name name FROM teacher WHERE t_id = #{id}
</select>
<select id="getStudent" parameterType="int" resultType="com.wrh.entity.Student">
SELECT s_id id,s_name name,s_age age from student where class_id = #{id}
</select>