Mybatis的多表查询
问题:
目前我们可以使用Mybatis完成单表的增删改查(基于SqlSession对象的和基于动态代理的),
以及可以实现单表的SQL语句的动态拼接。但是在开发中,我们发现所有的项目,其实底层
就是对数据库的增删改查,而查询最多的是多表联合查询,那么怎么完成多表查询呢?
解决:
使用Mybatis的多表联合查询
实现:
业务装配方式(了解)
resultMap的n+1方式(了解)
resultMap的联合查询方式(常用)
业务装配方式
业务装配方式的联合查询:
概念:
很多时候,我们的查询需要的结果分布在多张表中。
以前呢我们使用联合查询语句一次性将数据查询出来。
但是,其实多表联合查询可以分开成多个单表查询,比如
查询学生及其教师,就可以分为两个步骤:
1.查询所有的学生。
2.根据查询的学生的教师编号查询教师信息。
我们将以上的思路实现在业务层,将此种方式称为业务装配。
需求分析:
查询学生及其老师(一对一)
查询老师及其学生(一对多)
数据库设计:
学生表:student
学生编号:sid
学生姓名:sname
学生年龄:sage
教师ID:tid
教师表:teacher
教师编号:tid
教师姓名:tname
存储该教师下的学生信息:ls
SQL语句设计:
查询学生及其老师(一对一)
查询所有的学生:
select * from student
根据教师ID查询教师信息
select * from teacher where tid=#{0}
查询老师及其学生(一对多)
查询所有的教师
select * from teacher
根据教师ID查询学生
select * from student where tid=#{0}
总结:
好处:
将多表联合查询转换成了单表查询,便于书写SQL语句。
缺陷:
①提升了业务层的代码量。
②对数据库的IO操作非常频繁,SQL语句被执行了N+1次
Student类
private int sid;//学生编号
private String sname;//学生姓名
private int sage;//学生年龄
private int tid;//教师编号
private Teacher teacher;//该学生的教师信息
Student接口
public interface StudentMapper {
//业务装配方式:
//一对一关系:查询所有的学生及其教师
//查询所有的学生
List<Student> selStu();
//一对多关系:查询教师及其学生信息
//根据教师编号查询学生信息
List<Student> selStuById(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.i.mapper.StudentMapper">
<!-- Mybati多表联合查询 -->
<!-- 业务装配方式 -->
<!-- 一对一关系:查询学生及其老师 -->
<select id="selStu" resultType="student">
SELECT * FROM student
</select>
<!-- 一对多关系 查询教师及其学生 -->
<!-- 根据教师编号查询学生信息 -->
<select id="selStuById" resultType="student">
SELECT * FROM student WHERE tid=#{0}
</select>
</mapper>
Teacher类
private int tid;//教师编号
private String tname;//教师姓名
private List<Student> ls;//存储该教师下的学生信息
Teacher接口
public interface TeacherMapper {
//业务装配方式
//一对一关系:查询学生及其老师信息
//根据ID查询教师信息
Teacher selTeaById(int tid);
//一对多关系:查询教师及其学生
//查询所有教师
List<Teacher> selTea();
}
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.i.mapper.TeacherMapper">
<!-- Mybati多表联合查询 -->
<!-- 业务装配方式 -->
<!-- 一对一关系:查询学生及其老师 -->
<!-- 根据ID查询教师信息 -->
<select id="selTeaById" resultType="teacher">
SELECT * FROM teacher WHERE tid=#{0}
</select>
<!-- 一对多关系 查询教师及其学生-->
<!-- 查询所有教师 -->
<select id="selTea" resultType="teacher">
SELECT * FROM teacher
</select>
</mapper>
测试
public class TestDB {
public static void main(String[] args) throws IOException {
//获取SqlSession对象
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession ss = factory.openSession();
//获取Mapper接口对象
StudentMapper sm = ss.getMapper(StudentMapper.class);
TeacherMapper tm = ss.getMapper(TeacherMapper.class);
//多表联合查询
//业务装配方式:
//一对一关系: 查询学生及其教师信息
//查询所有学生
List<Student> ls = sm.selStu();
System.out.println(ls);
//遍历学生信息查询教师信息
for (Student s : ls) {
//根据教师ID查询教师信息
Teacher t = tm.selTeaById(s.getTid());
System.out.println(t);
}
System.out.println("===========");
//一对多关系: 查询教师及其学生
//查询所有的教师
List<Teacher> lt = tm.selTea();
//根据教师编号查询学生
for (Teacher t : lt) {
List<Student> ls2 = sm.selStuById(t.getTid());
t.setLs(ls2);
}
System.out.println(lt);
}
}
ResultMap N+1方式联合查询
问题:
在学习了业务装配方式后,其实就是将多表联合查询拆分成单表查询,然后
在业务层将数据根据表关系进行填充装配。那么,能不能把在业务层装配的动作
发生在数据库层呢?这样简化了业务层的代码压力。
解决:
使用ResultMap N+1方式
概念:
因为数据库层的代码是基于SqlSssion对象动态生成的,所以需要我们
手动声明业务装配的注入规则。使用resultMap标签声明规则,把此种方式
称为ResultMap N+1方式
实现:
一对一:查询学生及其教师信息
一对多:查询教师及其学生信息
Student接口
//ReultMap N+1方式
//一对一关系:查询学生及其教师
List<Student> selStuAll();
<!-- ReultMap N+1方式 -->
<!-- 一对一关系:查询学生及其教师 -->
<resultMap type="student" id="rm">
<!-- 可直接写SQL语句或调方法 -->
<!-- <association property="teacher" column="tid" select="SELECT * FROM teacher WHERE tid=#{0}"></association> -->
<association property="teacher" column="tid" select="com.i.mapper.TeacherMapper.selTeaById"></association>
</resultMap>
<select id="selStuAll" resultMap="rm">
SELECT * FROM student
</select>
Teacher接口
//ReultMap N+1方式
//一对一关系:查询教师及其学生信息
List<Teacher> selTeaAll();
<!-- ReultMap N+1方式 -->
<!-- 一对多关系:查询教师及其学生信息 -->
<resultMap type="teacher" id="rt">
<!-- ofType List中泛型的类型 property为变量的名称 -->
<!-- <collection property="teacher" column="tid" select="SELECT * FROM student WHERE tid=#{0}"></collection> -->
<collection property="ls" column="tid" ofType="student" select="com.i.mapper.StudentMapper.selStuById"></collection>
</resultMap>
<!-- 查询主表数据 -->
<select id="selTeaAll" resultMap="rt">
SELECT * FROM teacher
</select>
测试
//ReultMap N+1方式
//一对一关系:查询学生及其教师信息
List<Student> ls2 = sm.selStuAll();
for (Student s : ls2) {
System.out.println(s);
}
System.out.println("==========");
//一对一关系:查询教师及其学生信息
List<Teacher> lt2 = tm.selTeaAll();
for (Teacher t : lt2) {
System.out.println(t);
}
resultMap 中 的 association 和 collection 的区别 ?
association 用于 一对一 和 多对一的情况
collection 用于 一对一 和 一对多 的情况
缓存
缓存简介:
缓存(Cache )是计算机领域非常通用的概念。
它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。
缓存中的数据是数据存储源中数据的拷贝,应用程序在运行时直接读写缓存中的数据,只在某些特定时刻按照缓存中的数据来同步更新数据存储源。
缓存的物理介质通常是内存,而永久性数据存储源的物理介质通常是硬盘或磁盘,应用程序读写内在的速度显然比读写硬盘的速度快,如果缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。
缓存的实现不仅需要作为物理介质的硬件,同时还需要用于管理缓存的并发访问和过期等策略的软件。
因此,缓存是通过软件和硬件共同实现的
缓存分类
类别 | 说明 |
---|---|
一级缓存 | 事务范围:缓存只能被当前事务访问。 缓存的生命周期依赖于事务的生命周期当事务结束时,缓存也就结束生命周期。 在此范围下,缓存的介质是内存 |
二级缓存 | 进程范围:缓存被进程内的所有事务共享。 这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。 缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。 进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。 |
三级缓存 | 集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。 缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式 |
MyBatis支持1级缓存和2级缓存,在实际开发中,实际上很少使用到MyBatis自带的缓存,大部分情况下,缓存都是使用EHCache,MemoryCache、Redis等等来实现缓存。
一级缓存
MyBatis中1级缓存是基于SqlSession的
二级缓存
二级缓存基于SqlSessionFactory,一级缓存的作用域只是SqlSession,范围比较下,用到的不多。二级缓存是进程级别的缓存,用的比较普遍,二级缓存mybatis本身没有提供,常用的主键有Ehcache和Redis,本文主要讲解Ehcache
1.导包
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
开启二级缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>