日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Mybatis原理及源碼分析

本文轉(zhuǎn)載自微信公眾號「三不猴子」,作者sanbuhouzi。轉(zhuǎn)載本文請聯(lián)系三不猴子公眾號。

成都創(chuàng)新互聯(lián)公司是一家集成都網(wǎng)站設(shè)計、成都做網(wǎng)站、網(wǎng)站頁面設(shè)計、網(wǎng)站優(yōu)化SEO優(yōu)化為一體的專業(yè)的建站公司,已為成都等多地近百家企業(yè)提供網(wǎng)站建設(shè)服務(wù)。追求良好的瀏覽體驗,以探求精品塑造與理念升華,設(shè)計最適合用戶的網(wǎng)站頁面。 合作只是第一步,服務(wù)才是根本,我們始終堅持講誠信,負責(zé)任的原則,為您進行細心、貼心、認真的服務(wù),與眾多客戶在蓬勃發(fā)展的市場環(huán)境中,互促共生。

作為Java程序員Mybatis應(yīng)該是一個必會框架了,其源碼體量只有Spring 的1/5,也是Hibernate的1/5 ,相比于其他流行框架Mybatis源碼無疑是學(xué)習(xí)成本最低的,當(dāng)做年輕人看的第一個框架源碼,無疑是非常好的。

整體架構(gòu)

對于一個陌生的事物切勿一頭扎進細節(jié)里,我們先要觀其大概看看架構(gòu)脈絡(luò),MyBatis 分為三層架構(gòu),分別是基礎(chǔ)支撐層、核心處理層和接口層。

Mybatis 整體架構(gòu)

基礎(chǔ)支撐層

基礎(chǔ)支撐層是這個Mybatis框架的基建,為整個Mybatis框架提供非?;A(chǔ)的功能。(篇幅有限下面我們只對部分模塊做簡單的分析)

1.類型轉(zhuǎn)換模塊,我們在Mybatis中使用< typeAliase >標(biāo)簽定義一個別名就是使用類型轉(zhuǎn)換模塊實現(xiàn)的。類型轉(zhuǎn)換模塊最重要的功能還是實現(xiàn)了Mybatis中JDBC類型和Java類型之間的轉(zhuǎn)換。主要體現(xiàn)在:

  • 在SQL模板綁定用戶參入實參的場景中,將Java類型轉(zhuǎn)換成JDBC類型
  • 在結(jié)果集中,將JDBC類型轉(zhuǎn)換成Java類型。

Mybatis類型轉(zhuǎn)換

2.日志模塊,產(chǎn)生日志,定位異常。

3.反射工具,對原生的Java反射作了一些封裝。

4.Binding模塊,我們在執(zhí)行Mybatis中的方法時都是通過SqlSession來獲Mapper接口中的代理,這個代理將Mapper.xml文件中SQL進行關(guān)聯(lián)就是通過Binding模塊來實現(xiàn)的。值得注意點是這個過程是發(fā)生在編譯期??梢詫㈠e誤提前到編譯期。

5.數(shù)據(jù)源模塊,數(shù)據(jù)源對于一個ORM來說算是非常核心的組件之一了。Mybatis默認的數(shù)據(jù)源是非常出色的,Mybatis同時也支持集成第三方的數(shù)據(jù)源。

6.緩存模塊,緩存模塊的好壞直接影響這個ORM的性能。Mybatis提供了兩級緩存,同時也支持第三方緩存中間件集成。

Mybatis緩存

7.解析器模塊,主要是config.xml配置文件解析,和mapper.xml文件解析。

8.事務(wù)管理模塊,事務(wù)模塊對數(shù)據(jù)庫的事務(wù)機制進行控制,對數(shù)據(jù)庫事務(wù)進行了抽象,同時也對spring框架進行了整合。詳細的處理邏輯下文會結(jié)合源碼詳細解析。

核心處理層

核心處理層是我們在學(xué)習(xí)Mybatis原理的時候需要花80%時間的地方。核心處理層是 MyBatis 核心實現(xiàn)所在,其中涉及 MyBatis 的初始化以及執(zhí)行一條 SQL 語句的全流程。

配置解析

