2024年9月24日---关于MyBatis框架(3)
五 Mybatis的缓存
5.1 Mybatis缓存简介
缓存(cache):提前把数据存放到缓存当中,下一次再使用的时候,直接从缓存中拿,而不用再次去数据库中查询一次了。这样的优势在于:通过减少IO的⽅式,来提⾼程序的执⾏效率。 比如浏览器就有缓存功能…..
MyBatis的缓存:将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条相同select语句的话,直接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO,另⼀⽅⾯不再执⾏繁琐的查找算法;效率⼤⼤提升。
mybatis缓存包括:
-
⼀级缓存:将查询到的数据存储到SqlSession中。
-
⼆级缓存:将查询到的数据存储到SqlSessionFactory中。
-
集成其它第三⽅的缓存:⽐如EhCache【Java语⾔开发的】、Memcache【C语⾔开发的】 等。
⚠️ 缓存只针对于DQL语句,也就是说缓存机制只对应select语句。
5.2 一级缓存
一级缓存的范围是SqlSession
-
⼀级缓存默认是开启的,不需要做任何配置。
-
原理:只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存。
什么时候不走缓存
-
第⼀种:不同的SqlSession对象。
-
第⼆种:查询条件变化了。
什么时候缓存失效
-
①第⼀次查询和第⼆次查询之间,执行了clearCache()方法,⼿动清空了⼀级缓存。
-
②第⼀次查询和第⼆次查询之间,执⾏了增、删、改操作。
5.3 二级缓存
⼆级缓存的范围是SqlSessionFactory对象。使⽤⼆级缓存需要具备以下⼏个条件
-
①全局性地开启或关闭所有映射器配置⽂件中已配置的任何缓存。默认就是true,⽆需设置(默认二级缓存就是开启的)!
<setting name="cacheEnabled" value="true">
-
②在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加一个标签:
<catche />
-
③使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝
-
④SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中;此时⼆级缓存才可⽤。
⼆级缓存的失效:
-
只要两次查询之间出现了增、删、改操作,⼆级缓存就会失效。【当然⼀级缓存也会失效】!
二级缓存相关配置
① eviction:指定从缓存中移除某个对象的淘汰算法。(默认采⽤LRU策略)
LRU:Least Recently Used。最近最少使⽤,优先淘汰在间隔时间内使⽤频率最低的对象。(其实还有⼀种淘汰算法LFU,最不常⽤) FIFO:First In First Out。⼀种先进先出的数据缓存器,先进⼊⼆级缓存的对象最先被淘汰。 SOFT:软引⽤,淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。 WEAK:弱引⽤,淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
② flushInterval:⼆级缓存的刷新时间间隔,单位毫秒(刷新了也会使原来的缓存失效)。如果没有设置,就代表不刷新缓存,只要内存⾜够⼤,⼀ 直会向⼆级缓存中缓存数据,除⾮执⾏了增删改。
③size:设置⼆级缓存中最多可存储的java对象数量,默认值1024。
④readOnly
true: 只读缓存,多条相同的sql语句执⾏之后返回的对象是共享的同⼀个,性能好。但是多线程并发可能会存在安全问题。 false:读写缓存,多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般,但安全。
5.4 Mybatis缓存查询顺序
-
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
-
如果二级缓存没有命中,再查询一级缓存
-
如果一级缓存也没有命中,则查询数据库
-
SqlSession管必须之后,一级缓存中的数据会写入二级缓存
5.5 MyBatis集成第三方缓存EhCache
mybatis对外提供了接⼝,也可以集成第三⽅的缓存组件;⽐如EhCache、Memcache等。 EhCache是Java写的、Memcache是C语⾔写的,所以mybatis集成EhCache较为常⻅,按照以下步骤操作,就可以完成集成
第⼀步:在pom.xml配置中引⼊MyBatis整合ehcache的依赖
ehcache需要slf4j的⽇志组件,log4j不好使!
<!--mybatis集成ehcache的组件--> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> <scope>test</scope> </dependency>
第⼆步:在类的根路径下(resources)新建ehcache.xml⽂件,并提供以下配置信息。
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!--磁盘存储:将缓存中暂时不使⽤的对象,转移到硬盘,类似于Windows系统的虚拟内存--> <diskStore path="D:/ehcache"/> <defaultCache eternal="false" maxElementsInMemory="1000" ma overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/> </ehcache>
第三步:修改CarMapper.xml⽂件中的标签,添加type属性,引入EhcacheCache
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.bjpowernode.mybatis.mapper.CarMapper"> <!--集成EhcacheCache组件--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> <select id="selectById2" resultType="Car"> select * from t_car where id = #{id} </select> </mapper>
第四步:编写测试程序使⽤,代码还是和上面使用二级缓存的测试代码相同,效果展示也是相同的,都是第一次命中率是0.0,第二次命中率是0.5
六 MyBatis的逆向工程
七 Mybatis日志组件
7.1 Mybatis常用的日志组件
-
SLF4J
-
LOG4J
-
LOG4J2
-
STDOUT_LOGGING:
标准日志,Mybatis框架本身已经实现了这种日志,我们只需要使用<setting>
标签开启日志即可。
7.2 开启日志输出:
<!--设置日志输出组件--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
STDOUT_LOGGING我们可以直接使用,但是如果想使用SLF4J等第三方日志组件,需要引入相应jar包。
-
注意: 这个标签必须在
<properties></properties>
后<typeAliases></typeAliases>
前,这是dtd对xml文件的约束规范。
7.3 第三方日志组件的使用(SLF4J)
第一步:引入相应jar包logback。
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> <scope>test</scope> </dependency>
第二步:引入logback所必须的xml配置文件。这个xml文件必须在类的根路径下,文件名必须是logback.xml。
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--定义⽇志⽂件的存储地址--> <property name="LOG_HOME" value="/home"/> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示⽇期,%thread表示线程名,%-5level:级别从左显示5 个字符宽度%msg:⽇志消息,%n是换⾏符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!-- 按照每天⽣成⽇志⽂件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--⽇志⽂件输出的⽂件名--> <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern> <!--⽇志⽂件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示⽇期,%thread表示线程名,%-5level:级别从左显示5 个字符宽度%msg:⽇志消息,%n是换⾏符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--⽇志⽂件最⼤的⼤⼩--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>100MB</MaxFileSize> </triggeringPolicy> </appender> <!--mybatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- ⽇志输出级别,logback⽇志级别包括五个:TRACE < DEBUG < INFO < WARN < ER ROR --> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> </configuration>
第三步:开启日志输出:
<!--设置日志输出组件--> <settings> <setting name="logImpl" value="SLF4J"/> </settings>