20210706_分页,延时加载,缓存
内容
1.mybatis中分页插件
2.懒加载(延时加载)(关联查询,分布查询)
3.缓存(一级缓存 自带 和二级缓存需要进行设置概念清楚 redis替换)
一、MyBatis的分页插件(会使用)
使用步骤
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、需求:查询商品信息,有时需要关联出类型信息
问题:
第一种方法:我们直接关联查询出所有商品和类型的信息
select * from goods o inner join type on type.tid=goods.tid
分析:
①、这里我们一次查询出所有的信息,需要什么信息的时候直接从查询的结果中筛选。但是如果商品信息和商品类型都比较大的时候,这种关联查询肯定比较耗时。
②、我们的需求是有时候需要关联查询类型信息,这里不是一定需要类型信息的。即有时候不需要查询类型信息,我们也查了,程序进行了多余的耗时操作。
第二种方法:分步查询,首先查询出所有的商品信息,然后如果需要类型的信息,我们在根据查询的商品信息去类型的信息
- `//查询所有的订单信息,包括类型tid``select * from goods;```
//如果需要用户信息,我们在根据上一步查询的用户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
<setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="true"/>

- 深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的
select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。
在核心配置文件中 settings只要写<setting name="lazyLoadingEnabled" value="true"/>
或者
<setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>4.需要注意
延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的 select 语句,不能是使用多表连接所进行的select查询。因为多表连接查询,其实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。MyBatis中对于延迟加载设置,只对于resultMap中的collection和association起作用,可以应用到一对一、一对多、多对一、多对多的所有关联关系查询中。5.懒加载总结
通过上面的示例可以发现深度加载的方式最为懒,通过这种方式可以让mybatis在执行查询的时候减少sql的查询从而提高程序的执行效率,但是并不是所有场景下使用懒加载都能提高效率,有些场景比如在查询一对多时,就需要将一方和多方都查询出来,这样的话开启懒加载反而有可能会拖慢程序的执行效率。三、mybatis的缓存【理解,掌握概念】
1.什么是缓存,为什么需要缓存
为了完成一个查询功能,我们需要频繁去操作数据库,操作数据库的频率太高,查询效率不高
引入缓存之后,第一次去查询数据库,第二次以后,如果数据库的数据没有发生改变,我们就不去查询数据库,我们查缓存。使用缓存之后,就减少了查询数据库的次数,从而提高了查询效率2.哪些数据使用放在缓存中,哪些数据不适合放在缓存中
1)不频繁更新的数据,频繁发生更新的数据,不要放在缓存中
2)财务数据不要放在缓存中,不要的数据可以放在缓存中3.mybatis的缓存
mybatis中的缓存可以分为一级缓存和二级缓存,一级缓存mybatis自带,不要额外配置,属于sqlSession级别的缓存(和SqlSession相关的),二级缓存默认系统开启,如果系统需要二级缓存,那么需要人为配置,二级别缓存属于Mapper级别的缓存
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: 1Dept [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条sqlSqlSession 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);
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);
3.3 二级缓存
将查询出的数据保存在二级缓存中需要通过调用sqlSession的commit方法才可以进行保存在二级缓存
二级缓存是用来解决一级缓存不能跨会话共享(Sqlsession)的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,
MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库。
配置二级缓存的步骤
1)开启二级缓存
在mybatis-config.xml文件中开启二级缓存(默认有就是true)
<setting name="cacheEnabled" value="true"></setting>2)在Mapper映射文件中使用开启用二级缓存
-
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();}
在运行完程序,之后会报如下错误
提示:
使用二级缓存时,与查询结果映射的Java对象必须实现java.io.Serializable接口的序列化和反序列化操作,如果存在父类,其成员都需要实现序列化接口。实现序列化接口是为了对缓存数据进行序列化和反序列化操作,因此二级缓存数据存储介质多样,不一定在内存,有可能是硬盘或者远程服务器。
4)查询出的对象上进行序列化
Class Dept implements Serializable{省略}
如上二级缓存就起到了作用
3.3 映射文件中的配置
关于二级缓存中属性的解释,<cachetype="org.apache.ibatis.cache.impl.PerpetualCache"size="1024"eviction="LRU"flushInterval="120000"readOnly="false"></cache>
flushInterval:刷新间隔。
可以被设置为任意的正整数,而且他们代表一个合理的毫秒形式的时间段,默认情况下不设置,也就没有刷新间隔,缓存仅仅调用语句时刷新。
size:缓存数目。
可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目,默认值为1024
readOnly:只读。
该属性可以被设置为true或者false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势。可以读写的缓存会返回缓存对象拷贝(通过序列化)。这种方式会慢一些,但是安全,因此默认是false.
eviction:回收策略,
LRU:最近最少使用的策略,
FIFO:先进先出策略,
SOFT:软引用策略,
WEAK:弱引用策略,
更积极地移除基于垃圾收集器状态和弱引用规则的对象。
