前期準備
數據庫資源準備:
CREATE DATABASE mybatis;
USE mybatis;
CREATE TABLE `teacher`(
`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵id',
`name` VARCHAR (30) NOT NULL COMMENT '姓名'
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO teacher(`name`) VALUES ('胡老師');
INSERT INTO teacher(`name`) VALUES ('黃老師');
CREATE TABLE `student`(
`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵id',
`name` VARCHAR (30) NOT NULL COMMENT '姓名',
`tid` INT DEFAULT NULL,
KEY `fktid`(`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `student`( `name`,`tid`) VALUES ('小明',1);
INSERT INTO `student`( `name`,`tid`) VALUES ('小紅',1);
INSERT INTO `student`( `name`,`tid`) VALUES ('小剛',1);
INSERT INTO `student`( `name`,`tid`) VALUES ('小張',2);
INSERT INTO `student`( `name`,`tid`) VALUES ('小麗',2);
INSERT INTO `student`( `name`,`tid`) VALUES ('小美',2);
雖然只有簡單的兩個表,但是可以很明顯的反映一對一和一對多的情景。
如果學生對老師,就是一對一的情況,如果是老師對學生,就是一對多。
我們可以理解爲一個學生有一個老師,但是一個老師就包含多個學生。
這裏不再贅述Mybatis的配置,僅列出較爲重要的代碼。
一對一
這裏我們就以一個學生包含一個唯一的老師爲例:
需求:這裏我們需要查詢出學生的信息,其學生的信息包含老師的信息。
實體類如下(爲節省文章篇幅,省略了實體類的構造方法、getter、setter和toString):
Teacher類
package com.ara.pojo;
//老師
public class Teacher {
private int id;
private String name;
}
Student類
//學生
public class Student {
private int id;
private String name;
//學生要關聯一個老師 一個學生對應一個老師
private Teacher teacher;
}
TeacherMapper接口(由於這裏演示僅需要這一個方法,爲了簡便就直接使用註解了):
package com.ara.dao;
import com.ara.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface TeacherMapper {
/**
* 通過老師的id查詢出老師的信息
*
* @param id 老師的唯一主鍵id
* @return 返回id對應的老師信息
*/
@Select("select * from teacher where id = #{id}")
Teacher getTeacherById(@Param("id") int id);
}
StudentMapper接口:
package com.ara.dao;
import com.ara.pojo.Student;
import org.apache.ibatis.annotations.Param;
public interface StudentMapper {
/**
* 根據學生的主鍵id查詢學生的詳細信息 其中包含老師的信息
* 方式一
*
* @param id 主鍵id
* @return 返回對應的學生信息
*/
Student getStudentById1(@Param("id") int id);
/**
* 根據學生的主鍵id查詢學生的詳細信息 其中包含老師的信息
* 方式二
*
* @param id 主鍵id
* @return 返回對應的學生信息
*/
Student getStudentById2(@Param("id") int id);
}
StudentMapper.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.ara.dao.StudentMapper">
<!-- 直接使用聯表查詢 start -->
<select id="getStudentById1" parameterType="_int" resultMap="StudentMap1">
select s.id sid,s.name sname,t.id tid,t.name tname from student s,teacher t where s.tid = t.id and s.id = #{id};
</select>
<resultMap id="StudentMap1" type="Student">
<result column="sid" property="id" />
<result column="sname" property="name" />
<!--
由於這裏屬於嵌套組合的屬性 我們採用association標籤
因爲我們需要對Student中Teacher進行再次封裝 所以我們需要制定如下屬性
property:Student類中的屬性名
javaType:制定屬性的Java類型
-->
<association property="teacher" javaType="Teacher">
<result column="tid" property="id" />
<result column="tname" property="name" />
</association>
</resultMap>
<!-- 方式一:直接使用聯表查詢 end -->
<!-- 方式二:查詢中嵌套查詢 start -->
<select id="getStudentById2" parameterType="_int" resultMap="StudentMap2">
select * from student where id = #{id};
</select>
<resultMap id="StudentMap2" type="Student">
<result column="id" property="id" />
<result column="name" property="name" />
<!--
由於這裏屬於分兩次查詢 所以我們直接利用外鍵這列來查詢Teacher信息
column:查詢後的列名
property:Student類中的屬性名
javaType:制定屬性的Java類型
select:需要引用的查詢Id
-->
<association column="tid" property="teacher" javaType="Teacher" select="com.ara.dao.TeacherMapper.getTeacherById"/>
</resultMap>
<!-- 方式二:查詢中嵌套查詢 end -->
</mapper>
測試代碼:
package com.ara.test;
import com.ara.dao.StudentMapper;
import com.ara.pojo.Student;
import com.ara.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class StudentMapperTest {
@Test
public void getStudentByIdTest1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.getStudentById1(1);
System.out.println(student);
sqlSession.close();
}
@Test
public void getStudentByIdTest2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.getStudentById2(5);
System.out.println(student);
sqlSession.close();
}
}
getStudentByIdTest1結果:
getStudentByIdTest2結果:
兩種方式都可以查詢出結果,但是兩者的過程並不相同,聯表查詢雖然SQL語句較爲複雜,但是它的執行就只運行了一條SQL語句,嵌套查詢雖然SQL語句簡單,但是它卻執行了兩個SQL語句,具體開發中,還是要根據自己的需要來酌情選擇。
一對多
這裏我們就以一個老師有多個學生爲例:
需求:根據對應老師的Id查詢老師的信息及其所有的學生信息。
實體類如下(爲節省文章篇幅,省略了實體類的構造方法、getter、setter和toString):
Student類:
package com.ara.pojo;
//學生類
public class Student {
private int id;
private String name;
//學生要關聯一個老師id
private int tid;
}
Teacher類:
package com.ara.pojo;
import java.util.List;
//老師
public class Teacher {
private int id;
private String name;
//老師含有多個學生
private List<Student> students;
}
StudentMapper接口:
package com.ara.dao;
import com.ara.pojo.Student;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface StudentMapper {
/**
* 老師的id查詢其學生列表
* @param tid 老師id
* @return 返回學生列表
*/
@Select("select * from student where tid = #{tid}")
List<Student> getStudentByTId(@Param("tid")int tid);
}
TeacherMapper接口:
package com.ara.dao;
import com.ara.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
public interface TeacherMapper {
/**
* 根據教師的id查詢教師的信息 包含其名下學生信息
* 方式一
*
* @param id 教師id
* @return 返回對應教師的信息
*/
Teacher getTeacherById1(@Param("id") int id);
/**
* 根據教師的id查詢教師的信息 包含其名下學生信息
* 方式二
*
* @param id 教師id
* @return 返回對應教師的信息
*/
Teacher getTeacherById2(@Param("id") int id);
}
TeacherMapper.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.ara.dao.TeacherMapper">
<!-- 方式一:結果嵌套查詢 start -->
<select id="getTeacherById1" parameterType="_int" resultMap="teacherInfoMap1">
select t.id tid,t.name tname,s.id sid,s.name sname from teacher t,Student s where t.id = s.tid and t.id = #{id}
</select>
<resultMap id="teacherInfoMap1" type="Teacher">
<result column="tid" property="id" />
<result column="tname" property="name" />
<!--
複雜的屬性 我們需要單獨處理 集合使用collection
javaType:指定屬性的類型
ofType:指定集合中的泛型信息
-->
<collection property="students" ofType="Student" >
<result column="sid" property="id" />
<result column="sname" property="name" />
<result column="tid" property="tid" />
</collection>
</resultMap>
<!-- 方式一:結果嵌套查詢 end -->
<!-- 方式二:查詢嵌套查詢 start -->
<select id="getTeacherById2" parameterType="_int" resultMap="teacherInfoMap2" >
select * from teacher where id = #{id};
</select>
<resultMap id="teacherInfoMap2" type="Teacher">
<result column="id" property="id" />
<result column="name" property="name" />
<!--
複雜的屬性 我們需要單獨處理 集合使用collection
column:指定需要再次查詢的列名
property:指定Teacher中的students屬性
javaType:指定Teacher中的students屬性的Java類型
ofType:指定Teacher中的students屬性的Java類型的泛型類型
select:指定我們需要做再次查詢的方法id
-->
<collection column="id" property="students" javaType="ArrayList" ofType="Student" select="com.ara.dao.StudentMapper.getStudentByTId"/>
</resultMap>
<!-- 方式二:查詢嵌套查詢 end -->
</mapper>
測試代碼如下:
package com.ara.dao.test;
import com.ara.dao.TeacherMapper;
import com.ara.pojo.Teacher;
import com.ara.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class TeacherMapperTest {
@Test
public void getTeacherByIdTest1(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
Teacher teacher = sqlSession.getMapper(TeacherMapper.class).getTeacherById1(1);
System.out.println(teacher);
sqlSession.close();
}
@Test
public void getTeacherByIdTest2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
Teacher teacher = sqlSession.getMapper(TeacherMapper.class).getTeacherById2(2);
System.out.println(teacher);
sqlSession.close();
}
}
getTeacherByIdTest1測試結果:
getTeacherByIdTest2測試結果:
這兩種方式都能實現我們的需求,但是也存在着上述一對一的情況,就是聯表查詢的SQL語句雖然較爲複雜,但是它只運行了一條SQL,通過查詢中嵌套查詢,雖然SQL簡單了,但是它分了兩條SQL進行查詢。
都各有優缺點,結果中嵌套只是理解起來可能較爲複雜,但是它的效率就相對較高,查詢中嵌套查詢,雖然SQL語句很簡單,但是它的執行效率就相對來說較爲低下。
實際的開發中,根據具體的情況具體斟酌即可。