日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【mybatis源码系列1】 二级缓存

發(fā)布時間:2024/10/5 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【mybatis源码系列1】 二级缓存 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

面試mybatis必問二級緩存,都知道有二級緩存,那mybatis是怎么實現(xiàn)的,本系列文章以mybatis-3.5.6版本為例。啰里啰唆寫了,想直接看緩存的跳到緩存部分。

一、mybatis流程

寫mybatis代碼的時候,逃不過這幾步

// 1.獲取配置文件 InputStream in =Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml"); //2.開啟SqlSession工廠? ? ? ? SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in); //3.開啟SqlSession? ? ? ? SqlSession sqlSession = build.openSession(); //4.執(zhí)行sql并返回? // 4.1 直接用 ? sqlSession.selectOne("com.faith.mybatiscode.mapper.ProductConfigMapper.selectOne", 1); // 4.2 用mapper代理ProductConfigMapper mapper = sqlSession.getMapper(ProductConfigMapper.class);List<ProductConfigVo> productConfigVos = mapper.selectProductConfigs(Arrays.asList(1));

各種寫法大同小異,區(qū)別主要是兩點,第3步的時候是用單例sqlSession還是多實例的,第4步直接用sqlSession進行操作還是用mapper代理的方式。

1.結(jié)構(gòu)

還記得我們寫jdbc代碼的時候怎么寫的嗎?大概這么幾步

String url = "jdbc:mysql://127.0.0.1:3306/Supermarket?characterEncoding=utf-8";String username = "root";String password = "123";try{// 1.加載驅(qū)動程序Class.forName("com.mysql.jdbc.Driver");// 2.獲得數(shù)據(jù)庫鏈接Connection conn = DriverManager.getConnection(url, username, password);// 3.通過數(shù)據(jù)庫的連接操作數(shù)據(jù)庫,實現(xiàn)增刪改查(使用Statement類)String name="張三";String sql="select * from product_config where id_no=?";PreparedStatement statement = conn.prepareStatement(sql);statement.setString(1, name);// 4.處理數(shù)據(jù)庫的返回結(jié)果(使用ResultSet類)ResultSet rs = statement.executeQuery();while (rs.next()) {System.out.println(rs.getString("UserName") + " " + rs.getString("Password"));}// 5.關(guān)閉資源rs.close();statement.close();conn.close(); }catch(SQLException | ClassNotFoundException se){System.out.println("數(shù)據(jù)庫連接失敗!");se.printStackTrace() ;}

那mybatis是怎么幫我們做這些步驟的呢?先看這幾個對象:

1.SqlSessionFactory

public interface SqlSessionFactory {SqlSession openSession();SqlSession openSession(boolean autoCommit);SqlSession openSession(Connection connection);SqlSession openSession(TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType);SqlSession openSession(ExecutorType execType, boolean autoCommit);SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType, Connection connection);Configuration getConfiguration();}

看SqlSessionFactory接口,做了兩件事,獲取SqlSession和Configuration,看他的默認實現(xiàn)類DefaultSqlSessionFactory持有一個Configuration對象,看這個Configuration里有什么

?要了命了,這么多變量,冷靜下來看下,基本上包括我們在Mybatis-config.xml里配置的所有信息,其他的不認識,碰到再說。看 new SqlSessionFactoryBuilder().build(in) 做了什么,主要是

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}

所以就是解析配置文件為Configuration對象。

openSession

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

只是返回一個DefaultSession對象,注意這里openSession一次就new一個SqlSession,緩存分級就從這里區(qū)別開來了。從而我們知道每個SqlSession持有相同的Configuration對象,不同的Executor對象,即

?

sqlSession.selectOne @Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

從代碼我們可知,SqlSession是用到了Configuration對象組裝好MappedStatement,然后用executor執(zhí)行得到結(jié)果,對比jdbc,似乎有這種感覺。

1.configuration在解析xml的時候就組裝好帶占位符的sql語句了。在這里取出來給executor用。

2.executor管理了參數(shù)的拼裝,即PreparedStatement的實例化和參數(shù)設置,獲取連接,執(zhí)行,返回ResultSet和解析ResultSet到封裝的對象。

如果設這樣的話,緩存一定在executor里了。

緩存

1.在mybatis-config.xml里配置<cache/>標簽,表示開啟二級緩存,一級緩存是默認開啟的。看CachingExecutor里的代碼邏輯。

2.二級

@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

先從緩存里取,取不到再執(zhí)行查詢,這一級的cache是MappedStatement里的,這一步還沒到事務呢,所以是跨事務的。實際上代碼里的tcm就是一個TransactionalCacheManager,

真正的數(shù)據(jù)緩存是這個map的value,TransactionalCache,

主要關(guān)注delegate和entriesToAddOnCommit,delegate實際上和一級緩存一樣,是一個

?無論一級緩存還是二級緩存,最終都是存進這個cache的Map對象里。

而entriesToAddOnCommit這個map,看名字就知道是一組entries,調(diào)用SqlSession.commit()了之后才會刷到PerpetualCache的cache里,這里頭才是真正的緩存數(shù)據(jù)。其實SqlSession.close()也會刷。

3.一級

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}

?這個localCache就是上邊提到的 PerpetualCache,所以只要是查詢都會緩存。

問題

1.一級緩存二級緩存都存到一個Map里,而且key一樣,所有如果沒有配置外部的二級緩存,內(nèi)存里只存了一份緩存吧?

2.網(wǎng)上說二級緩存不要輕易開啟,容易導致內(nèi)存撐爆,這是什么道理?就算開啟了二級緩存,內(nèi)存里只有一份緩存數(shù)據(jù),怎么會因為開啟二級緩存而導致內(nèi)存占用變大呢?除非是一直SqlSession不commit(),entriesToAddOnCommit這個map把內(nèi)存撐爆,或者是一次SqlSession查出來的數(shù)據(jù)太大,在commit之前就爆了(如果這中情況的話一級緩存也爆)。

3.就算配置了外部二級緩存,要想使用上二級緩存,第一次必須查數(shù)據(jù)庫,意味著一級緩存里也被存進去了。所以一級緩存該占用的內(nèi)存還是占用啊,那二級緩存的意義在哪里?難道是為了多應用的架構(gòu),讓別的應用直接去外部的二級緩存里取,而不用查數(shù)據(jù)庫?

?

?

總結(jié)

以上是生活随笔為你收集整理的【mybatis源码系列1】 二级缓存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。