MyBatis3-缓存使用

一级缓存和二级缓存的区别:

1、一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为同一个SqlSession,当Session flush或close之后,该Session中的所有Cache就将清空。

2、二级缓存:与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。

3、对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear。

如果要实现MyBatis的二级缓存,一般来说有如下两种方式:

1. 采用MyBatis内置的Cache机制。

2. 采用三方Cache框架, 比如EhCache, OSCache等等。

下面是基于MyBatis的内置的一级和二级缓存测试:

1、一级缓存配置

默认是开启的,如果不想用缓存,直接在select节点中增加useCache="false"和flushCache="true"属性即可。如:

    <select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList" useCache="false" flushCache="true">
        select user.id,user.userName,user.userAddress,article.id as aid,article.title,article.content from user,article where user.id=article.userid and user.id=#{id}
    </select>

flushCache:将其设置为true,无论语句什么时候被调用,都会导致缓存被清空。默认值:false。

useCache:将其设置为true,将会导致本条语句的结果被缓存。默认值:true。

测试代码如下:

注意:此时在select节点中缓存是开启的。

package com.jsoft.testmybatis.test1;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jsoft.testmybatis.inter.IUserOperation;
import com.jsoft.testmybatis.models.Article;
import com.jsoft.testmybatis.models.User;

public class App {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("Configuration.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            IUserOperation userOperation = session.getMapper(IUserOperation.class);

            try {
                System.out.println("第一次查询开始");
                //List
                List<User> users = userOperation.selectUsers("%");
                for (User tempUser : users) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("第一次查询结束");

                System.out.println("第二次查询开始");
                //List
                List<User> users2 = userOperation.selectUsers("%");
                for (User tempUser : users2) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("第二次查询结束");

                System.out.println("加入数据开始开始");
                //Add
                User addUser = new User();
                addUser.setUserAddress("guangdong,guangzhou");
                addUser.setUserName("eason");
                addUser.setUserAge("80");
                int addRetCount = userOperation.addUser(addUser);
                session.commit();//必须提交事务,否则不会写入到数据库。如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。
                System.out.println("增加数据影响的行数:" + addRetCount);
                if (addUser.getId() > 0) {
                    System.out.println("增加数据成功,新增的id为:" + addUser.getId());
                }
                System.out.println("加入数据开始结束");

                System.out.println("第三次查询开始");
                //List
                List<User> users3 = userOperation.selectUsers("%");
                for (User tempUser : users3) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("第三次查询结束");

                //强制刷新缓存
                session.clearCache();

                System.out.println("第四次查询开始");
                //List
                List<User> users4 = userOperation.selectUsers("%");
                for (User tempUser : users4) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("第三次查询结束");

            } catch (Exception e) {
                // TODO: handle exception
                session.rollback();//有异常时回滚数据
                e.printStackTrace();
            }

        } finally {
            session.close();//close之后缓存清空
        }
    }
}

结果如下:

可以看出,缓存生效了。

下面测试select节点中配置缓存关闭的情况,结果如下:

可以看出缓存去除了,全部都是真实查询。

其实上面的测试示例还少了一个Session2的测试,不然效果不佳。

2、二级缓存测试:

开启二级缓存,在XML配置文件中添加Cache节点即可,参考配置如下:

    <cache eviction="FIFO"  <!--回收策略为先进先出 -->
        flushInterval="60000" <!--自动刷新时间60s -->
        size="512" <!--最多缓存512个引用对象 -->
        readOnly="true"/> <!--只读 -->

测试代码如下:

package com.jsoft.testmybatis.test1;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.jsoft.testmybatis.inter.IUserOperation;
import com.jsoft.testmybatis.models.Article;
import com.jsoft.testmybatis.models.User;

public class App2 {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("Configuration.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();
        SqlSession session2 = sqlSessionFactory.openSession();
        try {
            IUserOperation userOperation = session.getMapper(IUserOperation.class);
            IUserOperation userOperation2 = session.getMapper(IUserOperation.class);
            try {
                System.out.println("Session1第一次查询开始");
                //List
                List<User> users = userOperation.selectUsers("%");
                for (User tempUser : users) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("Session1第一次查询结束");

                System.out.println("Session2第二次查询开始");
                //List
                List<User> users2 = userOperation2.selectUsers("%");
                for (User tempUser : users2) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("Session2第二次查询结束");

                System.out.println("Session1加入数据开始开始");
                //Add
                User addUser = new User();
                addUser.setUserAddress("guangdong,guangzhou");
                addUser.setUserName("eason");
                addUser.setUserAge("80");
                int addRetCount = userOperation.addUser(addUser);
                session.commit();//必须提交事务,否则不会写入到数据库。如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。
                System.out.println("增加数据影响的行数:" + addRetCount);
                if (addUser.getId() > 0) {
                    System.out.println("增加数据成功,新增的id为:" + addUser.getId());
                }
                System.out.println("Session1加入数据开始结束");

                System.out.println("Session1第三次查询开始");
                //List
                List<User> users3 = userOperation.selectUsers("%");
                for (User tempUser : users3) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("Session1第三次查询结束");

                //强制刷新缓存
                session.clearCache();

                System.out.println("Session1第四次查询开始");
                //List
                List<User> users4 = userOperation.selectUsers("%");
                for (User tempUser : users4) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("Session1第四次查询结束");

                System.out.println("Session2第五次查询开始");
                //List
                List<User> users5 = userOperation2.selectUsers("%");
                for (User tempUser : users5) {
                    System.out.println(tempUser.getUserAddress());
                    System.out.println(tempUser.getUserName());
                }
                System.out.println("Session2第五次查询结束");
            } catch (Exception e) {
                // TODO: handle exception
                session.rollback();//有异常时回滚数据
                session2.rollback();//有异常时回滚数据
                e.printStackTrace();
            }

        } finally {
            session.close();//close之后缓存清空
            session2.close();//close之后缓存清空
        }
    }
}

