(四) - 缓存
MyBaits 缓存分类:
1. 一级缓存: SqlSession 级别, 默认开启, 并且不能关闭
操作数据库是需要创建 SqlSession 对象, 在对象中有一个 HashMap 用于存储缓存数据, 不同的 SqlSession 之间缓存数据区域互不影响.
一级缓存的做用户是 SqlSession 范围的, 当在同一个 SqlSession 中执行两次相同的 SQL 语句时, 第一次执行完毕会将结果保存到缓存中, 第二次查询时直接从缓存中获取.
需要注意的是, 如果 SqlSession 执行了 DML 操作(insert, update, delete), MyBatis 必须将缓存清空以保证数据的准确性.
举例说明, 如下代码查询两次, 但只会执行一次 SQL: (第二次查询是从缓存中拿的数据)
public class TestCache { public static void main(String[] args) { InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); DataRepository dataRepository = sqlSession.getMapper(DataRepository.class); //获取 student 信息 Student student1 = dataRepository.findStudent(4399); System.out.println(student1.getName()); Student student2 = dataRepository.findStudent(4399); System.out.println(student2.getName()); sqlSession.close(); } }
如果在查完第一次后关闭 SqlSession, 在重新打开, 则一级缓存被销毁, 第二次查询仍需运行 sql:
public class TestCache { public static void main(String[] args) { InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); DataRepository dataRepository = sqlSession.getMapper(DataRepository.class); //获取 student 信息 Student student1 = dataRepository.findStudent(4399); System.out.println(student1.getName()); sqlSession.close(); sqlSession = sqlSessionFactory.openSession(); dataRepository = sqlSession.getMapper(DataRepository.class); Student student2 = dataRepository.findStudent(4399); System.out.println(student2.getName()); } }
2. 二级缓存: Mapper 级别, 默认关闭, 可以开启.
使用二级缓存时, 多个 SqlSession 使用同一个 Mapper 的 SQL 语句操作数据库, 得到的数据会存在二级缓存区, 同样使用 HashMap 进行数据存储, 相比较于一级缓存, 二级缓存的范围更大, 多个 SqlSession 可以共用二级缓存, 二级缓存是跨 SqlSession 的.
二级缓存是多个 SqlSession 共享的, 其作用域是 Mapper 的同一个 namespace, 不同的 SqlSession 两次执行相同的 namespace 下的 SQL 语句, 参数也相等, 则第一次执行成功之后会将数据保存到二级缓存中, 第二次可直接从二级缓存中取出数据.
2.1. MyBatis 自带的二级缓存 (三步)
第一步. config.xml 中配置开启二级缓存:
<settings> <!--打印SQL--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--开启延时加载/懒加载--> <setting name="lazyLoadingEnabled" value="true"/> <!--开启二级缓存--> <setting name="cacheEnabled" value="true"/> </settings> <!--配置MyBatis运行环境--> <environments default="development"> ...
第二步. mapper.xml 中配置二级缓存
<mapper namespace="com.ryan.repository.DataRepository"> <!--配置二级缓存--> <cache></cache> <resultMap id="findStudent" type="com.ryan.javaClass.Student"> <id column="id" property="id"/> ...
第三步. 实体类实现序列化接口
public class Student implements Serializable { private long id; private String name; private long phoneNumber; private Country country; ...
设置好后, 再次执行上面的测试程序: (发行 sql 只执行了一次, 因为这次使用了二级缓存, 第二次查询直接从二级缓存中拿的数据)
public class TestCache { public static void main(String[] args) { InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); DataRepository dataRepository = sqlSession.getMapper(DataRepository.class); //获取 student 信息 Student student1 = dataRepository.findStudent(4399); System.out.println(student1.getName()); sqlSession.close(); sqlSession = sqlSessionFactory.openSession(); dataRepository = sqlSession.getMapper(DataRepository.class); Student student2 = dataRepository.findStudent(4399); System.out.println(student2.getName()); } }
2.2. 使用第三方缓存 -- ehcache 二级缓存
第一步. pom 添加相关依赖:
<!--ehcache二级缓存相关依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.4.3</version> </dependency> </dependencies>
第二步. 在 resources 目录下添加 ehcache.xml 文件:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <diskStore/> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
第三步. config.xml 中配置开启二级缓存(同上)
<setting name="cacheEnabled" value="true"/>
第四步. mapper.xml 中配置二级缓存
<mapper namespace="com.ryan.repository.DataRepository"> <!--配置二级缓存--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"> <!--缓存创建之后, 最后一次访问缓存的时间至缓存失效的时间间隔(秒)--> <property name="timeToIdleSeconds" value="3600"/> <!--缓存自创建时间起至失效的时间间隔(秒)--> <property name="timeToLiveSeconds" value="3600"/> <!--缓存回收策略, LRU表示一处近期使用最少的对象--> <property name="memoryStoreEvictionPolicy" value="LRU"/> </cache> <resultMap id="findStudent" type="com.ryan.javaClass.Student"> ...
***实体类不需要时限序列化接口
测试: (执行了一次sql)
public class TestCache { public static void main(String[] args) { InputStream inputStream = DataRepository.class.getClassLoader().getResourceAsStream("mybatis-config.xml"); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); DataRepository dataRepository = sqlSession.getMapper(DataRepository.class); //获取 student 信息 Student student1 = dataRepository.findStudent(4399); System.out.println(student1.getName()); sqlSession.close(); sqlSession = sqlSessionFactory.openSession(); dataRepository = sqlSession.getMapper(DataRepository.class); Student student2 = dataRepository.findStudent(4399); System.out.println(student2.getName()); } }