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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

java的工作原理你知道吗_每天用Mybatis,但是Mybatis的工作原理你真的知道吗?

發布時間:2023/11/27 生活经验 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java的工作原理你知道吗_每天用Mybatis,但是Mybatis的工作原理你真的知道吗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

近來想寫一個mybatis的分頁插件,但是在寫插件之前肯定要了解一下mybatis具體的工作原理吧,于是邊參考別人的博客,邊看源碼就開干了。

核心部件:SqlSession

Executor

StatementHandler

ParameterHandler

ResultSetHandler

TypeHandler

MappedStatement

Configuration

在分析工作原理之前,首先看一下我的mybatis全局配置文件<?xml ?version="1.0"?encoding="UTF-8"??>

configuration

PUBLIC?"-//mybatis.org//DTD?Config?3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8"?/>

第一步:創建一個sqlSessionFactory

在了解如何創建sqlSessionFactory之前,先看一下mybatis是如何加載全局配置文件,解析xml文件生成Configuration的public?Configuration?parse()?{

if?(parsed)?{

throw?new?BuilderException("Each?XMLConfigBuilder?can?only?be?used?once.");

}

parsed?=?true;

parseConfiguration(parser.evalNode("/configuration"));

return?configuration;

}

private?void?parseConfiguration(XNode?root)?{

try?{

propertiesElement(root.evalNode("properties"));?//issue?#117?read?properties?first

typeAliasesElement(root.evalNode("typeAliases"));

pluginElement(root.evalNode("plugins"));

objectFactoryElement(root.evalNode("objectFactory"));

objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

settingsElement(root.evalNode("settings"));

environmentsElement(root.evalNode("environments"));?//?read?it?after?objectFactory?and?objectWrapperFactory?issue?#631

databaseIdProviderElement(root.evalNode("databaseIdProvider"));

typeHandlerElement(root.evalNode("typeHandlers"));

mapperElement(root.evalNode("mappers"));

}?catch?(Exception?e)?{

throw?new?BuilderException("Error?parsing?SQL?Mapper?Configuration.?Cause:?"?+?e,?e);

}

}

在上面的第二段代碼中有一句mapperElement(root.evalNode("mappers"));

剛好我們的全局配置文件中有一個mapper的配置,由此可見,mapperElemet()方法是解析mapper映射文件的,具體代碼如下private?void?mapperElement(XNode?parent)?throws?Exception?{

if?(parent?!=?null)?{

for?(XNode?child?:?parent.getChildren())?{

if?("package".equals(child.getName()))?{

String?mapperPackage?=?child.getStringAttribute("name");

configuration.addMappers(mapperPackage);

}?else?{

String?resource?=?child.getStringAttribute("resource");

String?url?=?child.getStringAttribute("url");

String?mapperClass?=?child.getStringAttribute("class");

if?(resource?!=?null?&&?url?==?null?&&?mapperClass?==?null)?{//進入該判斷

ErrorContext.instance().resource(resource);

InputStream?inputStream?=?Resources.getResourceAsStream(resource);

XMLMapperBuilder?mapperParser?=?new?XMLMapperBuilder(inputStream,?configuration,?resource,?configuration.getSqlFragments());

mapperParser.parse();

}?else?if?(resource?==?null?&&?url?!=?null?&&?mapperClass?==?null)?{

ErrorContext.instance().resource(url);

InputStream?inputStream?=?Resources.getUrlAsStream(url);

XMLMapperBuilder?mapperParser?=?new?XMLMapperBuilder(inputStream,?configuration,?url,?configuration.getSqlFragments());

mapperParser.parse();

}?else?if?(resource?==?null?&&?url?==?null?&&?mapperClass?!=?null)?{

Class>?mapperInterface?=?Resources.classForName(mapperClass);

configuration.addMapper(mapperInterface);

}?else?{

throw?new?BuilderException("A?mapper?element?may?only?specify?a?url,?resource?or?class,?but?not?more?than?one.");

}

}

}

}

}

根據以上代碼可以分析,在寫mapper映射文件的地址時不僅可以寫成resource,還可以寫成url和mapperClass的形式,由于我們用的是resource,所以直接進入第一個判斷,最后解析mapper映射文件的方法是private?void?configurationElement(XNode?context)?{

try?{

String?namespace?=?context.getStringAttribute("namespace");

if?(namespace.equals(""))?{

throw?new?BuilderException("Mapper's?namespace?cannot?be?empty");

}

builderAssistant.setCurrentNamespace(namespace);

cacheRefElement(context.evalNode("cache-ref"));

cacheElement(context.evalNode("cache"));

parameterMapElement(context.evalNodes("/mapper/parameterMap"));

resultMapElements(context.evalNodes("/mapper/resultMap"));

sqlElement(context.evalNodes("/mapper/sql"));

buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

}?catch?(Exception?e)?{

throw?new?BuilderException("Error?parsing?Mapper?XML.?Cause:?"?+?e,?e);

}

}

