关于缓存,想必大家早已不陌生。第一次使用查询数据时,Mybatis 将其结果缓存起来,当下次执行相同的查询的时候直接返回(没有声明需要刷新缓存,且缓存没有超时)。 默认情况下,Mybatis 只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 缓存(总开关)是默认开启的,如果需要关闭缓存只需在 MyBatis 的配置文件中添加一个属性设置, 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行: 还可自定义缓存参数 创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。 可用的清除策略有(默认的清除策略是 LRU): flushInterval(刷新间隔):属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。 size(引用数目):属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。 readOnly(只读):属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。 另一种开启方式,在 Mapper 接口上添加 @CacheNamespace 注解。 注解配置和 xml 配置只能选择一个,否者报错如下: 另一种开启方式,还可和其他命名空间共享相同的缓存配置和实例,在 Mapper 接口上添加 @CacheNamespaceRef注解。 默认一级缓存,具体是什么效果呢?接下来看看下面的例子。 单元测试成功,(subject1 == subject2,且 subject1 == subject3)说明查询返回是的是相同的对象。 单元测试成功,(subject1 != subject2)说明查询返回是的是不同的对象。 接下来,我们测试一下开启二级缓存,配置如下: 执行会出现什么结果呢 SQL 只执行了一次,且多了一行输出 Cache Hit Ratio [org.apache.ibatis.autoconstructor.AutoConstructorMapper]: 0.5 ,说明二次缓存命中。 这里需要注意的是 Cache 的配置 ***readOnly=“true”***,如果修改缓存,是会影响其他调用者的。 直接使用默认配置会如何呢? 若使用默认配置 ***<cache/>***,可能会报如下错误: 此时需要给 PrimitiveSubject 类实现 Serializable 接口,因为默认 ***readOnly=“false”***,是通过 SerializedCache 类来实现的,序列化和反序列化需要实现 Serializable 接口,序列化可保证业务更改获取到的值不影响实际的缓存。具体实现代码在: 单元测试成功,且 subject1 != subject2(不相等,是因为反序列化后返回的结果),但是两者的内容是一样的,SQL 只执行一次,缓存命中。 开启一个不自动提交的事务1,事务1先查询数据总数为3条,删除一条记录之后执行查询总数变为2条,回滚之后再次查询总数为3条。 执行无报错,缓存事务测试通过。 Mybatis 二级缓存默认使用 PerpetualCache 进行本地存储,不能满足分布式系统的要求。 type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口。 若需对缓存进行配置,只需要简单地在你的缓存实现中添加公有的 JavaBean 属性,然后通过 cache 元素传递属性值。 Mybatis 默认只启用了本地的会话缓存,如果要开启二级缓存则另外需要增加配置,也可使用自定义的二级缓存实现。 源码分析请看下篇 Mybatis 缓存实现原理
Mybatis 缓存介绍
一级缓存(本地的会话缓存):只与 SqlSession 有关,不同的 SqlSession 缓存不同。
二级缓存:SqlSession 可以共享缓存。
全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 <settings> <setting name="cacheEnabled" value="false"/> </settings>
开启二级缓存的方式
<cache/>
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
@CacheNamespace public interface AutoConstructorMapper { }
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Caches collection already contains value for org.apache.ibatis
@CacheNamespaceRef(name = "org.apache.ibatis.submitted.cache.PersonMapper") // by name // 或者 @CacheNamespaceRef(PersonMapper.class) // by type public interface AutoConstructorMapper { }
一级缓存的效果(同一个 SqlSession )
@Test void localCacheTest() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); PrimitiveSubject subject1 = mapper.getSubject(1); PrimitiveSubject subject2 = mapper.getSubject(1); PrimitiveSubject subject3 = mapper.getSubject(1); assertTrue(subject1 == subject2); assertTrue(subject1 == subject3); } }
日志也只打印了一个查询语句,也说明 SQL 只执行了一次。2020-05-10 18:07:35 DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 2020-05-10 18:07:38 DEBUG [main] - Opening JDBC Connection 2020-05-10 18:07:38 DEBUG [main] - Setting autocommit to false on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@1f760b47] 2020-05-10 18:07:38 DEBUG [main] - ==> Preparing: SELECT * FROM subject WHERE id = ? 2020-05-10 18:07:38 DEBUG [main] - ==> Parameters: 1(Integer) 2020-05-10 18:07:38 DEBUG [main] - <== Total: 1 2020-05-10 18:07:38 DEBUG [main] - Resetting autocommit to true on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@1f760b47] 2020-05-10 18:07:38 DEBUG [main] - Closing JDBC Connection [org.hsqldb.jdbc.JDBCConnection@1f760b47]
一级缓存的效果(不同的 SqlSession )
@Test void localCacheDifferentSqlSession() { PrimitiveSubject subject1; PrimitiveSubject subject2; try (SqlSession sqlSession = sqlSessionFactory.openSession()) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); subject1 = mapper.getSubject(1); } try (SqlSession sqlSession = sqlSessionFactory.openSession()) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); subject2 = mapper.getSubject(1); } assertTrue(subject1 != subject2); }
日志打印了两次查询语句,说明 SQL 执行了两次,一级缓存对不同的 SqlSession 无效。2020-05-10 18:07:54 DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 2020-05-10 18:07:56 DEBUG [main] - Opening JDBC Connection 2020-05-10 18:07:56 DEBUG [main] - Setting autocommit to false on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@1f760b47] 2020-05-10 18:07:56 DEBUG [main] - ==> Preparing: SELECT * FROM subject WHERE id = ? 2020-05-10 18:07:56 DEBUG [main] - ==> Parameters: 1(Integer) 2020-05-10 18:07:56 DEBUG [main] - <== Total: 1 2020-05-10 18:07:56 DEBUG [main] - Resetting autocommit to true on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@1f760b47] 2020-05-10 18:07:56 DEBUG [main] - Closing JDBC Connection [org.hsqldb.jdbc.JDBCConnection@1f760b47] 2020-05-10 18:07:56 DEBUG [main] - Opening JDBC Connection 2020-05-10 18:07:56 DEBUG [main] - Setting autocommit to false on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@51bd8b5c] 2020-05-10 18:07:56 DEBUG [main] - ==> Preparing: SELECT * FROM subject WHERE id = ? 2020-05-10 18:07:56 DEBUG [main] - ==> Parameters: 1(Integer) 2020-05-10 18:07:56 DEBUG [main] - <== Total: 1 2020-05-10 18:07:56 DEBUG [main] - Resetting autocommit to true on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@51bd8b5c] 2020-05-10 18:07:56 DEBUG [main] - Closing JDBC Connection [org.hsqldb.jdbc.JDBCConnection@51bd8b5c]
二级缓存的效果(自定义缓存 )
<mapper namespace="org.apache.ibatis.autoconstructor.AutoConstructorMapper"> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <select id="getSubject" resultType="org.apache.ibatis.autoconstructor.PrimitiveSubject"> SELECT * FROM subject WHERE id = #{id} </select> </mapper>
@Test void twoLevelCacheDifferentSqlSession() { PrimitiveSubject subject1; PrimitiveSubject subject2; try (SqlSession sqlSession = sqlSessionFactory.openSession()) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); subject1 = mapper.getSubject(1); } try (SqlSession sqlSession = sqlSessionFactory.openSession()) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); subject2 = mapper.getSubject(1); } log.debug(subject1.toString()); log.debug(subject2.toString()); subject1.setAge(27); log.debug(subject1.toString()); log.debug(subject2.toString()); assertTrue(subject1 == subject2); }
2020-05-10 20:48:36 DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 2020-05-10 20:48:38 DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.autoconstructor.AutoConstructorMapper]: 0.0 2020-05-10 20:48:38 DEBUG [main] - Opening JDBC Connection 2020-05-10 20:48:38 DEBUG [main] - Setting autocommit to false on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@4ea5b703] 2020-05-10 20:48:38 DEBUG [main] - ==> Preparing: SELECT * FROM subject WHERE id = ? 2020-05-10 20:48:38 DEBUG [main] - ==> Parameters: 1(Integer) 2020-05-10 20:48:38 DEBUG [main] - <== Total: 1 2020-05-10 20:48:38 DEBUG [main] - Resetting autocommit to true on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@4ea5b703] 2020-05-10 20:48:38 DEBUG [main] - Closing JDBC Connection [org.hsqldb.jdbc.JDBCConnection@4ea5b703] 2020-05-10 20:48:38 DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.autoconstructor.AutoConstructorMapper]: 0.5 2020-05-10 20:48:38 DEBUG [main] - PrimitiveSubject{id=1, name='a', age=10, height=100, weight=45, active=true, dt=Sun May 10 20:48:38 CST 2020} 2020-05-10 20:48:38 DEBUG [main] - PrimitiveSubject{id=1, name='a', age=10, height=100, weight=45, active=true, dt=Sun May 10 20:48:38 CST 2020} 2020-05-10 20:48:38 DEBUG [main] - PrimitiveSubject{id=1, name='a', age=27, height=100, weight=45, active=true, dt=Sun May 10 20:48:38 CST 2020} 2020-05-10 20:48:38 DEBUG [main] - PrimitiveSubject{id=1, name='a', age=27, height=100, weight=45, active=true, dt=Sun May 10 20:48:38 CST 2020}
可以看 subject1.setAge(27) 前后的输出结果。获取缓存报 NotSerializableException 错?
org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: org.apache.ibatis.autoconstructor.PrimitiveSubject
##org.apache.ibatis.mapping.CacheBuilder#setStandardDecorators if (readWrite) { cache = new SerializedCache(cache); } public class SerializedCache implements Cache { public void putObject(Object key, Object object) { if (object == null || object instanceof Serializable) { delegate.putObject(key, serialize((Serializable) object)); } else { throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object); } } public Object getObject(Object key) { Object object = delegate.getObject(key); return object == null ? null : deserialize((byte[]) object); } }
二级缓存的效果(默认缓存配置 )
<mapper namespace="org.apache.ibatis.autoconstructor.AutoConstructorMapper"> <!-- <cache--> <!-- eviction="FIFO"--> <!-- flushInterval="60000"--> <!-- size="512"--> <!-- readOnly="true"/>--> <cache/> <select id="getSubject" resultType="org.apache.ibatis.autoconstructor.PrimitiveSubject"> SELECT * FROM subject WHERE id = #{id} </select>
@Test void twoLevelCacheDifferentSqlSessionWithDefaultCache() { ... log.debug(subject1.toString()); log.debug(subject2.toString()); assertTrue(subject1 != subject2); }
2020-05-10 20:49:38 DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. 2020-05-10 20:49:40 DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.autoconstructor.AutoConstructorMapper]: 0.0 2020-05-10 20:49:40 DEBUG [main] - Opening JDBC Connection 2020-05-10 20:49:40 DEBUG [main] - Setting autocommit to false on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@4ea5b703] 2020-05-10 20:49:40 DEBUG [main] - ==> Preparing: SELECT * FROM subject WHERE id = ? 2020-05-10 20:49:40 DEBUG [main] - ==> Parameters: 1(Integer) 2020-05-10 20:49:40 DEBUG [main] - <== Total: 1 2020-05-10 20:49:40 DEBUG [main] - Resetting autocommit to true on JDBC Connection [org.hsqldb.jdbc.JDBCConnection@4ea5b703] 2020-05-10 20:49:40 DEBUG [main] - Closing JDBC Connection [org.hsqldb.jdbc.JDBCConnection@4ea5b703] 2020-05-10 20:49:40 DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.autoconstructor.AutoConstructorMapper]: 0.5 2020-05-10 20:49:40 DEBUG [main] - PrimitiveSubject{id=1, name='a', age=10, height=100, weight=45, active=true, dt=Sun May 10 20:49:40 CST 2020} 2020-05-10 20:49:40 DEBUG [main] - PrimitiveSubject{id=1, name='a', age=10, height=100, weight=45, active=true, dt=Sun May 10 20:49:40 CST 2020}
二级缓存支持事务
开启一个自动提交的事务2,事务2执行查询,总数还是为3条,删除一条记录。
开启一个自动提交的事务3,事务3执行查询,总数变为2条。 @Test void transactionCache() { try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); assertEquals(3, mapper.getSubjects().size()); mapper.deleteSubject(1); assertEquals(2, mapper.getSubjects().size()); sqlSession.rollback(); assertEquals(3, mapper.getSubjects().size()); } try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); assertEquals(3, mapper.getSubjects().size()); mapper.deleteSubject(1); } try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) { AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); assertEquals(2, mapper.getSubjects().size()); } }
使用第三方缓存作为二级缓存
可以通过实现你自己的缓存,来完全覆盖缓存行为。<cache type="com.domain.something.MyCustomCache"> <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/> </cache>
总结
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算