MyBatis的缓存分为一级缓存和二级缓存,两种缓存的缓存粒度是一样的,都是对应一条sql查询语句,但是二者的生命周期是不一样的,一级缓存的生命周期是SqlSession对象的使用期间,随着SqlSession对象的死亡而消失;二级缓存的生命周期是同MyBatis应用一样长的;并且是首先查询二级缓存,然后再查询一起缓存正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持。
1.缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper ,并且可自定义存储源,如 Ehcache。二级缓存的作用域是全局的,二级缓存在SqlSession关闭或提交之后才会生效。
在分析MyBatis的二级缓存之前,我们先简单看下MyBatis中一个关于二级缓存的类(其他相关的类和接口之前已经分析过):org.apache.ibatis.mapping.MappedStatement:MappedStatement类在Mybatis框架中用于表示XML文件中一个sql语句节点,即一个、或者标签。Mybatis框架在初始化阶段会对XML配置文件进行读取,将其中的sql语句节点对象化为一个个MappedStatement对象3. 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。mybatis的一级缓存作用就是在同一个session过程中,将会对相同数据的存取不通过数据库,而是通过缓存机制提高效率。
看测试代码

import java.io.IOException;import java.io.InputStream;import junit.framework.TestCase;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 org.junit.Test;import com.guowuxin.mybatis.mapper.UserMapper;import com.guowuxin.mybatis.model.User;/*用户表drop table if exists user;create table user( id int primary key auto_increment, username varchar(50) unique, password varchar(100), useraddress varchar(50));insert into user values(1,"guowuxin","guowuxin","beijing");insert into user values(1,"guoxiaoming","guowuxin","beijing");*///如果两次查询都是查询1 将只会发送一条sql语句到数据库,否则将是2条sql语句
public class TestClass extends TestCase{ public static SqlSessionFactory sqlSessionFactoy; static{ String resource = "mybatis-config.xml"; try { InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactoy = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } @Test public void testgetUserById() { SqlSession sqlSession = sqlSessionFactoy.openSession(); try { UserMapper mapper = sqlSession.getMapper(UserMapper.class); User u1=mapper.getUserById(1); System.out.println(u1); User u2=mapper.getUserById(1); System.out.println(u2); sqlSession.commit(); } finally { sqlSession.close(); } }}package com.guowuxin.mybatis.mapper;import com.guowuxin.mybatis.model.User;//通过面向接口的mybatis编程方式,需要保证方法名和配置文件中的id名称一致public interface UserMapper { public User getUserById(int id);}package com.guowuxin.mybatis.model;public class User { private int id; private int age; private String userName; private String userAddress; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserAddress() { return userAddress; } public void setUserAddress(String userAddress) { this.userAddress = userAddress; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("id=").append(id).append(","); builder.append("age=").append(age).append(","); builder.append("userName=").append(userName).append(","); builder.append("userAddress=").append(userAddress).append("."); return builder.toString(); }}log4j.rootLogger=DEBUG,Console#Consolelog4j.appender.Console=org.apache.log4j.ConsoleAppenderlog4j.appender.Console.layout=org.apache.log4j.PatternLayoutlog4j.appender.Console.layout.ConversionPattern=%d %t %-5p (%c) - %m%nlog4j.logger.com.ibatis=DEBUGlog4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUGlog4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUGlog4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUGlog4j.logger.java.sql.Connection=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <typeAliases> <typeAlias alias="User" type="com.guowuxin.mybatis.model.User" /> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/guowuxin" /> <property name="username" value="root" /> <property name="password" value="admin" /> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml" /> </mappers></configuration><?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 这里namespace必须是PostsMapper接口的路径,不然要运行的时候要报错 “is not known to the MapperRegistry”--> <mapper namespace="com.guowuxin.mybatis.mapper.UserMapper"> <!-- 这儿的resultType是配置在mybatis-config.xml中得别名 --> <select id="getUserById" parameterType="int" resultType="User"> select * from user where id=#{id} </select></mapper>结果:………………..
2016-02-24 20:12:37,568 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:37,568 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=? 2016-02-24 20:12:37,586 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:12:37,593 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:37,593 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:37,593 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052 to pool.如果取得2个不同的id…………………..
2016-02-24 20:12:57,655 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 3874052.2016-02-24 20:12:57,656 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,656 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=? 2016-02-24 20:12:57,664 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:12:57,679 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,679 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=? 2016-02-24 20:12:57,679 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 2(Integer)id=2,age=0,userName=guoxiaoming,userAddress=beijing.2016-02-24 20:12:57,679 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,679 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,679 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052 to pool.二级缓存机制指的则是在不同的session之间都可以共享相同的数据内容。
import java.io.IOException;import java.io.InputStream;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 org.junit.Test;import com.guowuxin.mybatis.mapper.UserMapper;import com.guowuxin.mybatis.model.User;import junit.framework.TestCase;public class TestTwoCache extends TestCase{ public static SqlSessionFactory sqlSessionFactory; static{ String resource = "mybatis-config.xml"; try { InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } @Test public void testCache() { SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); try { UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); User u1=mapper1.getUserById(1); System.out.println(u1); sqlSession1.commit(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User u2=mapper2.getUserById(1); System.out.println(u2); sqlSession2.commit(); } finally { sqlSession1.close(); sqlSession2.close(); } }}在没有开启二级缓存之前,将会执行两次sql语句。
2016-02-24 20:28:10,786 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - PooledDataSource forcefully closed/removed all connections.2016-02-24 20:28:10,850 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Openning JDBC Connection2016-02-24 20:28:11,011 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 3874052.2016-02-24 20:28:11,012 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:28:11,013 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=? 2016-02-24 20:28:11,030 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:28:11,040 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Openning JDBC Connection2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 29420695.2016-02-24 20:28:11,154 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]2016-02-24 20:28:11,154 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=? 2016-02-24 20:28:11,154 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052 to pool.2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]2016-02-24 20:28:11,154 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 29420695 to pool.------------------------在实体类映射文件中加入<?xml version=”1.0” encoding=”UTF-8” ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 这里namespace必须是PostsMapper接口的路径,不然要运行的时候要报错 “is not known to the MapperRegistry”--> <mapper namespace="com.guowuxin.mybatis.mapper.UserMapper"> <!-- 这儿的resultType是配置在mybatis-config.xml中得别名 --> <cache /> <select id="getUserById" parameterType="int" resultType="User"> select * from user where id=#{id} </select></mapper>2016-02-25 10:15:11,162 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 22522451.2016-02-25 10:15:11,164 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@157aa53]2016-02-25 10:15:11,164 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Preparing: select * from user where id=? 2016-02-25 10:15:11,182 main DEBUG (com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-25 10:15:11,196 main DEBUG (org.apache.ibatis.cache.decorators.LoggingCache) - Cache Hit Ratio [com.guowuxin.mybatis.mapper.UserMapper]: 0.5id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-25 10:15:11,196 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@157aa53]2016-02-25 10:15:11,196 main DEBUG (org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@157aa53]2016-02-25 10:15:11,197 main DEBUG (org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 22522451 to pool.就会发现cache命中,只执行了一次sql语句———————————————如果二级缓存想要命中实现,则必须要将上一次sqlSession commit之后才能生效,不然将不会命中,原因:两个不同的session必须提交前面一个session才能缓存生效的原因是因为mybatis的缓存会被一个transactioncache类包装住,所有的cache.putObject全部都会被暂时存到一个map里,等事务提交以后,这个map里的缓存对象才会被真正的cache类执行putObject操作。这么设计的原因是为了防止事务执行过程中出异常导致回滚,如果get到object后直接put进缓存,万一发生回滚,就很容易导致mybatis缓存被脏读
参考文章:http://my.oschina.net/KingPan/blog/280167#OSC_h1_1