其中具體解析每一個sql語句節點的是buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

進入這個方法一層層深究,最后到這里可以知道MappedStatement是由builderAssistant(即MapperBuildAssistant)創建的。public?void?parseStatementNode()?{

...

builderAssistant.addMappedStatement(id,?sqlSource,?statementType,?sqlCommandType,

fetchSize,?timeout,?parameterMap,?parameterTypeClass,?resultMap,?resultTypeClass,

resultSetTypeEnum,?flushCache,?useCache,?resultOrdered,

keyGenerator,?keyProperty,?keyColumn,?databaseId,?langDriver,?resultSets);

}

最后進入方法addMappedStatement(),mappedStatement最后以id為鍵保存在了Configuration中的一個map變量mappedStatements中。public?MappedStatement?addMappedStatement(

String?id,

SqlSource?sqlSource,

StatementType?statementType,

SqlCommandType?sqlCommandType,

Integer?fetchSize,

Integer?timeout,

String?parameterMap,

Class>?parameterType,

String?resultMap,

Class>?resultType,

ResultSetType?resultSetType,

boolean?flushCache,

boolean?useCache,

boolean?resultOrdered,

KeyGenerator?keyGenerator,

String?keyProperty,

String?keyColumn,

String?databaseId,

LanguageDriver?lang,

String?resultSets)?{

if?(unresolvedCacheRef)?throw?new?IncompleteElementException("Cache-ref?not?yet?resolved");

id?=?applyCurrentNamespace(id,?false);

boolean?isSelect?=?sqlCommandType?==?SqlCommandType.SELECT;

MappedStatement.Builder?statementBuilder?=?new?MappedStatement.Builder(configuration,?id,?sqlSource,?sqlCommandType);

statementBuilder.resource(resource);

statementBuilder.fetchSize(fetchSize);

statementBuilder.statementType(statementType);

statementBuilder.keyGenerator(keyGenerator);

statementBuilder.keyProperty(keyProperty);

statementBuilder.keyColumn(keyColumn);

statementBuilder.databaseId(databaseId);

statementBuilder.lang(lang);

statementBuilder.resultOrdered(resultOrdered);

statementBuilder.resulSets(resultSets);

setStatementTimeout(timeout,?statementBuilder);

setStatementParameterMap(parameterMap,?parameterType,?statementBuilder);

setStatementResultMap(resultMap,?resultType,?resultSetType,?statementBuilder);

setStatementCache(isSelect,?flushCache,?useCache,?currentCache,?statementBuilder);

MappedStatement?statement?=?statementBuilder.build();

configuration.addMappedStatement(statement);

return?statement;

}

最后回到我們的創建sqlSessionFactory上,之前的一切都是為了生成一個sqlSessionFactory服務的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為參數build()方法生成DefautSqlSessionFactory。

第二步:創建sqlSessionpublic?SqlSession?openSession()?{

return?openSessionFromDataSource(configuration.getDefaultExecutorType(),?null,?false);

}

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();

}

}

//返回一個SqlSession,默認使用DefaultSqlSession

public?DefaultSqlSession(Configuration?configuration,?Executor?executor,?boolean?autoCommit)?{

this.configuration?=?configuration;

this.executor?=?executor;

this.dirty?=?false;

this.autoCommit?=?autoCommit;

}

executor在這一步得到創建,具體的使用在下一步。

第三步:執行具體的sql請求

在我的代碼里執行的是User?user?=?sqlSession.selectOne("test.findUserById",?1);

具體到里面的方法就是public??List?selectList(String?statement,?Object?parameter,?RowBounds?rowBounds)?{

try?{

//1.根據Statement?Id,在mybatis?配置對象Configuration中查找和配置文件相對應的MappedStatement

MappedStatement?ms?=?configuration.getMappedStatement(statement);

//2.?將查詢任務委托給MyBatis?的執行器?Executor

List?result?=?executor.query(ms,?wrapCollection(parameter),?rowBounds,?Executor.NO_RESULT_HANDLER);

return?result;

}?catch?(Exception?e)?{

throw?ExceptionFactory.wrapException("Error?querying?database.??Cause:?"?+?e,?e);

}?finally?{

ErrorContext.instance().reset();

}

}

