现在MyBatis
因其灵活性使用率很高,而且MyBatis
也提供缓存功能,所以也可了解一下。MyBatis
提供一级缓存和二级缓存,一级缓存(同一个sqlSession
共享数据)是默认开启的而且不能关闭,二级缓存(不同的sqlSession
之间共享数据)是需要配置开启的。
MyBatis之一级缓存
一级缓存是默认开启的,只要是同一个sqlSession
,第二次查询就会从缓存中读取数据。不过如果跟Spring
集成后,一级缓存就会失效。原因就是MyBatis
的一级缓存是需要同一个sqlSession
,但是Spring
会在sqlSession
使用完毕后就会关闭,这样缓存数据将会清空。当第二个请求,重新新建sqlSession
,然后又去请求数据库。目前我们一般是跟Spring
整合使用,但是因为这个原因只能寻求其二级缓存。
MyBatis之二级缓存
配置
一级缓存无法实现,但是MyBatis
提供二级缓存,而且配置比较简单,首先需要返回的POJO类
必须可序列化,即要求实现Serializable接口
,然后在XML文件中配置<cache />
配置缓存可以使用不同的参数:
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true" />
eviction
代表缓存回收策略,目前MyBatis提供以下回收策略。(1)LRU,最近最少使用的,移除最长时间不用的对象。(2)FIFO,先进先出,按对象进入缓存的顺序来移除他们。(3)SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象。(4)WEAK,弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。flushInterval
刷新间隔时间,单位为毫秒,如果不配置,那么当SQL被执行的时候才会去刷新缓存。size
引用数目,代表缓存最多可以存储多少对象,不宜设置过大,不然会导致内存溢出。readOnly
只读,意味着缓存数据只能读取而不能修改。
全局/局部配置
全局配置:默认就是true,一般不用配置,配置就是取消二级缓存,意义不大。
<settings>
<!-- 二级缓存总开关 -->
<setting name="cacheEnabled" value="false"/>
</settings>
局部配置:使用useCache
和flushCache
,前者表示是否需要使用缓存,而后者表示插入后是否需要刷新缓存。
<select ... flushCache="false" useCache="true" />
<insert ... flushCache="true" />
<update ... flushCache="true" />
<delete ... flushCache="true" />
使用原则
(1) 多个namespace不要操作同一张表
二级缓存中的数据是基于namespace的,不同的namespace的数据互不干扰,如果某namespace中的进行增删改而造成缓存刷新,只会引起该namespace中的二级缓存数据刷新,不会对其他namespace下的数据有影响,所以从其他namespace中读取的数据还是未更新的。
(2)不要在关联关系表上执行增删改操作
一个namespace一般是同一个表进行操作,若表间存在关联关系,也就意味着同一个表可能出现在多个namespace下,这样就会存在上述讲到的风险。
(3)查询多于修改时使用二级缓存
二级缓存频繁刷新容易降低系统性能。
案例
Department.java
public class Department implements Serializable
DepartmentMapper.xml
<cache/>
测试
@Test
public void testDepartment(){
try {
System.out.println("=================================");
MvcResult result1=mockMvc.perform(MockMvcRequestBuilders
.get("/depts"))
.andReturn();
MvcResult result2=mockMvc.perform(MockMvcRequestBuilders
.get("/depts"))
.andReturn();
System.out.println(result1.getResponse().getContentAsString());
System.out.println(result2.getResponse().getContentAsString());
System.out.println("=================================");
} catch (Exception e) {
e.printStackTrace();
}
}
结果
.
.
.
2023-09-01 11:46:44,987 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession
2023-09-01 11:46:45,012 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2063c53e]
2023-09-01 11:46:45,075 [main] DEBUG [com.plf.hello.dao.DepartmentMapper] - Cache Hit Ratio [com.plf.hello.dao.DepartmentMapper]: 0.0
2023-09-01 11:46:45,085 [main] DEBUG [org.mybatis.spring.transaction.SpringManagedTransaction] - JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5ce4369b] will be managed by Spring
2023-09-01 11:46:45,095 [main] DEBUG [com.plf.hello.dao.DepartmentMapper.selectByExample] - ==> Preparing: select dept_id, dept_name from tbl_dept
2023-09-01 11:46:45,149 [main] DEBUG [com.plf.hello.dao.DepartmentMapper.selectByExample] - ==> Parameters:
2023-09-01 11:46:45,216 [main] DEBUG [com.plf.hello.dao.DepartmentMapper.selectByExample] - <== Total: 2
2023-09-01 11:46:45,217 [main] DEBUG [com.alibaba.druid.pool.PreparedStatementPool] - {conn-10001, pstmt-20000} enter cache
2023-09-01 11:46:45,218 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2063c53e]
2023-09-01 11:46:45,218 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2063c53e]
2023-09-01 11:46:45,301 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2063c53e]
2023-09-01 11:46:45,301 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2063c53e]
2023-09-01 11:46:45,301 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction commit
2023-09-01 11:46:45,301 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5ce4369b]
2023-09-01 11:46:45,306 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5ce4369b] after transaction
.
.
.
2023-09-01 11:46:45,493 [main] DEBUG [com.plf.hello.dao.DepartmentMapper] - Cache Hit Ratio [com.plf.hello.dao.DepartmentMapper]: 0.5
2023-09-01 11:46:45,493 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4248e66b]
2023-09-01 11:46:45,493 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4248e66b]
2023-09-01 11:46:45,493 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4248e66b]
2023-09-01 11:46:45,493 [main] DEBUG [org.mybatis.spring.SqlSessionUtils] - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4248e66b]
2023-09-01 11:46:45,493 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction commit
2023-09-01 11:46:45,493 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Committing JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5ce4369b]
2023-09-01 11:46:45,497 [main] DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5ce4369b] after transaction
.
.
.
{"code":100,"msg":"处理成功","extend":{"depts":[{"deptId":1,"deptName":"技术部"},{"deptId":2,"deptName":"餐饮部"}]}}
{"code":100,"msg":"处理成功","extend":{"depts":[{"deptId":1,"deptName":"技术部"},{"deptId":2,"deptName":"餐饮部"}]}}
上述结果可知使用二级缓存,只请求了一次SQL语句,其中Cache Hit Ratio [com.plf.hello.dao.DepartmentMapper]: 0.5
显示缓存命中率0.5。
Ehcache缓存
MyBatis毕竟是用于操作SQL的,在缓存方面肯定不如其他的缓存更实用,目前比较多的就是Ehcache、Redis、MongoDB等用作缓存,这里主要实现Spring+Ehcache的缓存
pom.xml
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 缓存数据要存放的磁盘地址 -->
<diskStore path="E:\temp\ehcache" />
<!--
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) -->
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
<cache name="myCache"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
applicationContext.xml
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.3.xsd"
<cache:annotation-driven cache-manager="cacheManager"/>
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
<property name="transactionAware" value="true"/>
</bean>
DepartmentService.java
@Cacheable(value = "myCache")
public List<Department> getDepts() {
return departmentMapper.selectByExample(null);
}