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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis主线流程源码解析

發(fā)布時(shí)間:2025/3/15 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis主线流程源码解析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  ?Mybatis的基礎(chǔ)使用以及與Spring的相關(guān)集成在官方文檔都寫的非常詳細(xì),但無(wú)論我們采用xml還是注解方式在使用的過(guò)程中經(jīng)常會(huì)出現(xiàn)各種奇怪的問(wèn)題,需要花費(fèi)大量的時(shí)間解決。

抽空了解一下Mybatis的相關(guān)源碼還是很有必要。

  先來(lái)看一個(gè)簡(jiǎn)單的Demo:

@Test public void test() throws IOException {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();MethodInfo info = (MethodInfo) session.selectOne("com.ycdhz.mybatis.dao.MethodInfoMapper.selectById", 1);System.out.println(info.toString()); }

  這個(gè)是官網(wǎng)中入門的一段代碼,我根據(jù)自己的情況做了一些參數(shù)上的改動(dòng)。這段代碼很容易理解,解析一個(gè)xml文件,通過(guò)SqlSessionFactoryBuilder構(gòu)建一個(gè)SqlSessionFactory實(shí)例。

拿到了SqlSessionFactory我們就可以獲取SqlSession。SqlSession 包含了面向數(shù)據(jù)庫(kù)執(zhí)行 SQL 命令所需的所有方法,所以我們可以通過(guò) SqlSession 實(shí)例來(lái)直接執(zhí)行已映射的 SQL 語(yǔ)句。

代碼很簡(jiǎn)單的展示了Mybatis到底是什么,有什么作用。

?

  Mybatis主線流程:解析Configuration返回SqlSessionFactory;拿到SqlSession對(duì)執(zhí)行器進(jìn)行初始化 SimpleExecutor;操作數(shù)據(jù)庫(kù);

  

?

  我們先來(lái)看一下mybatis-config.xml,在這個(gè)xml中包含了Mybatis的核心設(shè)置,有獲取數(shù)據(jù)庫(kù)連接實(shí)例的數(shù)據(jù)源(DataSource)和決定事務(wù)作用域和控制方式的事務(wù)管理器(TransactionManager)等等。 具體的配置信息可以參考官方文檔。需要注意的是Xml的屬性配置有一定的順序要求,具體的可以查看http://mybatis.org/dtd/mybatis-3-config.dtd。 <!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)> <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mytest"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="mybatis/MethodInfoMapper.xml"/><!--<mapper class="com.ycdhz.mybatis.dao.MethodInfoMapper" />--><!--<package name="com.ycdhz.mybatis.dao" />--></mappers> </configuration>

  test()前兩行主要是通過(guò)流來(lái)讀取配置文件,我們直接從new SqlSessionFactoryBuilder().build(inputStream)這段代碼開(kāi)始:

public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// SqlSessionFactoryBuilde拿到輸入流后,構(gòu)建了一個(gè)XmlConfigBuilder的實(shí)例。通過(guò)parse()進(jìn)行解析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) {//XmlConfigBuilder.parse()解析完后將數(shù)據(jù)傳給DefaultSqlSessionFactoryreturn new DefaultSqlSessionFactory(config);} }

  

public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource("SQL Mapper Configuration");this.parsed = false;}public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;
     //這個(gè)方法主要就是解析xml文件了parseConfiguration(parser.evalNode("/configuration"));return configuration;}private void parseConfiguration(XNode root) {try {//issue #117 read properties firstpropertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));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);}} }

?  mybatis-config.xml 文件中的mapper屬性支持四種配置方式,但是只有package,class這兩種發(fā)式支持通過(guò)注解來(lái)配置和映射原生信息(原因在于configuration.addMappers()這個(gè)方法)。

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.");}}}}}

?  addMappers()調(diào)用了MapperAnnotationBuilder.parse()這樣一段代碼。我們發(fā)現(xiàn)當(dāng)resource不為空的時(shí)候,代碼首先會(huì)調(diào)用loadXmlResource()去Resource文件夾下查找(com/ycdhz/mybatis/dao/MethodInfoMapper.xml),

如果發(fā)現(xiàn)當(dāng)前文件就加載。但實(shí)際這個(gè)時(shí)候type信息來(lái)自注解,MethodInfoMapper.xml在容器中被加載兩次。所以Configruation下的靜態(tài)類StrictMap.put()時(shí)會(huì)拋出一個(gè) Mapped Statements collection already contains value for com.ycdhz.mybatis.dao.MethodInfoMapper.selectById 的異常

public class MapperAnnotationBuilder {public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}private void loadXmlResource() {// Spring may not know the real resource name so we check a flag// to prevent loading again a resource twice// this flag is set at XMLMapperBuilder#bindMapperForNamespaceif (!configuration.isResourceLoaded("namespace:" + type.getName())) {String xmlResource = type.getName().replace('.', '/') + ".xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);} catch (IOException e) {// ignore, resource is not required}if (inputStream != null) {XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());xmlParser.parse();}}} }

  

