mybatis的一级缓存是默认开启的 —————一级缓存是针对同一个session
案例:
public static SqlSession getSession() throws Exception {
if (null == myfactory) {
synchronized (obj) {
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
Reader reader = Resources.getResourceAsReader("sqlmap-config.xml");
myfactory = factoryBuilder.build(reader);
}
}
SqlSession session = myfactory.openSession();
return session;
}
测试:
@Test
public void test1() {
try {
SqlSession session = getSession();
Dept selectOne = (Dept) session.selectOne("cn.xdl.dao.DeptMapper.findById", 1);
System.out.println(selectOne);
System.out.println("==========================");
Dept selectOne1 = (Dept) session.selectOne("cn.xdl.dao.DeptMapper.findById", 1);
System.out.println(selectOne1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
mybaits的二级缓存默认是没有开启的 需要xml配置 才能开启
案 例:
配置sqlmap-Context.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<typeAlias type="cn.xdl.entity.Dept" alias="dept"/>
</typeAliases>
<environments default="environment">
<environment id="environment">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver"
value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/ultrapower"/>
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/xdl/mapper/DeptMapper.xml" />
</mappers>
</configuration>
配置mapper配置信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<!-- namespace指定和哪个Mapper映射器接口对应 -->
<mapper namespace="cn.xdl.dao.DeptMapper">
<!-- <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> -->
<cache></cache>
<!-- 定义SQL语句 -->
<select id="findById" parameterType="int"
resultType="cn.xdl.entity.Dept">
select * from dept where id = #{no}
</select>
测试:
@Test
public void test3() {
try {
SqlSession session1 =getSession();
SqlSession session2 = getSession();
SqlSession session3 = getSession();
// SqlSession session1 = myfactory.openSession();
// SqlSession session2 = myfactory.openSession();
// SqlSession session3 = myfactory.openSession();
System.out.println("==========================");
long st = System.currentTimeMillis();
DeptMapper mapper1 = session1.getMapper(DeptMapper.class);
Dept selectOne = mapper1.findById(1);
System.out.println("excute time :" + (System.currentTimeMillis() - st));
System.out.println(selectOne);
session1.close(); // 这里的session一定要close/commit 二级缓存才会生效
System.out.println("==========================");
long st1 = System.currentTimeMillis();
DeptMapper mapper2 = session2.getMapper(DeptMapper.class);
Dept selectOne2 = mapper2.findById(1);
System.out.println("excute time :" + (System.currentTimeMillis() - st1));
System.out.println(selectOne2);
session2.close();// 这里的session一定要close/commit 二级缓存才会生效
System.out.println("==========================");
long st2 = System.currentTimeMillis();
DeptMapper mapper3 = session3.getMapper(DeptMapper.class);
Dept selectOne3 = mapper3.findById(1);
System.out.println("excute time :" + (System.currentTimeMillis() - st2));
System.out.println(selectOne3);
session3.close();// 这里的session一定要close/commit 二级缓存才会生效
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
测试结果:
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
==========================
Cache Hit Ratio [cn.xdl.dao.DeptMapper]: 0.0
Opening JDBC Connection
Created connection 1279692482.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@4c468ec2]
ooo Using Connection [com.mysql.jdbc.Connection@4c468ec2]
==> Preparing: select * from dept where id = ?
==> Parameters: 1(Integer)
<== Columns: id, name, loc
<== Row: 1, java, 北京
<== Total: 1
excute time :222
Dept [id=1, dname=null, loc=北京]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@4c468ec2]
Closing JDBC Connection [com.mysql.jdbc.Connection@4c468ec2]
Returned connection 1279692482 to pool.
==========================
Cache Hit Ratio [cn.xdl.dao.DeptMapper]: 0.5 ===========》》》》这里能够看出二级缓存生效了
excute time :2
Dept [id=1, dname=null, loc=北京]
==========================
Cache Hit Ratio [cn.xdl.dao.DeptMapper]: 0.6666666666666666 ===========》》》》这里能够看出二级缓存生效了
excute time :0
Dept [id=1, dname=null, loc=北京]
mybatis二级缓存的其他特性:
正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持
- 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
useCache配置禁用二级缓存
在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<selectid='findOrderListResultMap' resultMap='ordersUserMap' useCache='false'>
总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。
mybatis刷新缓存(就是清空缓存)
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache='true' 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
如下:
<insertid='insertUser' parameterType='cn.itcast.mybatis.po.User' flushCache='true'>
总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存默认情况下为true,我们不用去设置它,这样可以避免数据库脏读。
Mybatis Cache参数
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
如下例子:
<cache eviction='FIFO' flushInterval='60000' size='512' readOnly='true'/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是 LRU:
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>当前mapper下所有语句开启二级缓存
这里配置了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而却返回的对象是只读的
1. LRU – 最近最少使用的:移除最长时间不被使用的对象。
2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
4. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
二级应用场景
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
二级缓存局限性
mybatis二级缓存对细粒度的数据级别的缓存实现不好,对同时缓存较多条数据的缓存,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。需要使用三级缓存
参考:
https://www.cnblogs.com/zedosu/p/6709943.html