Rumah Java javaTutorial 【MyBatis源码解析】MyBatis一二级缓存

【MyBatis源码解析】MyBatis一二级缓存

Jun 26, 2017 am 10:00 AM
mybatis Kod sumber menghuraikan

MyBatis缓存

我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相同的查询语句,完全可以把查询结果存储起来,下次查询同样的内容的时候直接从内存中获取数据即可,这样在某些场景下可以大大提升查询效率。

MyBatis的缓存分为两种:

  1. 一级缓存,一级缓存是SqlSession级别的缓存,对于相同的查询,会从缓存中返回结果而不是查询数据库

  2. 二级缓存,二级缓存是Mapper级别的缓存,定义在Mapper文件的标签中并需要开启此缓存,多个Mapper文件可以共用一个缓存,依赖标签配置

下面来详细看一下MyBatis的一二级缓存。

 

MyBatis一级缓存工作流程

接着看一下MyBatis一级缓存工作流程。前面说了,MyBatis的一级缓存是SqlSession级别的缓存,当openSession()的方法运行完毕或者主动调用了SqlSession的close方法,SqlSession就被回收了,一级缓存与此同时也一起被回收掉了。前面的文章有说过,在MyBatis中,无论selectOne还是selectList方法,最终都被转换为了selectList方法来执行,那么看一下SqlSession的selectList方法的实现:

 1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 2     try { 3       MappedStatement ms = configuration.getMappedStatement(statement); 4       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 5     } catch (Exception e) { 6       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e); 7     } finally { 8       ErrorContext.instance().reset(); 9     }10 }
Salin selepas log masuk
Salin selepas log masuk

继续跟踪第4行的代码,到BaseExeccutor的query方法:

1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {2     BoundSql boundSql = ms.getBoundSql(parameter);3     CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);4     return query(ms, parameter, rowBounds, resultHandler, key, boundSql);5 }
Salin selepas log masuk

第3行构建缓存条件CacheKey,这里涉及到怎么样条件算是和上一次查询是同一个条件的一个问题,因为同一个条件就可以返回上一次的结果回去,这部分代码留在下一部分分析。

接着看第4行的query方法的实现,代码位于CachingExecutor中:

 1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 2       throws SQLException { 3     Cache cache = ms.getCache(); 4     if (cache != null) { 5       flushCacheIfRequired(ms); 6       if (ms.isUseCache() && resultHandler == null) { 7         ensureNoOutParams(ms, parameterObject, boundSql); 8         @SuppressWarnings("unchecked") 9         List<E> list = (List<E>) tcm.getObject(cache, key);10         if (list == null) {11           list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);12           tcm.putObject(cache, key, list); // issue #578 and #11613         }14         return list;15       }16     }17     return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);18 }
Salin selepas log masuk
Salin selepas log masuk

第3行~第16行的代码先不管,继续跟第17行的query方法,代码位于BaseExecutor中:

 1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { 2     ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); 3     if (closed) { 4       throw new ExecutorException("Executor was closed."); 5     } 6     if (queryStack == 0 && ms.isFlushCacheRequired()) { 7       clearLocalCache(); 8     } 9     List<E> list;10     try {11       queryStack++;12       list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;13       if (list != null) {14         handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);15       } else {16         list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);17       }18     } finally {19       queryStack--;20     }21     ...22 }
Salin selepas log masuk

看12行,query的时候会尝试从localCache中去获取查询结果,如果获取到的查询结果为null,那么执行16行的代码从DB中捞数据,捞完之后会把CacheKey作为key,把查询结果作为value放到localCache中。

