面試官:mybatis的緩存機(jī)制是怎么樣的酗昼?

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市剪勿,隨后出現(xiàn)的幾起案子贸诚,更是在濱河造成了極大的恐慌,老刑警劉巖厕吉,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酱固,死亡現(xiàn)場離奇詭異,居然都是意外死亡赴涵,警方通過查閱死者的電腦和手機(jī)媒怯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來髓窜,“玉大人扇苞,你說我怎么就攤上這事〖淖荩” “怎么了鳖敷?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長程拭。 經(jīng)常有香客問我定踱,道長,這世上最難降的妖魔是什么恃鞋? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任崖媚,我火速辦了婚禮,結(jié)果婚禮上恤浪,老公的妹妹穿的比我還像新娘畅哑。我一直安慰自己,他們只是感情好水由,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布荠呐。 她就那樣靜靜地躺著,像睡著了一般砂客。 火紅的嫁衣襯著肌膚如雪泥张。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天鞠值,我揣著相機(jī)與錄音媚创,去河邊找鬼。 笑死彤恶,一個(gè)胖子當(dāng)著我的面吹牛筝野,可吹牛的內(nèi)容都是我干的晌姚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼歇竟,長吁一口氣:“原來是場噩夢啊……” “哼挥唠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起焕议,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宝磨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后盅安,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唤锉,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年别瞭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窿祥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝙寨,死狀恐怖晒衩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情墙歪,我是刑警寧澤听系,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站虹菲,受9級特大地震影響靠胜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毕源,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一浪漠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霎褐,春花似錦郑藏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拌牲。三九已至俱饿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塌忽,已是汗流浹背拍埠。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留土居,地道東北人枣购。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓嬉探,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棉圈。 傳聞我的和親對象是個(gè)殘疾皇子涩堤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348