IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> Mybatis的一级、二级缓存 -> 正文阅读

[PHP知识库]Mybatis的一级、二级缓存

相关内容总结说明:
mybatis默认开启一级缓存,同一个sqlSession多次查询同一个namespace+id时,只会第一次查询数据库,后面的查询将直接从一级缓存中读取。

?

当执行sqlSession.commit()时,会刷新一级缓存,也就是清空一级缓存,如果此时开启了二级缓存,则会将一级缓存的结果写入二级缓存中保存。

?

mybatis的二级缓存需要设置才能开启。首先需要在mybatis-config.xml的settings标签中添加 < setting name = "cacheEnabled" value = "true" />。
然后在需要开启二级缓存的sqlmapper文件(如HeroMapper.xml文件)中加上? < cache ></ cache >?或? < cache/>.
并且对应的Hero对象需要继承Serializable(表示将对应的对象从内存写入硬盘)
mybatis的二级缓存写入时刻有:
1)sqlSession.commit();?清空一级缓存,如果开启了二级缓存,则 可能会将一级缓存的内容写入二级缓存( 如果commit前都是查询操作,则写入二级缓存,如果存在增删改操作,再commit,则清空二级缓存)。
注意:若当前某个sqlSession只进行了一系列查询,然后执行commit(),就将其对应的一级缓存内容写入二级缓存中。所以二级缓存存放的可以能多个sqlSession对象的一级缓存写入的内容。
但如果此时 任一个sqlSession进行了增删改操作后,再执行commit(),则会 清空二级缓存。此时 所有对应的sqlSession对象查询时,都不会命中。
二级缓存清空后,所有的sqlSession的查询会从头开始,即如果只是sqlSession查询,不commit()也不close(),第一次从数据库查询,二次及以上查询将从一级缓存中返回数据。

?

2)sqlSession.close();? 这个方式主要是在多个sqlSession对象访问同一个mapper接口时用到。比如?sqlSession1?访问?HeroMapper.class?并进行了查询操作,然后执行 sqlSession1.close(),就会将sqlSession1?查到的数据写入二级缓存中 ,SqlSession2?如果访问?HeroMapper.class?进行了相同的查询操作,就可以访问到?sqlSession1?之前查过的缓存数据了。
注意:sqlSession1.close()之后,sqlSession1?就不可以再用来查询数据了,所以访问二级缓存的数据时,不可能是当前的sqlSession执行close()之后,再次查询数据,都是查的其他的sqlSession对应写入到二级缓存的数据。

mybatis开启二级缓存后,查询一级缓存、二级缓存、数据库的先后顺序是:

二级缓存 ——》 一级缓存 ——》 数据库

说明:可能大家都会觉得应该是先 一级缓存,再二级缓存,最后数据库。但其实际查询的先后顺序从控制台日志就可以看出来。

每次查询结果前,不管是否输出的SQL(即查询数据库),首先展示的都是命中率,而命中率是相对二级缓存是否可以查到数据而言的,这就说明了,每次数据查询,最先查询的都是二级缓存。

没有写入二级缓存,执行二次及以上次查询时,显示命中率是0,但不输出查询SQL,这说明结果是从一级缓存中返回的,也就是先在二级缓存中查了,没查到,然后去一级缓存中查,查到了,返回。

在上面说到二级缓存写入时刻时,对commit()重点进行了说明,如果某个sqlSession进行了增删改操作,然后commit(),会清空二级缓存;但如果没有哪个sqlSession进行增删改操作,或者一直不commit(),则会一直命中。这个时候的命中率的提升,说明当二级缓存中有数据时,结果将直接从二级缓存中返回,因为如果sqlSession查询不commit(),按道理是会将结果写入一级缓存的,但此时命中率一直升高,而且不输出SQL,只能说明是先查的二级缓存。二级缓存中有数据时,多次查询是否了写入一级缓存,估计是没有。

所以 顺序是:二级缓存 ——》 一级缓存 ——》 数据库

