Mybatis多表关联查询、懒加载、缓存
多表关联查询:
手动处理映射关系:
数据库表格和java类之间的对应关系
-
自动处理映射关系
自动根据java类的属性名和数据库的字段名封装数据 要求字段名和属性名相同
-
手动处理映射关系
Java中的实体类和数据库表格的字段名不同 怎么处理
- 字段使用别名 缺点 SQL语句复用度低 无法处理多变关联查询问题
- 定义映射关系 映射关系可以在多处使用 可以处理数据库表格之间一对一 一对多 多对多的关系
映射的xml
<!--手动映射;type是返回值,返回的是Emp对象-->
<resultMap id="empresultMap" type="emp">
<!--column是在数据库中对应的字段名;property是实体类中的字段名-->
<id column="empno" property="id"></id>
<result column="ename" property="name"></result>
</resultMap>
<!--id:方法名;resultMap就是上面的手动映射的id-->
<select id="findEmpByEmpno" resultMap="empresultMap">
select * from emp where empno = #{empno}
</select>
一对一的关系处理:
什么时候用到这个关系呢?当从数据库中查询出的结果是两个表的数据的时候(查询的结果只是一条数据),需要将结果封装为两个对象的属性。
如下有两个表Emp,Dept;
1.需要将Dept对象组合为Emp对象的属性
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
public Dept dept;
}
public class Dept {
private Integer deptno;
private String dname;
private String loc;
}
2.xml映射文件
<resultMap id="empJoinDept" type="emp">
<!--手动处理映射关系,column数据库的字段名与是一类的字段名property相对应-->
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="comm"></result>
<result column="deptno" property="deptno"></result>
<!--处理组合的另一个实体类,property是emp中的属性字段名,javaType是Dept实体类的别名;将emp中的dept属性组合Dept实体类-->
<association property="dept" javaType="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
</association>
</resultMap>
<select id="findEmpJoinDept" resultMap="empJoinDept">
SELECT *
FROM emp e LEFT JOIN dept d ON e.DEPTNO=d.DEPTNO
WHERE e.EMPNO = #{empno}
</select>
一对多的关系处理:
什么时候用到这个关系呢?当从数据库中查询出的结果是两个表的数据的时候(查询的结果是多条数据,其中一个表的数据是相同的,只有另一个表的数据不相同时),需要将结果封装为两个对象的属性(数据不同的返回的应该是一个集合)。
如下有两个表Emp,Dept(查询的结果Dept的属性相同,Emp的属性有多个)
1.需要将Emp对象组合为Dept对象的属性
public class Dept {
private Integer deptno;
private String dname;
private String loc;
private List<Emp> empList;//Emp查询出多条数据,所以返回的是集合
}
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
}
映射的xml文件
<!--type是返回值,dept是实体类Dept的别名-->
<resultMap id="deptJoinEpm" type="dept">
<!--column是数据库中字段的名称,property是实体类中属性的名称-->
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
<!--property是dept属性中的字段名(组合的Emp对象名),ofType是集合中的每个元素的类型(返回的是一个Emp对象的集合)-->
<collection property="empList" ofType="emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="comm"></result>
<result column="deptno" property="deptno"></result>
</collection>
</resultMap>
<select id="findDeptJoinEmp" resultMap="deptJoinEpm">
SELECT *
FROM dept d LEFT JOIN emp e ON d.deptno =e.DEPTNO
WHERE d.deptno= #{deptno}
</select>
多对多关系处理:
当表之间存在多对多关系时;会将两个表的主键作为第三张表的主键;可以将多对多的关系变为一对多和一对一。
如下xml映射文件
<resultMap id="empJoinProject" type="emp">
<!--emp本身的8个属性-->
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="comm"></result>
<result column="deptno" property="deptno"></result>
<!--property Emp的属性;ofType封装的实体类的别名;封装第三张表的两个属性以及一对一关系的实体类的组合-->
<collection property="workrecordes" ofType="workrecorde">
<id column="empno" property="empno"></id>
<id column="pid" property="pid"></id>
<association property="project" javaType="project">
<id column="pid" property="pid"/>
<result column="pname" property="pname"/>
</association>
</collection>
</resultMap>
<select id="findEmpJoinProject" resultMap="empJoinProject">
SELECT *
FROM emp e LEFT JOIN workrecorde w ON e.EMPNO=w.empno
LEFT JOIN project p ON w.pid=p.pid
WHERE e.EMPNO=#{empno}
</select>
SQL语句之间的相互调用
进行多表查询的时候,可以调用另一xml文件进行帮助查询
如下是主Xml文件
<resultMap id="workrecordJoinProject" type="workrecorde">
<id column="empno" property="empno"></id>
<id column="pid" property="pid"></id>
<!--column将上面查出的pid作为参数,调用下面的映射文件-->
<association property="project" javaType="project" column="pid" select="mapper.ProjectMapper.finfProjectJoinEmps">
</association>
</resultMap>
<select id="findWorkrecordeJoinProjectByEmpno" resultMap="workrecordJoinProject">
SELECT *
FROM workrecorde WHERE empno=#{empno}
</select>
被调用的xml文件
<mapper namespace="mapper.ProjectMapper">
<!--手动映射;type是返回值,返回的是Emp对象-->
<select id="finfProjectJoinEmps" resultType="project">
select * from project where pid = #{pid}
</select>
</mapper>
懒加载;也叫延迟加载的设置
当进行多表查询进行sql之间的相互调用时,会产生很多sql语句一起加载,这时可以再核心配置文件中设置延迟加载,即什么时候用到所需要的数据的时候再执行sql语句
mybatis.xml核心配置文件的设置,设置下面两个属性
<settings>
<!--全局启用或禁用延迟加载。当禁用时, 所有关联对象都会即时加载。 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当启用时, 有延迟加载属性的对象在被 调用时将会完全加载任意属性。否则, 每种属性将会按需要加载。 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
mybatis缓存
一级缓存:
SQLSession对象内部的缓存;在同一个SqlSession对象中调用的方法和传入的参数一致时,多次查询,mybatis默认使用缓存技术减少查询的次数
什么情况下 一级缓存有作用 (一级缓存默认开启)
-
同一个SQLSession
-
调用的方法 SQL语句的名称空间和id相同
-
同一个参数
SqlSession内部的缓存如何实现:
通过Map集合实现缓存
键:名称空间+SQL的id+参数+SQLSession的哈希码
值:SQL语句的查询结果
优缺点:
- 减少了查询的次数
- 造成数据的脏读
一级缓存产生了脏读怎么办?(一级缓存默认开启,无法关闭)
- 手动刷新,清空缓存sqlSession.clearCache()
- 发生了增删改(之后的提交动作),也会默认清空缓存
二级缓存:
一级缓存作用于一个SqlSession
二级缓存作用于一个namespace
以名称空间作为缓存的标志 每一名称空间都有自己独立的缓存区域
多个mapper对象是否可以是同一个名称空间? 可以 二级缓存可以跨 mapper对象 必须同一个接口的mapper对象
多个SQLSession对象 是否可以使用同一个名称空间? 可以二级缓存 可以跨SQLSession对象
二级缓存不是自动开启的:
因为在项目中 SQLsession对象应该避免大量创建
同一个接口下 的mapper代理对象应该避免大量创建 所以二级缓存 用的不多
如何开启耳机缓存?
- 核心配置文件中开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- Mapper映射文件中 开启针对于某个名称空间的二级缓存
<mapper namespace="mapper.WorkrecordMapper">
<cache/>
</mapper>
- 针对于SQL语句是否使用二级缓存的设置
<!--useCache 控制当前sql语句是否使用二级缓存,默认是true,默认开启;flushCache 控制当前的sql语句执行一次后,是否会刷新缓存,默认是false-->
<select id="findWorkrecordeJoinProjectByEmpno" resultMap="workrecordJoinProject" useCache="true" flushCache="false">
SELECT *
FROM workrecorde WHERE empno=#{empno}
</select>
注意:开启二级缓存是实体类要实现系列化接口
默认的属性和参数
参数
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
如下例子:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是 LRU:
-
LRU – 最近最少使用的:移除最长时间不被使用的对象。
-
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
-
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
-
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
转载:https://blog.csdn.net/qq_42488087/article/details/101553200