测试结果如下:

可以看出Cache不受Session的限制,且操作缓存的方法是一致的。

3、总结

1、映射语句文件中的所有select语句将会被缓存。

2、映射语句文件中的所有insert,update和delete语句会刷新缓存。

3、缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。

4、缓存会根据指定的时间间隔来刷新。

5、每个缓存可以存储 1024 个列表或对象的引用(不管查询出来的结果是什么) 。

6、缓存将作为“读/写”缓存,意味着获取的对象不是共享的且对调用者是安全的。不会有其它的调用者或线程潜在修改。

4、参考:

http://www.yihaomen.com/article/java/428.htm(文中结论主要转自此篇)

http://www.cnblogs.com/xdp-gacl/p/4270403.html(文中结论主要转自此篇)

http://blog.csdn.net/u012373815/article/details/47069223

http://blog.csdn.net/luanlouis/article/details/41390801

http://blog.csdn.net/luanlouis/article/details/41280959

http://blog.csdn.net/luanlouis/article/details/41408341

5、测试工程:https://github.com/easonjim/5_java_example/tree/master/mybatis/test12

下面是第三方缓存框架EhCache的配置使用:

说明:为什么要使用EhCache,因为它提供了很多内置缓存没有的强大功能,比如缓存的集群等。但是有一点,EhCache使用的日志框架是slf4j,需要而外配置使用slf4j,如果不想使用slf4j,也可以配置使用log4j。下面就是使用log4j的示例。

1、添加POM依赖:

        <!-- ehcache -->
        <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
        <!-- change slf4j for log4j -->
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>

而对于使用log4j组件时,需要下载一个特殊的包slf4j-log4j12才能正常输出日志,参考:http://jiajun-kucoo.blog.163.com/blog/static/64148688201352791439772/

2、在resources中增加ehcache.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore path="D:/ehcache" />
    <!--
    name:Cache的唯一标识
    maxElementsInMemory:内存中最大缓存对象数
    maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大
    eternal:Element是否永久有效,一但设置了,timeout将不起作用
    overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中
    timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大
    timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大
    diskPersistent:是否缓存虚拟机重启期数据
    diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
    diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
    memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)
    备注: 持久化到硬盘的路径由虚拟机参数"java.io.tmpdir"决定.  例如, 在windows中, 会在此路径下 C:\Documents and Settings\li\Local Settings\Temp  在linux中, 通常会在: /tmp 下  System.out.println(System.getProperty("java.io.tmpdir"));
    -->
    <defaultCache maxElementsInMemory="1000"
        maxElementsOnDisk="10000000"
        eternal="false"
        overflowToDisk="true"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="FIFO">
    </defaultCache>
    <!--
        <cache name="test" overflowToDisk="true" eternal="false"
        timeToIdleSeconds="300" timeToLiveSeconds="600" maxElementsInMemory="1000"
        maxElementsOnDisk="10" diskPersistent="true" diskExpiryThreadIntervalSeconds="300"
        diskSpoolBufferSizeMB="100" memoryStoreEvictionPolicy="LRU" />
    -->
</ehcache>

详细配置参考:http://blog.csdn.net/etttttss/article/details/17141485

3、在user.xml中,也就是Mapper文件配置第三方缓存:

    <!-- 以下两个<cache>标签二选一,第一个可以输出日志,第二个不输出日志 -->
    <cache type="org.mybatis.caches.ehcache.LoggingEhcache" />
    <!-- <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> -->

4、最后在log4j.properties文件中增加日志的切入点

log4j.logger.net.sf.ehcache=DEBUG

这句话的意思是在这个包net.sf.ehcache下的DEBUG级别的日志全部输出。

5、打印的日志如下:

6、参考:

http://www.yihaomen.com/article/java/428.htm

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

7、测试工程:https://github.com/easonjim/5_java_example/tree/master/mybatis/test13

