• MyBatis-Cache


    一、一级缓存

    /**
     * 一级缓存(本地缓存):SqlSession 级别。一级缓存是默认开启的,为 SqlSession 级别的一个Map
     * 与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中获取。
     */
    public static void main(String[] args) throws IOException {
        SqlSession session = null;
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            session = sqlSessionFactory.openSession();
    
            MyUserMapper mapper = session.getMapper(MyUserMapper.class);
            System.out.println(mapper.selectMyUserById(7));
            System.out.println(mapper.selectMyUserById(7));
        } finally {
            session.close();
        }
    }

    一级缓存失效的几种情况,相同的查询也会向数据库发送SQL

    public static void main(String[] args) throws IOException {
        SqlSession session = null;
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            session = sqlSessionFactory.openSession();
            MyUserMapper mapper = session.getMapper(MyUserMapper.class);
    
            // sqlSession 不同
            System.out.println(mapper.selectMyUserById(7));
            session = sqlSessionFactory.openSession();
            mapper = session.getMapper(MyUserMapper.class);
            System.out.println(mapper.selectMyUserById(7));
    
            // sqlSession 相同,查询条件不同(当前一级缓存中还没有这个数据)
            System.out.println(mapper.selectMyUserById(7));
            System.out.println(mapper.selectMyUserById(8));
    
            // sqlSession 相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
            System.out.println(mapper.selectMyUserById(7));
            mapper.updateMyUser(new MyUser(10,"xasx",24,null));
            System.out.println(mapper.selectMyUserById(7));
    
            // sqlSession 相同,手动清除了一级缓存
            System.out.println(mapper.selectMyUserById(7));
            session.clearCache();
            System.out.println(mapper.selectMyUserById(7));
        } finally {
            session.close();
        }
    }

     

    二、二级缓存

    1.开启二级缓存 mybatis-config.xml

    <settings>
        <!-- 开启全局二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    
        <!-- 自动映射有三种模式,NONE、PARTIAL、FULL。NONE 不启用自动映射,PARTIAL 只对非嵌套的 resultMap 进行自动映射,FULL 表示对所有的 resultMap 都进行自动映射。默认为 PARTIAL -->
        <setting name="autoMappingBehavior" value="PARTIAL"/>
        <!-- 数据库字段下划线转Bean字段的驼峰命名 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 控制台打印SQL -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    
        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认false -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认false (true in ≤3.4.1) -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    2.配置二级缓存 xxxMapper.xml

    <!-- namespace 对应接口文件的全路径 -->
    <mapper namespace="com.dao.MyUserMapper">
        <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
        <!-- eviction:缓存的回收策略:默认的是 LRU
                • LRU – 最近最少使用的:移除最长时间不被使用的对象。
                • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
                • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
                • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
            flushInterval:缓存刷新间隔,缓存多长时间清空一次,默认不清空,设置一个毫秒值
            readOnly:缓存中的数据是否只读
                true:只读,会将数据在缓存中的引用交给用户。数据可能被修改,不安全,速度快
                false:非只读,会利用序列化和反序列的技术克隆一份新的数据返回。安全,速度慢
            size:缓存存放多少元素
            type:指定自定义缓存的全类名,实现 org.apache.ibatis.cache.Cache 接口 -->
        <select id="selectMyUserById" resultType="myUser">
          select * from myuser where id = #{id}
        </select>

    3.实体类实现序列化接口 Serializable

    public class MyUser implements Serializable {
        private Integer id;
        private String name;
        private Integer age;
        private Dept dept;

    4.测试

    /**
     * 二级缓存(全局缓存):基于 namespace 级别的缓存,一个 namespace 对应一个二级缓存(map)
     * 
     * 创建会话:查询一条数据,这个数据就会被放在当前会话的一级缓存中
     * 会话关闭:一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,会先找二级缓存中的内容,没有就找一级缓存,再没有就会发送 SQL 查询数据库
     * 效果:数据会从二级缓存中获取,查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
     * 使用:
     *  1)、在全局配置中开启二级缓存:<setting name="cacheEnabled" value="true"/>
     *     2)、在 xxxMapper.xml 中配置二级缓存:<cache/>
     *     3)、POJO 实现序列化接口 Serializable
     */
    public static void main(String[] args) throws IOException {
        SqlSession session = null;
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            session = sqlSessionFactory.openSession();
            
            MyUserMapper mapper = session.getMapper(MyUserMapper.class);
            System.out.println(mapper.selectMyUserById(7));
    
            // 关闭 session 让一级缓存中类容提交到二级缓存
            session.close();
    
            // 重新获取 sqlSession
            session = sqlSessionFactory.openSession();
            mapper = session.getMapper(MyUserMapper.class);
            System.out.println(mapper.selectMyUserById(7));
        } finally {
            session.close();
        }
    }

     

    三、缓存相关配置

    代码配置

    // 只清除当前 session 的一级缓存
    session.clearCache();

    全局设置

    <settings>
        <!-- 二级缓存设置,不影响一级缓存 -->
        <setting name="cacheEnabled" value="false"/>
        <!-- 本地(一级)缓存作用域,默认 SESSION,会缓存一个会话(SqlSession)中执行的所有查询。 设置为 STATEMENT,会话仅作用在语句执行上,对 SqlSession 的调用将不会共享数据,可认为是禁用一级缓存 -->
        <setting name="localCacheScope" value="SESSION"/>
    </settings>

    SQL 映射文件配置

    <!-- flushCache:将其设置为 true,任何时候只要 SQL 被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句),false(对应查询语句)。
         useCache:将其设置为 true,将会导致本条 SQL 的结果被二级缓存(不影响一级),默认值:对 select 元素为 true -->
    <select id="selectMyUserById" resultType="myUser" useCache="true" flushCache="false">
      select * from myuser where id = #{id}
    </select>

    也可以在接口上配置

    @Options(flushCache = Options.FlushCachePolicy.FALSE, useCache = true)
    public MyUser selectMyUserById(Integer id);

     

    四,使用第三方缓存(二级)

    以 ehcache 为例

    1.导入依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com</groupId>
        <artifactId>mybatis</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.15</version>
            </dependency>
            <dependency>
                <groupId>org.ehcache</groupId>
                <artifactId>ehcache</artifactId>
                <version>3.7.0</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.caches</groupId>
                <artifactId>mybatis-ehcache</artifactId>
                <version>1.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-nop</artifactId>
                <version>1.7.26</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <!-- 指定jdk -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    2.添加缓存配置文件 ehcache.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
        <!-- 磁盘保存路径 -->
        <diskStore path="D: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="10000"
                maxElementsOnDisk="10000000"
                eternal="false"
                overflowToDisk="true"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>

    3.修改缓存配置,xxxMapper.xml

    <!-- namespace 对应接口文件的全路径 -->
    <mapper namespace="com.dao.MyUserMapper">
        <!-- 引用缓存:namespace:指定和哪个名称空间下的缓存一样 -->
        <!--<cache-ref namespace="com.dao.Dept"/>-->
        
        <!--使用自定义缓存-->
        <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
        <select id="selectMyUserById" resultType="myUser" useCache="true" flushCache="false">
          select * from myuser where id = #{id}
        </select>

    5.测试

    /**
     * 使用第三方缓存:
     *    1)、导入依赖;
     *    2)、xxxMapper.xml 中使用自定义缓存 <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
     */
    public static void main(String[] args) throws IOException {
        SqlSession session = null;
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            
            session = sqlSessionFactory.openSession();
            MyUserMapper mapper = session.getMapper(MyUserMapper.class);
            System.out.println(mapper.selectMyUserById(7));
            
            session.close();
    
            session = sqlSessionFactory.openSession();
            mapper = session.getMapper(MyUserMapper.class);
            System.out.println(mapper.selectMyUserById(7));
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }


    http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache

    http://www.mybatis.org/ehcache-cache/index.html

  • 相关阅读:
    如何面试程序员?
    开始做项目
    ===
    依赖注入获得一个对象却想返回不同的值(Error)
    java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]ResultSet can not reread row data for column 4.
    java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]Object has been closed.
    .net 4.5新特性
    有限状态机简单示例
    JavaScript入门经典(第四版)文摘
    小强升职记读后感
  • 原文地址:https://www.cnblogs.com/jhxxb/p/10495953.html
Copyright © 2020-2023  润新知