MyBatis的緩存分為一級緩存和二級緩存吸祟,兩種緩存的緩存粒度是一樣的瑟慈,都是對應(yīng)一條sql查詢語句,但是二者的生命周期是不一樣的屋匕,一級緩存的生命周期是SqlSession對象的使用期間葛碧,隨著SqlSession對象的死亡而消失;二級緩存的生命周期是同MyBatis應(yīng)用一樣長的过吻;并且是首先查詢二級緩存进泼,然后再查詢一起緩存正如大多數(shù)持久層框架一樣蔗衡,MyBatis 同樣提供了一級緩存和二級緩存的支持。
1.緩存乳绕,其存儲作用域?yàn)?Session绞惦,當(dāng) Session flush 或 close 之后,該Session中的所有 Cache 就將清空洋措。
2. 二級緩存與一級緩存其機(jī)制相同济蝉,默認(rèn)也是采用 PerpetualCache,HashMap存儲菠发,不同在于其存儲作用域?yàn)?Mapper 王滤,并且可自定義存儲源,如 Ehcache滓鸠。二級緩存的作用域是全局的雁乡,二級緩存在SqlSession關(guān)閉或提交之后才會生效。
在分析MyBatis的二級緩存之前糜俗,我們先簡單看下MyBatis中一個(gè)關(guān)于二級緩存的類(其他相關(guān)的類和接口之前已經(jīng)分析過):org.apache.ibatis.mapping.MappedStatement:MappedStatement類在Mybatis框架中用于表示XML文件中一個(gè)sql語句節(jié)點(diǎn)踱稍,即一個(gè)、或者標(biāo)簽悠抹。Mybatis框架在初始化階段會對XML配置文件進(jìn)行讀取珠月,將其中的sql語句節(jié)點(diǎn)對象化為一個(gè)個(gè)MappedStatement對象3. 對于緩存數(shù)據(jù)更新機(jī)制,當(dāng)某一個(gè)作用域(一級緩存Session/二級緩存Namespaces)的進(jìn)行了 C/U/D 操作后楔敌,默認(rèn)該作用域下所有 select 中的緩存將被clear桥温。mybatis的一級緩存作用就是在同一個(gè)session過程中,將會對相同數(shù)據(jù)的存取不通過數(shù)據(jù)庫梁丘,而是通過緩存機(jī)制提高效率。
看測試代碼
importjava.io.IOException;importjava.io.InputStream;importjunit.framework.TestCase;importorg.apache.ibatis.io.Resources;importorg.apache.ibatis.session.SqlSession;importorg.apache.ibatis.session.SqlSessionFactory;importorg.apache.ibatis.session.SqlSessionFactoryBuilder;importorg.junit.Test;importcom.guowuxin.mybatis.mapper.UserMapper;importcom.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 intouservalues(1,"guowuxin","guowuxin","beijing");insert intouservalues(1,"guoxiaoming","guowuxin","beijing");*/
//如果兩次查詢都是查詢1 將只會發(fā)送一條sql語句到數(shù)據(jù)庫旺韭,否則將是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();? ? ? ? }? ? }}
packagecom.guowuxin.mybatis.mapper;importcom.guowuxin.mybatis.model.User;//通過面向接口的mybatis編程方式氛谜,需要保證方法名和配置文件中的id名稱一致publicinterfaceUserMapper{publicUser getUserById(int id);}packagecom.guowuxin.mybatis.model;publicclassUser{privateint id;privateint age;privateString userName;privateString userAddress;publicint getId() {returnid;? ? }publicvoidsetId(int id) {this.id = id;? ? }publicString getUserName() {returnuserName;? ? }publicvoidsetUserName(String userName) {this.userName = userName;? ? }publicString getUserAddress() {returnuserAddress;? ? }publicvoidsetUserAddress(String userAddress) {this.userAddress = userAddress;? ? }publicint getAge() {returnage;? ? }publicvoidsetAge(int age) {this.age = age;? ? }? ? @OverridepublicString toString()? ? {finalStringBuilder builder =newStringBuilder();? ? ? ? builder.append("id=").append(id).append(",");? ? ? ? builder.append("age=").append(age).append(",");? ? ? ? builder.append("userName=").append(userName).append(",");? ? ? ? builder.append("userAddress=").append(userAddress).append(".");returnbuilder.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"><?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接口的路徑,不然要運(yùn)行的時(shí)候要報(bào)錯 “is not known to the MapperRegistry”--><!-- 這兒的resultType是配置在mybatis-config.xml中得別名 -->select * from user where id=#{id}
結(jié)果:………………..
2016-02-24 20:12:37,568 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:37,568 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>? Preparing: select *from user whereid=? 2016-02-24 20:12:37,586 mainDEBUG(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 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommittotrueon JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:37,593 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:37,593 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052topool.
如果取得2個(gè)不同的id…………………..
2016-02-24 20:12:57,655 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 3874052.2016-02-24 20:12:57,656 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,656 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>? Preparing: select *from user whereid=? 2016-02-24 20:12:57,664 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:12:57,679 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,679 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>? Preparing: select *from user whereid=? 2016-02-24 20:12:57,679 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 2(Integer)id=2,age=0,userName=guoxiaoming,userAddress=beijing.2016-02-24 20:12:57,679 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommittotrueon JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,679 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:12:57,679 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052topool.
二級緩存機(jī)制指的則是在不同的session之間都可以共享相同的數(shù)據(jù)內(nèi)容区端。
importjava.io.IOException;importjava.io.InputStream;importorg.apache.ibatis.io.Resources;importorg.apache.ibatis.session.SqlSession;importorg.apache.ibatis.session.SqlSessionFactory;importorg.apache.ibatis.session.SqlSessionFactoryBuilder;importorg.junit.Test;importcom.guowuxin.mybatis.mapper.UserMapper;importcom.guowuxin.mybatis.model.User;importjunit.framework.TestCase;publicclassTestTwoCacheextendsTestCase{publicstaticSqlSessionFactory sqlSessionFactory;static{? ? ? ? String resource ="mybatis-config.xml";try{? ? ? ? ? ? InputStream inputStream = Resources.getResourceAsStream(resource);? ? ? ? ? ? sqlSessionFactory =newSqlSessionFactoryBuilder().build(inputStream);? ? ? ? }catch(IOException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }? ? @TestpublicvoidtestCache() {? ? ? ? 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();? ? ? }? ? }}
在沒有開啟二級緩存之前值漫,將會執(zhí)行兩次sql語句。
2016-02-24 20:28:10,786 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - PooledDataSource forcefully closed/removed all connections.2016-02-24 20:28:10,850 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Openning JDBC Connection2016-02-24 20:28:11,011 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 3874052.2016-02-24 20:28:11,012 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:28:11,013 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>? Preparing: select *from user whereid=? 2016-02-24 20:28:11,030 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:28:11,040 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Openning JDBC Connection2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 29420695.2016-02-24 20:28:11,154 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]2016-02-24 20:28:11,154 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>? Preparing: select *from user whereid=? 2016-02-24 20:28:11,154 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommittotrueon JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3b1d04]2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 3874052topool.2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommittotrueon JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1c0ec97]2016-02-24 20:28:11,154 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 29420695topool.------------------------
在實(shí)體類映射文件中加入<?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接口的路徑织盼,不然要運(yùn)行的時(shí)候要報(bào)錯 “is not known to the MapperRegistry”--><!-- 這兒的resultType是配置在mybatis-config.xml中得別名 -->select * from user where id=#{id}
2016-02-25 10:15:11,162 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Created connection 22522451.2016-02-25 10:15:11,164 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@157aa53]2016-02-25 10:15:11,164 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==>? Preparing: select *from user whereid=? 2016-02-25 10:15:11,182 mainDEBUG(com.guowuxin.mybatis.mapper.UserMapper.getUserById) - ==> Parameters: 1(Integer)id=1,age=0,userName=guowuxin,userAddress=beijing.2016-02-25 10:15:11,196 mainDEBUG(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 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Resetting autocommittotrueon JDBC Connection [com.mysql.jdbc.JDBC4Connection@157aa53]2016-02-25 10:15:11,196 mainDEBUG(org.apache.ibatis.transaction.jdbc.JdbcTransaction) - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@157aa53]2016-02-25 10:15:11,197 mainDEBUG(org.apache.ibatis.datasource.pooled.PooledDataSource) - Returned connection 22522451topool.
就會發(fā)現(xiàn)cache命中杨何,只執(zhí)行了一次sql語句———————————————如果二級緩存想要命中實(shí)現(xiàn),則必須要將上一次sqlSession commit之后才能生效沥邻,不然將不會命中危虱,原因:兩個(gè)不同的session必須提交前面一個(gè)session才能緩存生效的原因是因?yàn)閙ybatis的緩存會被一個(gè)transactioncache類包裝住,所有的cache.putObject全部都會被暫時(shí)存到一個(gè)map里唐全,等事務(wù)提交以后埃跷,這個(gè)map里的緩存對象才會被真正的cache類執(zhí)行putObject操作蕊玷。這么設(shè)計(jì)的原因是為了防止事務(wù)執(zhí)行過程中出異常導(dǎo)致回滾,如果get到object后直接put進(jìn)緩存弥雹,萬一發(fā)生回滾垃帅,就很容易導(dǎo)致mybatis緩存被臟讀
參考文章:http://my.oschina.net/KingPan/blog/280167#OSC_h1_1