zoukankan      html  css  js  c++  java
  • MyBatis

    MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。

    • MyBatis系统中默认定义了两级缓存。
    • 一级缓存和二级缓存。
      • 1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
      • 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
      • 3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

    1.一级缓存

    • 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
    • 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
    • 在mybatis3.1之后, 可以配置本地缓存的作用域.
      在 mybatis.xml 中配置
    设置参数 描述 有效值 默认值
    localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
    若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
    SESSION STATEMENT

    1.1 一级缓存演示&失效情况

    • 同一次会话期间只要查询过的数据都会保存在当
      前SqlSession的一个Map中

      • key:hashCode+查询的SqlId+编写的sql查询语句+参数
    • 一级缓存失效的四种情况

      • 1、不同的SqlSession对应不同的一级缓存
      • 2、同一个SqlSession但是查询条件不同
      • 3、同一个SqlSession两次查询期间执行了任何一次增删改操作
      • 4、同一个SqlSession两次查询期间手动清空了缓存

    2.二级缓存

    • 二级缓存(second level cache),全局作用域缓存
    • 二级缓存默认不开启,需要手动配置
    • MyBatis提供二级缓存的接口以及实现,缓存实现要求 POJO实现Serializable接口
    • 二级缓存在 SqlSession 关闭或提交之后才会生效

    (1)使用步骤

    • 1、全局配置文件中开启二级缓存
      • <setting name="cacheEnabled" value="true"/>
    • 2、需要使用二级缓存的映射文件处使用cache配置缓存
      • <cache />
    • 3、注意:POJO需要实现Serializable接口

    Mapper里面的

    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    
    • eviction 可用的回收策略有:

      • LRU – 最近最少使用的:移除最长时间不被使用的对象。
      • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
      • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
      • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    • flushInterval:刷新间隔,单位毫秒 •

      • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
    • size:引用数目,正整数

      • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
    • readOnly:只读,true/false

      • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象 不能被修改。这提供了很重要的性能优势。
      • false(默认):读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些, 但是安全,因此默认是 false。

    (2)使用自定义缓存
    除了这些自定义缓存的方式, 你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆盖缓存行为。

    <cache type="com.domain.something.MyCustomCache"/>
    

    2.1 二级缓存案例:

    参照上面的步骤:
    (1)全局配置mybatis-config.xml

    <settings>
     <!--开启二级缓存-->
      <setting name="cacheEnabled" value="true"></setting>
      ...
    

    (2)在 employeeMapper.xml 里添加 <cache>标签

    <mapper namespace="com.tangge.Mapper.employeeMapper">
      <cache></cache>
    

    (3)POJO需要实现 Serializable 接口

    com.tangge.model.employee 实现接口 Serializable

    @Alias("emp")
    public class employee implements Serializable{
    ...
    }
    

    ---->【测试】:

    /**
         * 缓存:
         * - 一级缓存:本地缓存 sqlSession 级别
         * 与数据库同一次会话期间查询到的数据会缓存到本地缓存中,以后需要获取相同数据,直接缓存中拿。
         *
         * - 二级缓存:全局缓存 nameSpace 级别
         *      工作机制:
         *          1.一条查询被放入一级缓存中。
         *          2.如果会话关闭,一级缓存数据会被保存到二级缓存中。新查询,可以参照二级缓存。
         *          3.sqlSession === EmployeeMapper  ===> Employee
         *                           DepartmentMapper===> Department
         *            不同namespace查出的数据会被放在自己对应的缓存中(map)
         *            效果:数据会从二级缓存中获取。
         *            查出的数据,默认放在一级缓存中,只有会话提交关闭以后,一级缓存中数据才会放入二级缓存。
         */
        public static void getTwoLevelCache() {
            SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();
            SqlSession session = sqlSessionFactory.openSession();
            SqlSession session2 = sqlSessionFactory.openSession();
            try {
                employeeMapper mapper = session.getMapper(employeeMapper.class);
                employeeMapper mapper2 = session2.getMapper(employeeMapper.class);
                //第1次查询
                employee emp = mapper.getEmployeeById(1);
                System.out.println(emp);
                session.close();
                //第2次查询
                employee emp2 = mapper2.getEmployeeById(1);
                System.out.println(emp2);
                session2.close();
            } finally {
    
            }
        }
    

    注意个问题:

    查出的数据,默认放在一级缓存中,只有会话提交关闭以后,一级缓存中数据才会放入二级缓存。

    3.缓存有关设置

    • 1、全局setting的cacheEnable:
      • 配置二级缓存的开关。一级缓存一直是打开的。
    • 2、select标签的useCache属性:
      • 配置这个select是否使用二级缓存。一级缓存一直是使用的
    • 3、sql标签的flushCache属性:
      • 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。
        查询<select>标签:默认flushCache=false。
    • 4、sqlSession.clearCache():
      • 只是用来清除一级缓存。
    • 5、当在某一个作用域 (一级缓存Session/二级缓存
      Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

    4.第三方包

    4.1 ehcache

    http://www.java2s.com/example/jar/e/ehcache-core-index.html
    ehcache-core-2.6.6.jar(降级测试)
    slf4j.api-1.6.1.jar
    slf4j-log4j12-1.7.1.jar

    https://github.com/mybatis/ehcache-cache/releases
    mybatis-ehcache-1.0.3
    帮助文档:http://www.mybatis.org/ehcache-cache/

    4.1.1 配置说明

    diskStore : ehcache支持内存和磁盘两种存储

    path :指定磁盘存储的位置
    defaultCache : 默认的缓存

    maxEntriesLocalHeap=”10000”
    eternal=”false”
    timeToIdleSeconds=”120”
    timeToLiveSeconds=”120”
    maxEntriesLocalDisk=”10000000”
    diskExpiryThreadIntervalSeconds=”120”
    memoryStoreEvictionPolicy=”LRU”
    cache :自定的缓存,当自定的配置不满足实际情况时可以通过自定义(可以包含多个cache节点)

    name : 缓存的名称,可以通过指定名称获取指定的某个Cache对象

    maxElementsInMemory :内存中允许存储的最大的元素个数,0代表无限个

    clearOnFlush:内存数量最大时是否清除。

    eternal :设置缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。根据存储数据的不同,例如一些静态不变的数据如省市区等可以设置为永不过时

    timeToIdleSeconds : 设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。

    timeToLiveSeconds :缓存数据的生存时间(TTL),也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。

    overflowToDisk :内存不足时,是否启用磁盘缓存。

    maxEntriesLocalDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。

    maxElementsOnDisk:硬盘最大缓存个数。

    diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。

    diskPersistent:是否在VM重启时存储硬盘的缓存数据。默认值是false。

    diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。

    4.1.2 配置步骤

    1.配置文档:

    <ehcache
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
      updateCheck="false">
    
      <diskStore path="E:开发工具jar安装包缓存ehcache-disksotre"/>
      <defaultCache
        maxElementsInMemory="20000"
        maxElementsOnDisk="10000000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU"
      />
      <cache name="eternalCache"
        maxElementsInMemory="20000"
        eternal="true"
        overflowToDisk="true"
        diskPersistent="false"
        timeToLiveSeconds="0"
        diskExpiryThreadIntervalSeconds="120"
      />
    </ehcache>
    

    2.在Mapper的<cache>设置

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

    4.1.3 修改一处Mapper,改其他所有

    其他Mapper配置,引用同一个Mapper命名空间里的cache设置

    <cache-ref namespace="">
    

    好多坑

    (1)ehcache-core-2.7.1.jar
    问题:
    1.Caused by: java.lang.ClassNotFoundException: org.terracotta.statistics.StatisticsManager
    解决方案:
    The previous 2.7.0 version had these classes present.
    2.70 版本以上都有这个问题了
    Our maven setup must is :

    <dependency>
    	<groupId>net.sf.ehcache</groupId>
    	<artifactId>ehcache</artifactId>
    	<version>2.7.0</version>
    	<exclusions>
    		<exclusion>
    			<groupId>net.sf.ehcache.internal</groupId>
    			<artifactId>ehcache-terracotta-bootstrap</artifactId>
    		</exclusion>
    		<exclusion>
    			<groupId>org.terracotta.internal</groupId>
    			<artifactId>statistics</artifactId>
    		</exclusion>
    	</exclusions>
    </dependency>
    
  • 相关阅读:
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 697 数组的度(类似于数组的map)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 696 计数二进制子串(暴力)
    Java实现 LeetCode 695 岛屿的最大面积(DFS)
    Java实现 LeetCode 695 岛屿的最大面积(DFS)
    PHP serialize() 函数
    PHP print_r() 函数
  • 原文地址:https://www.cnblogs.com/tangge/p/9549461.html
Copyright © 2011-2022 走看看