20210706_分页,延时加载,缓存


内容

1.mybatis中分页插件

2.懒加载(延时加载)(关联查询,分布查询)

3.缓存(一级缓存 自带 和二级缓存需要进行设置概念清楚 redis替换)

一、MyBatis的分页插件(会使用)

关于分页myBatis的PageHelper根据简单

使用步骤

1.下载pageHelper的jar;

(两个jar包:pagehelper-5.1.0-beta2.jar和jsqlparser-1.0.jar)

2.在mybatis的核心文件中进行配置,

注意核心配置文件中标签的顺序
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>

3.代码中

SqlSession s=MyBatisUtil.getSqlSession();

GoodsDao dao=s.getMapper(GoodsDao.class);

PageHelper.startPage(2, 5);//第一个参数是开始的页,第二个参数是每页显示的条数

List<Goods> g=dao.findAll();

/**
* PageInfo中的属性
* count=true, pageNum=1, pageSize=2, startRow=0,
* endRow=2, total=5, pages=3, reasonable=false, pageSizeZero=false
*/
PageInfo<Emp1> pageInfo = new PageInfo<>(list);//对集合数据和分页数据进行分装
session.close();

二、延时加载(懒加载)

懒加载,查询时,可以根据需求去查询,不需要就不加载,需要时才加载
内容

  • 1、需求:查询订单信息,有时候需要关联查出用户信息。
  • 2、什么是懒加载?
  • 3、具体实例
  • 4、总结
    本章我们讲如何通过懒加载来提高mybatis的查询效率。

1、需求:查询商品信息,有时需要关联出类型信息

问题:
  第一种方法:我们直接关联查询出所有商品和类型的信息

  1. select * from goods o inner join type on type.tid=goods.tid

  分析:
  ①、这里我们一次查询出所有的信息,需要什么信息的时候直接从查询的结果中筛选。但是如果商品信息和商品类型都比较大的时候,这种关联查询肯定比较耗时。
  ②、我们的需求是有时候需要关联查询类型信息,这里不是一定需要类型信息的。即有时候不需要查询类型信息,我们也查了,程序进行了多余的耗时操作。
  第二种方法:分步查询,首先查询出所有的商品信息,然后如果需要类型的信息,我们在根据查询的商品信息去类型的信息

  1. `//查询所有的订单信息,包括类型tid``select * from goods;```
  2. //如果需要用户信息,我们在根据上一步查询的用户tid查询类型的信息``select * from type where tid=#{tid}

  分析:
  ①、这里两步都是单表查询,执行效率比关联查询要高很多
  ②、分为两步,如果我们不需要关联用户信息,那么我们就不必执行第二步,程序没有进行多余的操作。
  那么我们说,这第二种方法就是mybatis的懒加载。

2、什么是懒加载?

  通俗的讲就是按需加载,我们需要什么的时候再去进行什么操作。而且先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
  在mybatis中,resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

3.步骤