MyBatis 的初始化以及執(zhí)行一條 SQL 語句的全流程中也包含了配置解析,我們在現(xiàn)實開發(fā)中一般都是使用spring boot starter的自動配置。我們一項目啟動為起點一層一層剝開Mybatis的流程。先打開org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration首先明確一點就是MybatisAutoConfiguration的目的就是要得到一個SqlSessionFactory。

 
 
 
 
  1. @Bean
  2. @ConditionalOnMissingBean
  3. public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  4.   SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
  5.   factory.setDataSource(dataSource);
  6.   factory.setVfs(SpringBootVFS.class);
  7.   if (StringUtils.hasText(this.properties.getConfigLocation())) {
  8.     factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
  9.   }
  10.   Configuration configuration = this.properties.getConfiguration();
  11.   if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
  12.     configuration = new Configuration();
  13.   }
  14.   if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
  15.     for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
  16.       customizer.customize(configuration);
  17.     }
  18.   }
  19.   factory.setConfiguration(configuration);
  20.   if (this.properties.getConfigurationProperties() != null) {
  21.     factory.setConfigurationProperties(this.properties.getConfigurationProperties());
  22.   }
  23.   if (!ObjectUtils.isEmpty(this.interceptors)) {
  24.     factory.setPlugins(this.interceptors);
  25.   }
  26.   if (this.databaseIdProvider != null) {
  27.     factory.setDatabaseIdProvider(this.databaseIdProvider);
  28.   }
  29.   if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
  30.     factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
  31.   }
  32.   if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
  33.     factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
  34.   }
  35.   if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
  36.     factory.setMapperLocations(this.properties.resolveMapperLocations());
  37.   }
  38.   return factory.getObject();
  39. }

這里是通過MybatisProperties里面的配置并放入到SqlSessionFactoryBean中,再由SqlSessionFactoryBean得到SqlSessionFactory??吹阶詈笠恍衦eturn factory.getObject();我們進去看看這個factory.getObject()的邏輯是如何得到一個SqlSessionFactory。

 
 
 
 
  1. @Override
  2. public SqlSessionFactory getObject() throws Exception {
  3.   if (this.sqlSessionFactory == null) {
  4.     afterPropertiesSet();
  5.   }
  6.   return this.sqlSessionFactory;
  7. }

這一步?jīng)]什么好說的,看看afterPropertiesSet()方法

 
 
 
 
  1. @Override
  2. public void afterPropertiesSet() throws Exception {
  3.   notNull(dataSource, "Property 'dataSource' is required");
  4.   notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  5.   state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
  6.             "Property 'configuration' and 'configLocation' can not specified with together");
  7.   this.sqlSessionFactory = buildSqlSessionFactory();
  8. }

重點來了,看看這個buildSqlSessionFactory()方法這里的核心目的就是將configurationProperties解析到Configuration對象中。代碼太長了我就不貼出來了, buildSqlSessionFactory()的邏輯我畫了個圖,有興趣的小伙伴自行看一下。

Mybatis配置解析1

我們不要陷入細節(jié)之中,我們看看中點看看buildSqlSessionFactory() 方法的最后一行this.sqlSessionFactoryBuilder.build(configuration)點進去

 
 
 
 
  1. public SqlSessionFactory build(Configuration config) {
  2.     return new DefaultSqlSessionFactory(config);
  3.   }

通過buildSqlSessionFactory()解析得到的Configuration對象創(chuàng)建一個DefaultSqlSessionFactory(config),到此我們就得到了SqlSessionFactory同時被配置成一個bean了。

我們最終操作都是SqlSession,什么時候會通過SqlSessionFactory得到一個SqlSession呢?

要解決這個問題我們回到最開始的MybatisAutoConfiguration的sqlSessionTemplate(SqlSessionFactory sqlSessionFactory)這個方法,點開SqlSessionTemplate發(fā)現(xiàn)它是一個實現(xiàn)了SqlSession到這里我們猜測就是在這里SqlSessionFactory會構(gòu)建一個SqlSession出來。我們進入new SqlSessionTemplate(sqlSessionFactory)看看源碼。

 
 
 
 
  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  2.    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
  3.  }

再往下看,我們就看到了

 
 
 
 
  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
  2.     PersistenceExceptionTranslator exceptionTranslator) {
  3.   notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  4.   notNull(executorType, "Property 'executorType' is required");
  5.   this.sqlSessionFactory = sqlSessionFactory;
  6.   this.executorType = executorType;
  7.   this.exceptionTranslator = exceptionTranslator;
  8.   this.sqlSessionProxy = (SqlSession) newProxyInstance(
  9.       SqlSessionFactory.class.getClassLoader(),
  10.       new Class[] { SqlSession.class },
  11.       new SqlSessionInterceptor());
  12. }

這里通過動態(tài)代理創(chuàng)建了一個SqlSession。

參數(shù)映射、SQL解析

我們先看一下MapperFactoryBean類,這個類實現(xiàn)了FactoryBean在bean初始化的時候會調(diào)用getObject()方法我們看看這個類下重寫的getObject()方法里的內(nèi)容。

 
 
 
 
  1. @Override
  2.  public T getObject() throws Exception {
  3.    return getSqlSession().getMapper(this.mapperInterface);
  4.  }

這里調(diào)用了sqlSession的getMapper()方法。一層一層點進去里面返回的是一個代理對象。最后的執(zhí)行是由MapperProxy執(zhí)行。

 
 
 
 
  1. public  T getMapper(Class type, SqlSession sqlSession) {
  2.     final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
  3.     if (mapperProxyFactory == null) {
  4.       throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  5.     }
  6.     try {
  7.       return mapperProxyFactory.newInstance(sqlSession);
  8.     } catch (Exception e) {
  9.       throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  10.     }
  11.   }