MyBatis一级缓存存储流程看完了,接着我们从这段代码中可以得到三个结论:

  1. MyBatis的一级缓存是SqlSession级别的,但是它并不定义在SqlSessio接口的实现类DefaultSqlSession中,而是定义在DefaultSqlSession的成员变量Executor中,Executor是在openSession的时候被实例化出来的,它的默认实现为SimpleExecutor

  2. MyBatis中的一级缓存,与有没有配置无关,只要SqlSession存在,MyBastis一级缓存就存在,localCache的类型是PerpetualCache,它其实很简单,一个id属性+一个HashMap属性而已,id是一个名为"localCache"的字符串,HashMap用于存储数据,Key为CacheKey,Value为查询结果

  3. MyBatis的一级缓存查询的时候默认都是会先尝试从一级缓存中获取数据的,但是我们看第6行的代码做了一个判断,ms.isFlushCacheRequired(),即想每次查询都走DB也行,将标签所在的Mapper的Namespace+标签中定义的sql语句

即只要两次查询满足以上三个条件且没有定义flushCache="true",那么第二次查询会直接从MyBatis一级缓存PerpetualCache中返回数据,而不会走DB。

 

MyBatis二级缓存

上面说完了MyBatis,接着看一下MyBatis二级缓存,还是从二级缓存工作流程开始。还是从DefaultSqlSession的selectList方法进去:

 1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 2     try { 3       MappedStatement ms = configuration.getMappedStatement(statement); 4       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 5     } catch (Exception e) { 6       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e); 7     } finally { 8       ErrorContext.instance().reset(); 9     }10 }
Salin selepas log masuk
Salin selepas log masuk

执行query方法,方法位于CachingExecutor中:

1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {2     BoundSql boundSql = ms.getBoundSql(parameterObject);3     CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);4     return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);5 }
Salin selepas log masuk