具体说明:
1.一级缓存
? ??1) mybatis默认开启一级缓存
? ? 2)一级缓存是相对于 同一个 SqlSession?而言的,用同一个SqlSession查询查询同样的数据,只会执行一次SQL,因为使用SqlSession第一次查询后,mybatis会将结果放到SqlSession中。除非使用SqlSession.commit() 。
? ? 3)SqlSession.commit()会刷新一级缓存。sqlsession.close()也会清空一级缓存。
? ? 4)与执行select不同的是,执行update,insert,delect操作后会清空一级缓存中的数据,而不是通过算法生成缓存的键值存入一级缓存,之所以有这种差别是因为 select的flushCache(清空缓存)默认为false,而update,insert,delect的flushCache(清空缓存)默认为true。
如果Mybatis-config.xml文件中的TransactionManage的 type 是 ?JDBC?类型,则需要手动?commit。
?? ??? ??? ?< environment id= "development1" >
?????????????? < transactionManager type= "JDBC" ></ transactionManager >
?????????????? < dataSource type= "POOLED" >
?????????????????? < property name= "driver" value= "${how2java.driver}" />
?????????????????? < property name= "url"? value= "${how2java.url}" />
?????????????????? < property name= "username" value= "${how2java.username}" />
?????????????????? < property name= "password" value= "${how2java.password}" />
?????????????? </ dataSource >
????????? </ environment >
? ? 5)在Java代码中,增删改操作后,有时不调用sqlSession.commit(),发现查到的数据也已经更新。但实际在数据库中查询数据,却发现没有更新。
?? ?
?? ?原因是因为这个原因是因为mybatis默认不是自动提交事务的, 所以其实没有修改数据库,刚刚更新完后立即返回的结果, 是从mybatis为了提高性能设置的缓存里读取的,不是从数据库读取的。
解决的办法:一是在openSession() 的括号里写true, 设定自动提交事务, 一是在代码中加入 sqlSession.commit()
————————————————
版权声明:本文为CSDN博主「陈君豪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: mybatis显示修改数据库成功, 但没有修改数据库的原因 (mybatis可以查询但无法修改数据库)_howard789的博客-CSDN博客
???????? ??? ? SqlSession sqlSession2? = sessionFactory.openSession();
??????????????? HeroMapper heroMapper = sqlSession2.getMapper(HeroMapper. class);
??????????????? Hero hero4 = heroMapper.selectHeroById(3);
??????????????? System. out.println( hero4);
???????????????
??????????????? Hero hero = new Hero();
??????????????? hero.setId(3);
??????????????? hero.setName( "test");
??????????????? hero.setHp(22.4);
??????????????? hero.setDamage(33);
??????????????? heroMapper.updateHeroById( hero);
//????????????? sqlSession.commit(); ? ?//更新操作后不提交
???????????????
??????????????? Hero hero2 = heroMapper.selectHeroById(3);
??????????????? System. out.println( hero2);
?? ?在这个代码操作中,更新ID=3的Hero对象后,不对sqlSession进行提交,虽然?控制台?中打印的结果看,hero2? 已经?在 hero4?的基础上更新了,但实际在数据库中查询的还是?hero4?的数据。
? ? 从控制台输出也可以看出:
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 396283472.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@179ece50]
DEBUG [main] - ==>? Preparing: select id,name,hp,damage from hero where id = ??
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==????? Total: 1
id:3,name:小先,hp:27.0,damage:100.0? ? ? 【hero4对象】
DEBUG [main] - ==>? Preparing: update hero set name=?,hp=?,damage=? where id =??
DEBUG [main] - ==> Parameters: test(String), 22.4(Double), 33.0(Double), 3(Integer)
DEBUG [main] - <==??? Updates: 1
DEBUG [main] - ==>? Preparing: select id,name,hp,damage from hero where id = ??
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==????? Total: 1
id:3,name:test,hp:22.4,damage:33.0 ? ? ?【hero2对象,输出的是更新后的数据】
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4135c3b]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4135c3b]
DEBUG [main] - Returned connection 68377659 to pool.
DEBUG [main] - Rolling back JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@179ece50]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@179ece50]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@179ece50]
DEBUG [main] - Returned connect ion 396283472 to pool.
?? ?? ? 【因为transactionManage的type类型是JDBC,mybatis不会自动commit,hero2只是从缓存中读取的,最终因为没有手动commit,所以在断开connection前,进行了rollback 】
?? ?? ? 如果希望更新的数据写入数据库,有两种方式:
? ? ?? ?1)SqlSession sqlSession2? = sessionFactory.openSession(true);
? ? 该方式会将增删改后的数据写入数据库,但不会清空二级缓存。
? ? 2)更新操作后进行? sqlSession2.commit();?即,将上面代码的注释放开
? ? 该方式会将增删改后的数据?写入数据库,同时清空二级缓存。
2.二级缓存
?? ? Mybatis 的二级缓存是指mapper映射文件。二级缓存的作用域是 同一个namespace 下的mapper 映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。
在同一个namespace下的mapper文件中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,并且执行了 sqlSession.commit(),则该SQLSession对应的mapper映射文件的二级缓存会被清空。
? ? 示例:
?? ??? ??? ???Reader rs = Resources. getResourceAsReader( "mybatis-config.xml");
??????????????? SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build( rs);
???????????????
??????????????? SqlSession sqlSession1? = sessionFactory.openSession();
???????????????
??????????????? // sqlsession的两种使用方式:
??????????????? //a.实例操作 参数:SQLMapper映射文件里面要调用的ID
??????????????? String statement = "com.how2jave.mybatis_demo.mapper.HeroMapper.selectHeroById";
??????????????? Hero hero1 = sqlSession1.selectOne( statement,3);
??????????????? System. out. println( hero1);?? ?? ? ? ? ? ??
?? ??? ??? ??? ?HeroMapper heroMapper1 = sqlSession1.getMapper(HeroMapper. class);
??????????????? Hero hero3 = heroMapper1.selectHeroById(3);
??????????????? sqlSession1.close();? //关闭sqlSession1后,会将sqlSession1的一级缓存内容写入HeroMapper.class对应的二级缓存中
???????????????
??????????????? //b.基于mapper接口的操作
??????????????? SqlSession sqlSession2? = sessionFactory.openSession( true);? ?//使用方式1将更新的数据写入数据库,该方式不会清空二级缓存
??????????????? HeroMapper heroMapper = sqlSession2.getMapper(HeroMapper. class);
??????????????? Hero hero = new Hero();
??????????????? hero.setId(3);
??????????????? hero.setName( "test");
??????????????? hero.setHp(22.4);
??????????????? hero.setDamage(33);
??????????????? heroMapper.updateHeroById( hero);
//????????????? sqlSession2.commit(); ? ? //将更新的数据写入数据库的方式2,该方式会清空二级缓存,先注释掉
???????????????
??????????????? Hero hero2 = heroMapper.selectHeroById(3);
??????????????? Hero hero6 = heroMapper.selectHeroById(3);
??????????????? Hero hero7 = heroMapper.selectHeroById(3);
??????????????? Hero hero8 = heroMapper.selectHeroById(3);
?? ?
console的信息如下:
DEBUG [main] - Cache Hit Ratio [com.how2jave.mybatis_demo.mapper.HeroMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 1427646530.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55182842]
DEBUG [main] - ==>? Preparing: select id,name,hp,damage from hero where id = ??
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==????? Total: 1
id:3,name:小先,hp:27.0,damage:100.0
DEBUG [main] - Cache Hit Ratio [com.how2jave.mybatis_demo.mapper.HeroMapper]: 0.0
id:3,name:小先,hp:27.0,damage:100.0? ? 【sqlSession1的一级缓存,没有查询数据库,直接从一级缓存中获取了数据】
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55182842]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55182842]
DEBUG [main] - Returned connection 1427646530 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 1427646530 from pool.
DEBUG [main] - ==>? Preparing: update hero set name=?,hp=?,damage=? where id =??
DEBUG [main] - ==> Parameters: test(String), 22.4(Double), 33.0(Double), 3(Integer)
DEBUG [main] - <==??? Updates: 1? ? sqlSession2 ? = sessionFactory .openSession( true ),将更新的数据写入了数据库,在?Navicat中查到了更新后的数据
DEBUG [main] - Cache Hit Ratio [com.how2jave.mybatis_demo.mapper.HeroMapper]: 0.3333333333333333
DEBUG [main] - ==>? Preparing: select id,name,hp,damage from hero where id = ??
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==????? Total: 1?? ?? ?【sqlSession2的二级缓存查询,】
DEBUG [main] - Cache Hit Ratio [com.how2jave.mybatis_demo.mapper.HeroMapper]: 0.5
DEBUG [main] - Cache Hit Ratio [com.how2jave.mybatis_demo.mapper.HeroMapper]: 0.6
DEBUG [main] - Cache Hit Ratio [com.how2jave.mybatis_demo.mapper.HeroMapper]: 0.6666666666666666
id:3,name:test,hp:22.4,damage:33.0
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@55182842]
DEBUG [main] - Returned connection 1427646530 to pool.
二级缓存中多个sqlSession执行commit或close方法示例:
??? ??? ??? ??? ?String statement = "com.how2jave.mybatis_demo.mapper.HeroMapper.selectHeroById";
??????????????? Hero hero1 = sqlSession1.selectOne( statement,3);
??????????????? System. out.println( hero1);
???????????????
???????????????
??????????????? HeroMapper heroMapper1 = sqlSession1.getMapper(HeroMapper. class);
??????????????? sqlSession1.commit();?? //此处执行commit和close都会将sqlSession1中的查询写入mybatis的二级缓存中
//????????????? sqlSession1.close();
???????????????
??????????????? //b.基于mapper接口的操作
??????????????? SqlSession sqlSession2? = sessionFactory.openSession( true);
??????????????? HeroMapper heroMapper = sqlSession2.getMapper(HeroMapper. class);
???????????????
??????????????? SqlSession sqlSession3? = sessionFactory.openSession( true);
??????????????? HeroMapper heroMapper3 = sqlSession3.getMapper(HeroMapper. class);
??????????????? Hero hero2 = heroMapper.selectHeroById(3);
???????????????
??????????????? Hero hero = new Hero();
??????????????? hero.setId(3);
??????????????? hero.setName( "test");
??????????????? hero.setHp(22.4);
??????????????? hero.setDamage(33);
??????????????? heroMapper.updateHeroById( hero);
//????????????? sqlSession2.commit();
//若上面不执行update操作,只是commit,则不会清空mybatis二级缓存中 " com.how2jave.mybatis_demo.mapper.HeroMapper.selectHeroById",3? 的查询结果,
但如果 先执行上面的update操作,然后再执行commit,会将mybatis二级缓存中对应的数据清空 ,下面的查询都不会命中。
也就是说, commit操作后, 如果mybatis二级缓存中发现有数据更新了,不会把更新的数据写入二级缓存中,而是直接将这部分数据删除
但是如果update操作不是用 sqlSession2.commit ()将数据写入的数据库,而是使用? SqlSession sqlSession2 ? = sessionFactory .openSession( true );?并将 sqlSession2.commit ()注释掉,则数据库写入操作成功,且mybatis二级缓存会命中(命中率增加)。 但只有当前的sqlSession2才能查到最新的数据,其他的sqlSession查到的都是更新前的数据。如下面的 hero3 、hero9?都是数据更新前的数据。
??????????????
??????????????? Hero hero6 = heroMapper.selectHeroById(3);
??????????????? Hero hero7 = heroMapper.selectHeroById(3);
??????????????? Hero hero8 = heroMapper.selectHeroById(3);
??????????????? System. out.println( hero8);
??????????????? Hero hero3 = heroMapper1.selectHeroById(3);
??????????????? System. out.println( hero3);
???????????????
??????????????? Hero hero9 = heroMapper3.selectHeroById(3);
??????????????? System. out.println( hero9);
??????????????? sqlSession2.close();
  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 14:26:09  更:2021-09-22 14:26:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:22:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码