接下來的流程我還是畫個流程圖,防止小伙伴們走丟。我這里的內(nèi)容可能未必完全和小標(biāo)題一樣,我主要按照sql執(zhí)行的流程講解的。

Mybatis參數(shù)綁定

先看一下MapperProxy中的invoke方法,cachedMapperMethod()方法將MapperMethod緩存起來了。

 
 
 
 
  1. @Override
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3.   try {
  4.     if (Object.class.equals(method.getDeclaringClass())) {
  5.       return method.invoke(this, args);
  6.     } else if (isDefaultMethod(method)) {
  7.       return invokeDefaultMethod(proxy, method, args);
  8.     }
  9.   } catch (Throwable t) {
  10.     throw ExceptionUtil.unwrapThrowable(t);
  11.   }
  12.   final MapperMethod mapperMethod = cachedMapperMethod(method);
  13.   return mapperMethod.execute(sqlSession, args);
  14. }
  15.  private MapperMethod cachedMapperMethod(Method method) {
  16.     MapperMethod mapperMethod = methodCache.get(method);
  17.     if (mapperMethod == null) {
  18.       mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
  19.       methodCache.put(method, mapperMethod);
  20.     }
  21.     return mapperMethod;
  22.   }

我們在往下看mapperMethod.execute(sqlSession, args)方法。

 
 
 
 
  1. public Object execute(SqlSession sqlSession, Object[] args) {
  2.   Object result;
  3.   switch (command.getType()) {
  4.     case INSERT: {
  5.     Object param = method.convertArgsToSqlCommandParam(args);
  6.       result = rowCountResult(sqlSession.insert(command.getName(), param));
  7.       break;
  8.     }
  9.     case UPDATE: {
  10.       Object param = method.convertArgsToSqlCommandParam(args);
  11.       result = rowCountResult(sqlSession.update(command.getName(), param));
  12.       break;
  13.     }
  14.     case DELETE: {
  15.       Object param = method.convertArgsToSqlCommandParam(args);
  16.       result = rowCountResult(sqlSession.delete(command.getName(), param));
  17.       break;
  18.     }
  19.     case SELECT:
  20.       if (method.returnsVoid() && method.hasResultHandler()) {
  21.         executeWithResultHandler(sqlSession, args);
  22.         result = null;
  23.       } else if (method.returnsMany()) {
  24.         result = executeForMany(sqlSession, args);
  25.       } else if (method.returnsMap()) {
  26.         result = executeForMap(sqlSession, args);
  27.       } else if (method.returnsCursor()) {
  28.         result = executeForCursor(sqlSession, args);
  29.       } else {
  30.         Object param = method.convertArgsToSqlCommandParam(args);
  31.         result = sqlSession.selectOne(command.getName(), param);
  32.       }
  33.       break;
  34.     case FLUSH:
  35.       result = sqlSession.flushStatements();
  36.       break;
  37.     default:
  38.       throw new BindingException("Unknown execution method for: " + command.getName());
  39.   }
  40.   if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  41.     throw new BindingException("Mapper method '" + command.getName() 
  42.         + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  43.   }
  44.   return result;
  45. }

method.convertArgsToSqlCommandParam(args)這里就是處理參數(shù)轉(zhuǎn)換的邏輯。還有很多細節(jié)由于篇幅有限以及時間倉促我們不做過多的贅述,感興趣的小伙伴可以結(jié)合上面的圖自己看看。下面我們看SQL的執(zhí)行流程是怎么樣的。整體流程如下圖。

Mybatis執(zhí)行流程

我們就不對每一個執(zhí)行器都分析,我只挑一個SimpleExecutor來具體跟一下源碼。我們還是先看看圖吧,防止自己把自己搞蒙。

以simpleExecutor為例的執(zhí)行流程

 
 
 
 
  1. @Override
  2. public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  3.   Statement stmt = null;
  4.   try {
  5.     Configuration configuration = ms.getConfiguration();
  6.     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  7.     stmt = prepareStatement(handler, ms.getStatementLog());
  8.     return handler.query(stmt, resultHandler);
  9.   } finally {
  10.     closeStatement(stmt);
  11.   }
  12. }

這里獲取了Configuration,創(chuàng)建了一個StatementHandler,預(yù)處理操作,具體執(zhí)行的根據(jù)創(chuàng)建的預(yù)處理方法,最后執(zhí)行query方法

 
 
 
 
  1. @Override
  2. public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
  3.   String sql = boundSql.getSql();
  4.   statement.execute(sql);
  5.   return resultSetHandler.handleResultSets(statement);
  6. }

到此我們整理了整個Mybatis的執(zhí)行流程,分析了其中的源碼,由于篇幅有限很多地方都沒有細致的分析,但是也貼出了圖,希望能幫助到你。


網(wǎng)站標(biāo)題:Mybatis原理及源碼分析
瀏覽地址:http://www.5511xx.com/article/ccichie.html