小言_互联网的博客

Mybatis多表关联查询、懒加载、缓存

370人阅读  评论(0)

Mybatis多表关联查询、懒加载、缓存

多表关联查询:

手动处理映射关系:

数据库表格和java类之间的对应关系

  1. 自动处理映射关系

    自动根据java类的属性名和数据库的字段名封装数据 要求字段名和属性名相同

  2. 手动处理映射关系

Java中的实体类和数据库表格的字段名不同 怎么处理

  1. 字段使用别名 缺点 SQL语句复用度低 无法处理多变关联查询问题
  2. 定义映射关系 映射关系可以在多处使用 可以处理数据库表格之间一对一 一对多 多对多的关系

映射的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语句的查询结果

优缺点:

  1. 减少了查询的次数
  2. 造成数据的脏读

一级缓存产生了脏读怎么办?(一级缓存默认开启,无法关闭)

  1. 手动刷新,清空缓存sqlSession.clearCache()
  2. 发生了增删改(之后的提交动作),也会默认清空缓存
二级缓存:

​ 一级缓存作用于一个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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场