Mybatis的多表查询

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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值