3.1.延迟加载(1.关联关系使用association、collection,2.分布查询)

  • MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库访问压力。MyBatis`的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

<resultMap type="entity.Goods" id="map1">
<id column="gid" property="gid"/>
<result column="gname" property="gname"/>
<association property="type" select="dao.TypeDao.findByTid" column="tid" javaType="entity.Type"></association>
</resultMap>

<select id="lazyFind" resultMap="map1" resultType="java.util.List">
select * from goods
</select>

3.2. 加载时机,配置为想要的加载机制

MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式延迟加载与深度延迟加载

  • 直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select查询。(默认的
  • 侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。需要在核心配置文件中配置如下setting
  1. <setting name="lazyLoadingEnabled" value="true"/>
  2. <setting name="aggressiveLazyLoading" value="true"/>

Day19分页插件 - 图1

  • 深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select查询。
    在核心配置文件中 settings只要写
    1. <setting name="lazyLoadingEnabled" value="true"/>

或者

  1. <setting name="lazyLoadingEnabled" value="true"/>
  2. <setting name="aggressiveLazyLoading" value="false"/>

    4.需要注意

    延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的 select 语句,不能是使用多表连接所进行的select查询。因为多表连接查询,其实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
    MyBatis中对于延迟加载设置,只对于resultMap中的collectionassociation起作用,可以应用到一对一、一对多、多对一、多对多的所有关联关系查询中。

    5.懒加载总结

    通过上面的示例可以发现深度加载的方式最为懒,通过这种方式可以让mybatis在执行查询的时候减少sql的查询从而提高程序的执行效率,但是并不是所有场景下使用懒加载都能提高效率,有些场景比如在查询一对多时,就需要将一方和多方都查询出来,这样的话开启懒加载反而有可能会拖慢程序的执行效率。

    三、mybatis的缓存【理解,掌握概念】

    1.什么是缓存,为什么需要缓存

    为了完成一个查询功能,我们需要频繁去操作数据库,操作数据库的频率太高,查询效率不高
    引入缓存之后,第一次去查询数据库,第二次以后,如果数据库的数据没有发生改变,我们就不去查询数据库,我们查缓存。使用缓存之后,就减少了查询数据库的次数,从而提高了查询效率

    2.哪些数据使用放在缓存中,哪些数据不适合放在缓存中

    1)不频繁更新的数据,频繁发生更新的数据,不要放在缓存中
    2)财务数据不要放在缓存中,不要的数据可以放在缓存中

    3.mybatis的缓存

    mybatis中的缓存可以分为一级缓存和二级缓存,一级缓存mybatis自带,不要额外配置,属于sqlSession级别的缓存(和SqlSession相关的),二级缓存默认系统开启,如果系统需要二级缓存,那么需要人为配置,二级别缓存属于Mapper级别的缓存
    Day19分页插件 - 图2

    3.1 一级缓存,代码演示

    SqlSession session=MyBatisUtil.sqlSessionFactory.openSession();

Dept dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);
System.out.println("*****************************");
Dept dept1=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept1);
结果,只发送了一条sql语句
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@69a3d1d]
==> Preparing: select did,dname ,state st from dept where did=1
==> Parameters:
<== Columns: did, dname, st
<== Row: 1, test1, 1
<== Total: 1
Dept [did=1, dname=test1, address=null, st=1, stateStr=null, emps=null, countEmps=0]
*****************************
Dept [did=1, dname=test1, address=null, st=1, stateStr=null, emps=null, countEmps=0]
通过如上控制台的语句会发现,第二次没有重新发出SQL语句,证明第二次是从缓存中查的,而不是从数据库中查的

3.2 一级缓存什么失效

有如下四种情况,一级缓存就失效了

1)两次查询数据不一样

如果第一次查询的数据和第二次查询的数据不一样,发送2条sql,第3操作时,使用了缓存
Dept dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);
System.out.println("*****************************");
Dept dept1=session.getMapper(DeptMapper.class).findById(2);
System.out.println(dept1);
System.out.println("**********************************");
dept1=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept1);

2)查询数据一样,调用了DML

如果查询的是同样的数据,调用了更新方法(增删改DML)的方法,如下代码提交3条sql语句
SqlSession session=MyBatisUtil.sqlSessionFactory.openSession();

Dept dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);
System.out.println("*****************************");

session.getMapper(DeptMapper.class).add(new Dept("公关部", 1));

System.out.println("**********************************");
dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);

session.commit();
session.close();
虽然依然还是查询的是相同部门,但是中间如果完成了更新操作,缓存是刷新的

3) 清除session方法

发送2条sql
SqlSession session=MyBatisUtil.sqlSessionFactory.openSession();

Dept dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);
System.out.println("*****************************");

session.clearCache();//清除一级缓存

System.out.println("**********************************");
dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);

session.close();

4)如果 sqlSession不一样

多个SqlSession对象,发送了两条sql语句,一级缓存就是sqlSession范围的缓存
SqlSession session=MyBatisUtil.sqlSessionFactory.openSession();

Dept dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);
System.out.println("*****************************");

System.out.println("**********************************");
session=MyBatisUtil.sqlSessionFactory.openSession();
dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);

session.close();

3.3 二级缓存

将查询出的数据保存在二级缓存中需要通过调用sqlSession的commit方法才可以进行保存在二级缓存
二级缓存是用来解决一级缓存不能跨会话共享(Sqlsession)的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,
MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库。

配置二级缓存的步骤

1)开启二级缓存

在mybatis-config.xml文件中开启二级缓存(默认有就是true)

  1. <setting name="cacheEnabled" value="true"></setting>

    2)在Mapper映射文件中使用开启用二级缓存

  2. <cache></cache>

    3)测试

    SqlSession session=MyBatisUtil.sqlSessionFactory.openSession();

Dept dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);
System.out.println("*****************************");
session.commit();

System.out.println("**********************************");
session=MyBatisUtil.sqlSessionFactory.openSession();
dept=session.getMapper(DeptMapper.class).findById(1);
System.out.println(dept);

session.close();
}
在运行完程序,之后会报如下错误
Day19分页插件 - 图3
提示:
使用二级缓存时,与查询结果映射的Java对象必须实现java.io.Serializable接口的序列化和反序列化操作,如果存在父类,其成员都需要实现序列化接口。实现序列化接口是为了对缓存数据进行序列化和反序列化操作,因此二级缓存数据存储介质多样,不一定在内存,有可能是硬盘或者远程服务器

4)查询出的对象上进行序列化

Class Dept implements Serializable{
省略
}
Day19分页插件 - 图4
如上二级缓存就起到了作用

3.3 映射文件中的配置

关于二级缓存中属性的解释
<cache
type="org.apache.ibatis.cache.impl.PerpetualCache"
size="1024"
eviction="LRU"
flushInterval="120000"
readOnly="false"
></cache>

flushInterval:刷新间隔。

可以被设置为任意的正整数,而且他们代表一个合理的毫秒形式的时间段,默认情况下不设置,也就没有刷新间隔,缓存仅仅调用语句时刷新。

size:缓存数目。

可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目,默认值为1024

readOnly:只读。

该属性可以被设置为true或者false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可以读写的缓存会返回缓存对象拷贝(通过序列化)。这种方式会慢一些,但是安全,因此默认是false.

eviction:回收策略,

默认为LRU。有如下几种。

LRU:最近最少使用的策略,

移除最长时间不被使用的对象。

FIFO:先进先出策略,

按对象进入缓存的顺序来移除它们。

SOFT:软引用策略,

移除基于垃圾回收器状态和软引用规则的对象

WEAK:弱引用策略,

更积极地移除基于垃圾收集器状态和弱引用规则的对象。