多表查询 假设有两张表,分别是员工表和部门表。
多对一 查询员工以及员工所在的部门(需要在 User 实体类中定义一个 Dept 类型的属性 dept),可以有三种方法:
级联 使用级联方式,只需要在 resultMap 中定义 result 标签即可,dept 部门属性表示为 dept.xxx
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resultMap id ="empDeptMap" type ="Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <result column ="age" property ="age" > </result > <result column ="sex" property ="sex" > </result > <result column ="did" property ="dept.did" > </result > <result column ="dname" property ="dept.dname" > </result > </resultMap > <select id ="getEmpAndDeptByEid" resultMap ="empDeptMap" > select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did = dept.did where emp.eid = #{eid} </select >
association 使用 association 标签,可以指明属性 dept 和属性类型 Dept,在 association 标签中定义部门的映射关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <resultMap id ="empDeptMap" type ="Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <result column ="age" property ="age" > </result > <result column ="sex" property ="sex" > </result > <association property ="dept" javaType ="Dept" > <id column ="did" property ="did" > </id > <result column ="dname" property ="dname" > </result > </association > </resultMap > <select id ="getEmpAndDeptByEid" resultMap ="empDeptMap" > select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did = dept.did where emp.eid = #{eid} </select >
分步查询 分步查询就是首先查询员工信息,然后根据员工信息查询部门信息。
具体就是使用 association 的 select 属性和 column 属性 :
select 属性:设置分步查询,查询某个属性的值的 sql 的标识。
column 属性:将第一步中的查询结果中的字段,设置为下一步查询的输入值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <resultMap id ="empDeptStepMap" type ="Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <result column ="age" property ="age" > </result > <result column ="sex" property ="sex" > </result > <association property ="dept" select ="com.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep" column ="did" > </association > </resultMap > <select id ="getEmpByStep" resultMap ="empDeptStepMap" > select * from t_emp where eid = #{eid} </select > <select id ="getEmpDeptByStep" resultType ="Dept" > select * from t_dept where did = #{did} </select >
一对多 查询部门下的所有员工(需要在 Dept 实体类中定义 User List),可以有以下几种方法:
collection 使用 collection 标签可以表示一对多的关系,ofType 属性指明集合中存储的数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <resultMap id ="deptEmpMap" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="dname" column ="dname" > </result > <collection property ="emps" ofType ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="ename" column ="ename" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </collection > </resultMap > <select id ="getDeptEmpByDid" resultMap ="deptEmpMap" > select dept.*,emp.* from t_dept dept left join t_emp emp on dept.did = emp.did where dept.did = #{did} </select >
分步查询 即先查询部门,再查询部门下的所有员工。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <resultMap id ="deptEmpStep" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="dname" column ="dname" > </result > <collection property ="emps" fetchType ="eager" select ="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid" column ="did" > </collection > </resultMap > <select id ="getDeptByStep" resultMap ="deptEmpStep" > select * from t_dept where did = #{did} </select > <select id ="getEmpListByDid" resultType ="Emp" > select * from t_emp where did = #{did} </select >
延迟加载 分步查询的好处就是可以实现延迟加载 。
MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载。在 MyBatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false
,然后通过设置 association 和 collection 中的 fetchType 属性设置当前分步查询是否延迟加载(lazy 延迟加载,eager 立即加载)。
原理 MyBatis 延迟加载实现的原理是,使用 CGLIB 创建目标对象的代理对象 。
当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName() ,拦截器 invoke() 方法发现 a.getB() 是 null 值,那么就会执行查询关联 B 对象的 sql 语句去查询,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。
缓存 MyBatis 有缓存的功能,将 select 语句缓存到内存中,下⼀次还是这条相同 select 语句的话,直接从缓存中取,不再查数据库。MyBatis 的缓存包括:
一级缓存:将查询到的结果存储到 SqlSession 中。
二级缓存:将查询到的数据存储到 SqlSessionFactory 中。
还有一些第三方缓存,如 EHCache、Memcache 等。
一级缓存 一级缓存是 SqlSession 级别的,同一个 SqlSession 下查询的数据会被缓存。一级缓存失效的原因:
使用不同的 SqlSession。
查询语句发生变化。
查询之间有增删改操作。
手动调用了 clearCache() 方法,手动清空一级缓存。
二级缓存 二级缓存是 SqlSessionFactory 级别的,二级缓存需要手动开启:
在配置文件中设置 <setting name="cacheEnabled" value="true">
开启缓存,默认是 true。
在需要使⽤二级缓存的映射文件中添加一个标签:<catche />
。
使用二级缓存的实体类必须是可序列化的,也就是实现 java.io.Serializable 接口。
SqlSession 对象关闭或者提交后,一级缓存中的数据才会被写入到二级缓存中。
配置 二级缓存在 <cache />
标签中配置,可选的配置项:
eviction:淘汰算法。
LRU
FIFO
SOFT:淘汰软引用指向的对象。
WEEK:淘汰弱引用指向的对象。
flushInterval:刷新间隔,单位毫秒。默认是不刷新。
readOnly:只读属性。
true:缓存返回相同的实例,可能存在并发问题。
false:通过序列化返回对象的拷贝,线程安全,默认为 false。
size:缓存的对象数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出。