在這里通過statementId拿到了我們在第一步存在map里面的MappedStatement。在這里引用參考博客的一句話:SqlSession根據Statement ID, 在mybatis配置對象Configuration中獲取到對應的MappedStatement對象,然后調用mybatis執行器來執行具體的操作。

再繼續看query()和queryFromDatabase()這兩個方法@SuppressWarnings("unchecked")

public??List?query(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler,?CacheKey?key,?BoundSql?boundSql)?throws?SQLException?{

ErrorContext.instance().resource(ms.getResource()).activity("executing?a?query").object(ms.getId());

if?(closed)?throw?new?ExecutorException("Executor?was?closed.");

if?(queryStack?==?0?&&?ms.isFlushCacheRequired())?{

clearLocalCache();

}

List?list;

try?{

queryStack++;

list?=?resultHandler?==?null???(List)?localCache.getObject(key)?:?null;

if?(list?!=?null)?{

handleLocallyCachedOutputParameters(ms,?key,?parameter,?boundSql);

}?else?{

list?=?queryFromDatabase(ms,?parameter,?rowBounds,?resultHandler,?key,?boundSql);

}

}?finally?{

queryStack--;

}

if?(queryStack?==?0)?{

for?(DeferredLoad?deferredLoad?:?deferredLoads)?{

deferredLoad.load();

}

deferredLoads.clear();?//?issue?#601

if?(configuration.getLocalCacheScope()?==?LocalCacheScope.STATEMENT)?{

clearLocalCache();?//?issue?#482

}

}

return?list;

}

private??List?queryFromDatabase(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler,?CacheKey?key,?BoundSql?boundSql)?throws?SQLException?{

List?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;

}

在這兩個方法里面會為當前的查詢創建一個緩存key,如果緩存中沒有值,直接從數據庫中讀取,執行查詢后將得到的list結果放入緩存之中。

緊接著看doQuery()在SimpleExecutor類中重寫的方法public??List?doQuery(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler,?BoundSql?boundSql)?throws?SQLException?{

Statement?stmt?=?null;

try?{

Configuration?configuration?=?ms.getConfiguration();

StatementHandler?handler?=?configuration.newStatementHandler(wrapper,?ms,?parameter,?rowBounds,?resultHandler,?boundSql);

stmt?=?prepareStatement(handler,?ms.getStatementLog());

return?handler.query(stmt,?resultHandler);

}?finally?{

closeStatement(stmt);

}

}

Statement連接對象就是在這里創建的,因此Executor的作用之一就是創建Statement了,創建完后又把Statement丟給StatementHandler返回List查詢結果。

接下來再看一下這里的兩個方法prepareStatement()和query()的具體實現private?Statement?prepareStatement(StatementHandler?handler,?Log?statementLog)?throws?SQLException?{

Statement?stmt;

Connection?connection?=?getConnection(statementLog);

stmt?=?handler.prepare(connection);

handler.parameterize(stmt);

return?stmt;

}

public??List?query(Statement?statement,?ResultHandler?resultHandler)?throws?SQLException?{

PreparedStatement?ps?=?(PreparedStatement)?statement;

ps.execute();

return?resultSetHandler.?handleResultSets(ps);

}

prepareStatement()是創建Statement的具體實現方法,調用parameterize()對創建的Statement對象設置參數,即為我們設為占位符的地方賦上指定的參數,parameterize()方法再深入進去就是調用ParameterHandler的setParameters()方法具體賦值了。歡迎大家關注我的公眾號【程序員追風】,文章都會在里面更新,整理的資料也會放在里面

這里的query()是調用了ResultSetHandler的handleResultSets(Statement) 方法。作用就是把ResultSet結果集對象轉換成List類型的集合。

總結以上步驟就是:根據具體傳入的參數,動態地生成需要執行的SQL語句,用BoundSql對象表示

為當前的查詢創建一個緩存Key

緩存中沒有值,直接從數據庫中讀取數據

執行查詢,返回List 結果,然后 將查詢的結果放入緩存之中

根據既有的參數,創建StatementHandler對象來執行查詢操作

將創建Statement傳遞給StatementHandler對象,調用parameterize()方法賦值

調用StatementHandler.query()方法,返回List結果集

總結

以上三個步驟所有流程大體可以用一張圖來總結

最后

歡迎大家一起交流,喜歡文章記得點個贊喲,感謝支持!

總結

以上是生活随笔為你收集整理的java的工作原理你知道吗_每天用Mybatis,但是Mybatis的工作原理你真的知道吗?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。