继续跟第4行的query方法,同样位于CachingExecutor中:

 1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 2       throws SQLException { 3     Cache cache = ms.getCache(); 4     if (cache != null) { 5       flushCacheIfRequired(ms); 6       if (ms.isUseCache() && resultHandler == null) { 7         ensureNoOutParams(ms, parameterObject, boundSql); 8         @SuppressWarnings("unchecked") 9         List<E> list = (List<E>) tcm.getObject(cache, key);10         if (list == null) {11           list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);12           tcm.putObject(cache, key, list); // issue #578 and #11613         }14         return list;15       }16     }17     return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);18 }
Salin selepas log masuk
Salin selepas log masuk

从这里看到,执行第17行的BaseExecutor的query方法之前,会先拿Mybatis二级缓存,而BaseExecutor的query方法会优先读取MyBatis一级缓存,由此可以得出一个重要结论:假如定义了MyBatis二级缓存,那么MyBatis二级缓存读取优先级高于MyBatis一级缓存

而第3行~第16行的逻辑:

  • 第5行的方法很好理解,根据flushCache=true或者flushCache=false判断是否要清理二级缓存

  • 第7行的方法是保证MyBatis二级缓存不会存储存储过程的结果

  • 第9行的方法先尝试从tcm中获取查询结果,这个tcm解释一下,这又是一个装饰器模式(数数MyBatis用到了多少装饰器模式了),创建一个事物缓存TranactionalCache,持有Cache接口,Cache接口的实现类就是根据我们在Mapper文件中配置的创建的Cache实例

  • 第10行~第12行,如果没有从MyBatis二级缓存中拿到数据,那么就会查一次数据库,然后放到MyBatis二级缓存中去

至于如何判定上次查询和这次查询是一次查询?由于这里的CacheKey和MyBatis一级缓存使用的是同一个CacheKey,因此它的判定条件和前文写过的MyBatis一级缓存三个维度的判定条件是一致的。

最后再来谈一点,"Cache cache = ms.getCache()"这句代码十分重要,这意味着Cache是从MappedStatement中获取到的,而MappedStatement又和每一个标签所在的Mapper的Namespace+标签中定义的sql语句

对于MapperA来说,其中的任何一个条件都没有变化,自然会将原结果返回。

这个问题对于MyBatis的二级缓存来说是一个无解的问题,因此使用MyBatis二级缓存有一个前提:必须保证所有的增删改查都在同一个命名空间下才行

Atas ialah kandungan terperinci 【MyBatis源码解析】MyBatis一二级缓存. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Penjelasan terperinci tentang fungsi Set tag dalam teg SQL dinamik MyBatis Penjelasan terperinci tentang fungsi Set tag dalam teg SQL dinamik MyBatis Feb 26, 2024 pm 07:48 PM

Tafsiran teg SQL dinamik MyBatis: Penjelasan terperinci tentang penggunaan teg Set MyBatis ialah rangka kerja lapisan kegigihan yang sangat baik Ia menyediakan banyak teg SQL dinamik dan boleh membina pernyataan operasi pangkalan data secara fleksibel. Antaranya, tag Set ialah tag yang digunakan untuk menjana klausa SET dalam kenyataan UPDATE, yang sangat biasa digunakan dalam operasi kemas kini. Artikel ini akan menerangkan secara terperinci penggunaan teg Set dalam MyBatis dan menunjukkan kefungsiannya melalui contoh kod tertentu. Apakah itu Set tag Set tag digunakan dalam MyBati

Penjelasan terperinci tentang ralat Oracle 3114: Cara menyelesaikannya dengan cepat Penjelasan terperinci tentang ralat Oracle 3114: Cara menyelesaikannya dengan cepat Mar 08, 2024 pm 02:42 PM

Penjelasan terperinci tentang ralat Oracle 3114: Bagaimana untuk menyelesaikannya dengan cepat, contoh kod khusus diperlukan Semasa pembangunan dan pengurusan pangkalan data Oracle, kami sering menghadapi pelbagai ralat, antaranya ralat 3114 adalah masalah yang agak biasa. Ralat 3114 biasanya menunjukkan masalah dengan sambungan pangkalan data, yang mungkin disebabkan oleh kegagalan rangkaian, pemberhentian perkhidmatan pangkalan data atau tetapan rentetan sambungan yang salah. Artikel ini akan menerangkan secara terperinci punca ralat 3114 dan cara menyelesaikan masalah ini dengan cepat, dan melampirkan kod tertentu

Menganalisis mekanisme caching MyBatis: bandingkan ciri dan penggunaan cache peringkat pertama dan cache peringkat kedua Menganalisis mekanisme caching MyBatis: bandingkan ciri dan penggunaan cache peringkat pertama dan cache peringkat kedua Feb 25, 2024 pm 12:30 PM

Analisis mekanisme caching MyBatis: Perbezaan dan aplikasi cache peringkat pertama dan cache peringkat kedua Dalam rangka kerja MyBatis, caching ialah ciri yang sangat penting yang boleh meningkatkan prestasi operasi pangkalan data dengan berkesan. Antaranya, cache tahap pertama dan cache tahap kedua adalah dua mekanisme caching yang biasa digunakan dalam MyBatis. Artikel ini akan menganalisis perbezaan dan aplikasi cache peringkat pertama dan cache peringkat kedua secara terperinci, dan memberikan contoh kod khusus untuk digambarkan. 1. Cache Tahap 1 Cache tahap 1 juga dipanggil cache setempat Ia didayakan secara lalai dan tidak boleh dimatikan. Cache tahap pertama ialah SqlSes

Analisis makna dan penggunaan titik tengah dalam PHP Analisis makna dan penggunaan titik tengah dalam PHP Mar 27, 2024 pm 08:57 PM

[Analisis makna dan penggunaan titik tengah dalam PHP] Dalam PHP, titik tengah (.) ialah operator yang biasa digunakan untuk menyambung dua rentetan atau sifat atau kaedah objek. Dalam artikel ini, kami akan menyelami makna dan penggunaan titik tengah dalam PHP, menggambarkannya dengan contoh kod konkrit. 1. Operator titik tengah rentetan Concatenate Penggunaan yang paling biasa dalam PHP adalah untuk menggabungkan dua rentetan. Dengan meletakkan . antara dua rentetan, anda boleh menyambungkannya untuk membentuk rentetan baharu. $string1=&qu

Analisis ciri baharu Win11: Bagaimana untuk melangkau log masuk ke akaun Microsoft Analisis ciri baharu Win11: Bagaimana untuk melangkau log masuk ke akaun Microsoft Mar 27, 2024 pm 05:24 PM

Analisis ciri baharu Win11: Cara melangkau log masuk ke akaun Microsoft Dengan keluaran Windows 11, ramai pengguna mendapati ia membawa lebih banyak kemudahan dan ciri baharu. Walau bagaimanapun, sesetengah pengguna mungkin tidak suka sistem mereka terikat pada akaun Microsoft dan ingin melangkau langkah ini. Artikel ini akan memperkenalkan beberapa kaedah untuk membantu pengguna melangkau log masuk ke akaun Microsoft dalam Windows 11 dan mencapai pengalaman yang lebih peribadi dan autonomi. Mula-mula, mari kita fahami sebab sesetengah pengguna enggan log masuk ke akaun Microsoft mereka. Di satu pihak, sesetengah pengguna bimbang bahawa mereka

Parsing Wormhole NTT: rangka kerja terbuka untuk sebarang Token Parsing Wormhole NTT: rangka kerja terbuka untuk sebarang Token Mar 05, 2024 pm 12:46 PM

Wormhole ialah peneraju dalam kebolehkendalian blockchain, memfokuskan pada mencipta sistem terdesentralisasi yang berdaya tahan, kalis masa hadapan yang mengutamakan pemilikan, kawalan dan inovasi tanpa kebenaran. Asas visi ini ialah komitmen terhadap kepakaran teknikal, prinsip etika dan penjajaran komuniti untuk mentakrifkan semula landskap kebolehoperasian dengan kesederhanaan, kejelasan dan rangkaian luas penyelesaian berbilang rantaian. Dengan peningkatan bukti pengetahuan sifar, penyelesaian penskalaan dan piawaian token yang kaya dengan ciri, rantaian blok menjadi lebih berkuasa dan kesalingoperasian menjadi semakin penting. Dalam persekitaran aplikasi yang inovatif ini, sistem tadbir urus baharu dan keupayaan praktikal membawa peluang yang belum pernah berlaku sebelum ini kepada aset merentas rangkaian. Pembina protokol kini bergelut dengan cara untuk beroperasi dalam pelbagai rantaian yang muncul ini

Bagaimana untuk memaparkan kod sumber kod PHP dalam penyemak imbas tanpa ditafsir dan dilaksanakan? Bagaimana untuk memaparkan kod sumber kod PHP dalam penyemak imbas tanpa ditafsir dan dilaksanakan? Mar 11, 2024 am 10:54 AM

Bagaimana untuk memaparkan kod sumber kod PHP dalam penyemak imbas tanpa ditafsir dan dilaksanakan? PHP ialah bahasa skrip sebelah pelayan yang biasa digunakan untuk membangunkan laman web dinamik. Apabila fail PHP diminta pada pelayan, pelayan mentafsir dan melaksanakan kod PHP di dalamnya dan menghantar kandungan HTML akhir kepada penyemak imbas untuk paparan. Walau bagaimanapun, kadangkala kami mahu memaparkan kod sumber fail PHP terus dalam penyemak imbas dan bukannya dilaksanakan. Artikel ini akan memperkenalkan cara untuk memaparkan kod sumber kod PHP dalam penyemak imbas tanpa ditafsir dan dilaksanakan. Dalam PHP, anda boleh menggunakan

Apache2 tidak boleh menghuraikan fail PHP dengan betul Apache2 tidak boleh menghuraikan fail PHP dengan betul Mar 08, 2024 am 11:09 AM

Disebabkan oleh keterbatasan ruang, berikut ialah artikel ringkas: Apache2 ialah perisian pelayan web yang biasa digunakan, dan PHP ialah bahasa skrip bahagian pelayan yang digunakan secara meluas. Dalam proses membina tapak web, kadangkala anda menghadapi masalah bahawa Apache2 tidak dapat menghuraikan fail PHP dengan betul, menyebabkan kod PHP gagal dilaksanakan. Masalah ini biasanya disebabkan oleh Apache2 tidak mengkonfigurasi modul PHP dengan betul, atau modul PHP tidak serasi dengan versi Apache2. Secara umumnya terdapat dua cara untuk menyelesaikan masalah ini, satu

See all articles