自定义自己的缓存:

其实从上面的配置来看,<cache type="org.mybatis.caches.ehcache.LoggingEhcache" />节点中的type指定的是具体的类,而这个类继承的是Cache接口,所以只要自己自定义继承这个接口实现自己特定类也能达到缓存效果。

1、参考:http://www.cnblogs.com/lzy1991/p/5335249.html

时间: 2024-10-25 09:21:35

MyBatis3-缓存使用的相关文章

mybatis配置自带缓存和第三方缓存

参考:https://mybatis.github.io/mybatis-3/zh/sqlmap-xml.html, http://www.yihaomen.com/article/Java/428.htm 许多应用程序,为了提高性能而增加缓存, 特别是从数据库中获取的数据. 在默认情况下,mybatis 的一级缓存是默认开启的.类似于hibernate, 所谓一级缓存,也就是基于同一个sqlsession 的查询语句,即 session 级别的缓存,非全局缓存,或者非二级缓存. 如果要实现 m

SpringMVC4 + Spring + MyBatis3 【转】

本文使用最新版本(4.1.5)的springmvc+spring+mybatis,采用最间的配置方式来进行搭建. 1. web.xml 我们知道springmvc是基于Servlet: DispatcherServlet来处理分发请求的,所以我们需要先在web.xml文件中配置DispatcherServlet,而Spring的启动则是使用了监听器,所以需要配置spring的监听器: <?xml version="1.0" encoding="UTF-8"?&

java:Mybatis框架3(二级缓存,延时和积极加载,SSI(Ibatis)集成,SSM集成)

1.二级缓存: 需要导入二级缓存jar包: mybatis03: ehcache.xml: <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <diskStore path="java.io.tmpdir"/> <!-- Mandat

---Mybatis3学习笔记(1)

附上Mybatis3的中文官网:http://www.mybatis.org/mybatis-3/zh/index.html Mybatis简介: 1.首先他是一个持久层框架. 2.支持普通SQL,存储过程和高级映射 3.内部封装了JDBC,使开发者只需要关注SQL本身 4.几乎避免了所有的JDBC代码和手动参数设置以及获取结果集 5.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Jav

MyBatis3一对一,一对多

在学习MyBatis3的过程中,文档上面一直在强调一个id的东西!在做这个实验的时候,也因为没有理解清楚id含义而导致一对多的"多"中也只有一条数据.id和result的唯一不同是id表示的结果将是当比较对象实例时用到的标识属性.这帮助来改进整体表现,特别是缓存和嵌入结果映射.所以不同数据的id应该唯一区别,不然导致数据结果集只有一条数据. 一.表 二.实体 1.person [java] view plain copy package com.kerwin.mybatis.pojo;

SpringMVC4 + Spring + MyBatis3

SpringMVC4 + Spring + MyBatis3 本文使用最新版本(4.1.5)的springmvc+spring+mybatis,采用最间的配置方式来进行搭建. 1. web.xml 我们知道springmvc是基于Servlet: DispatcherServlet来处理分发请求的,所以我们需要先在web.xml文件中配置DispatcherServlet,而Spring的启动则是使用了监听器,所以需要配置spring的监听器: <?xml version="1.0&quo

6.MyBaits的分页和缓存查询

1. 创建javaweb项目MyBaits_Page_CaChe 2.在项目的WebRoot下的WEB-INF下的lib文件下加入jar文件 log4j-1.2.17.jar mybatis-3.2.3.jar ojdbc14.jar 3.在src下创建jdbc.properties属性文件 1 driver=oracle.jdbc.driver.OracleDriver 2 url=jdbc\:oracle\:thin\:@127.0.0.1\:1521\:orcl 3 username=sc

MyBatis学习手记(二)MyBatis缓存机制

MyBatis学习手记二 前:MyBatis官方学习(中文)文档 http://mybatis.github.io/mybatis-3/zh/index.html 一,首先,要使用MyBatis必须使用官方提供的MyBatis的JAR包              链接:https://github.com/mybatis/mybatis-3/releases 这里使用的数据库是MySQL,所以还需要Mysql的驱动包. 二,看MyBatis官方介绍,说MyBatis支持一级缓存,二级缓存.这里才

Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法(转)

一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基本上没有什么问题,但唯一可能出现问题的就是在hibernate做添加操作生成主键策略的时候.因为我们都知道hibernate的数据库本地方言会针对不同的数据库采用不同的主键生成策略. 所以针对这一问题不得不采用自定义的主键生成策略,自己写一个主键生成器的表来维护主键生成方式或以及使用其他的方式来生成

故障排除 Mybatis ORA-01000 和 本地缓存问题

※异常信息 环境 MyBatis Oracle11.2c Terasoluna BatchCaused by: org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: ORA-00604: 再帰SQLレベル1でエラーが発生しました.ORA-01000: 最大オープン?カーソル数を超えました.ORA-00604: 再帰SQLレベル1で