  這個(gè)時(shí)候我們已經(jīng)完成了xml文件的解析過(guò)程,拿到了DefaultSqlSessionFactory。下面我們?cè)賮?lái)看一下sqlSessionFactory.openSession()的過(guò)程:

public class DefaultSqlSessionFactory implements SqlSessionFactory{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();}} }public class Configuration {protected boolean cacheEnabled = true;protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;protected final InterceptorChain interceptorChain = new InterceptorChain();public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;} }

  

  openSession()方法會(huì)調(diào)用DefaultSqlSessionFactory.openSessionFromDataSource()方法,在這個(gè)方法中會(huì)開(kāi)啟事務(wù)、創(chuàng)建了執(zhí)行器Executor:

  MyBatis的事務(wù)管理分為兩種形式(配置mybatis-config.xml文件的transactionManager屬性):

    1)使用JDBC的事務(wù)管理機(jī)制:即利用java.sql.Connection對(duì)象完成對(duì)事務(wù)的提交(commit())、回滾(rollback())、關(guān)閉(close())等

    2)使用MANAGED的事務(wù)管理機(jī)制:這種機(jī)制MyBatis自身不會(huì)去實(shí)現(xiàn)事務(wù)管理,而是讓程序的容器如(JBOSS,Weblogic)來(lái)實(shí)現(xiàn)對(duì)事務(wù)的管理

    ?

?  執(zhí)行器ExecutorType分為三類(默認(rèn)使用的是ExecutorType.SIMPLE):

    1)ExecutorType.SIMPLE: 這個(gè)執(zhí)行器類型不做特殊的事情。它為每個(gè)語(yǔ)句的執(zhí)行創(chuàng)建一個(gè)新的預(yù)處理語(yǔ)句。

    2)ExecutorType.REUSE: 這個(gè)執(zhí)行器類型會(huì)復(fù)用預(yù)處理語(yǔ)句。

    3)ExecutorType.BATCH: 這個(gè)執(zhí)行器會(huì)批量執(zhí)行所有更新語(yǔ)句,如果 SELECT 在它們中間執(zhí)行還會(huì)標(biāo)定它們是 必須的,來(lái)保證一個(gè)簡(jiǎn)單并易于理解的行為。?

  ? ? ? 因?yàn)镸ybatis的一級(jí)緩存是默認(rèn)開(kāi)啟的,查看newExecutor()不難發(fā)現(xiàn),最后通過(guò)CachingExecutor對(duì)SimpleExecutor進(jìn)行了裝飾(詳細(xì)代碼可以查看https://www.cnblogs.com/jiangyaxiong1990/p/9236764.html)

    

  Mybatis緩存設(shè)計(jì)成兩級(jí)結(jié)構(gòu),分為一級(jí)緩存、二級(jí)緩存:(參考 https://blog.csdn.net/luanlouis/article/details/41280959 )

    1)一級(jí)緩存是Session會(huì)話級(jí)別的緩存,位于表示一次數(shù)據(jù)庫(kù)會(huì)話的SqlSession對(duì)象之中,又被稱之為本地緩存。默認(rèn)情況下自動(dòng)開(kāi)啟,用戶沒(méi)有定制它的權(quán)利(不過(guò)這也不是絕對(duì)的,可以通過(guò)開(kāi)發(fā)插件對(duì)它進(jìn)行修改);

      實(shí)際上MyBatis的一級(jí)緩存是使用PerpetualCache來(lái)維護(hù)的,PerpetualCache實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單,其內(nèi)部就是通過(guò)一個(gè)簡(jiǎn)單的HashMap<k,v>?來(lái)實(shí)現(xiàn)的,沒(méi)有其他的任何限制。

    2)二級(jí)緩存是Application應(yīng)用級(jí)別的緩存,它的是生命周期很長(zhǎng),跟Application的聲明周期一樣,也就是說(shuō)它的作用范圍是整個(gè)Application應(yīng)用。

????     ?MyBatis的二級(jí)緩存設(shè)計(jì)得比較靈活,你可以使用MyBatis自己定義的二級(jí)緩存實(shí)現(xiàn);你也可以通過(guò)實(shí)現(xiàn)org.apache.ibatis.cache.Cache接口自定義緩存;也可以使用第三方內(nèi)存緩存庫(kù),如Redis等

  

?

?

    

?

?

  

轉(zhuǎn)載于:https://www.cnblogs.com/jiangyaxiong1990/p/10306981.html

總結(jié)

以上是生活随笔為你收集整理的Mybatis主线流程源码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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