mybatis - 執行 getById

来源:https://www.cnblogs.com/elvinle/archive/2020/02/12/12301109.html
-Advertisement-
Play Games

1. getById 的執行 前面一篇 提到過, Mapper.java 創建的時候, 會通過 jdk 代理的方式來創建, 且代理處理類為: MapperProxy . 所以當執行 UserMapper 的 getById 方法的時候, 就會去 MapperProxy 中執行 invoke 方法. ...


1. getById 的執行

前面一篇 提到過, Mapper.java 創建的時候, 會通過 jdk 代理的方式來創建, 且代理處理類為: MapperProxy .

所以當執行 UserMapper 的 getById 方法的時候, 就會去 MapperProxy 中執行 invoke 方法.

  //MapperProxy.java 
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

 

cachedMapperMethod 方法是做了一層緩存處理. 先從緩存中獲取, 如果獲取不到, 再創建 MapperMethod 對象.
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

 

接下來看一下 MapperMethod.execute() 方法. 這個方法中, 會看到我們期待的東西.

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
      //對參數進行處理 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }

getById 在這裡會調用 SqlSessionTemplate 的 selectOne 方法. 

  //SqlSessionTemplate.java  
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy.<T> selectOne(statement, parameter);
  }

前面解析過, sqlSessionProxy.selectOne 的時候, 會去  SqlSessionInterceptor 中執行 invoke() 方法.

  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

 這裡有兩個方法需要重點關註, 一個是  getSqlSession() , 另一個是 invoke() 方法.

 1.1 getSqlSession()

  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Creating a new SqlSession");
    }

    session = sessionFactory.openSession(executorType);

    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

這裡的 SqlSessionFactory 就是配置類中創建的 預設的實現類: DefaultSqlSessionFactory

  //DefaultSqlSessionFactory.java
  @Override
  public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, 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();
    }
  }

這裡先不管別的代碼, 直接看返回值, 創建了一個 DefaultSqlSession 的實例返回.

 

1.2 method.invoke()

回到  SqlSessionInterceptor 中來, 繼續看下麵的執行代碼:

Object result = method.invoke(sqlSession, args);

sqlSession 就是上面創建的 DefaultSqlSesion 實例. 我們知道 Method.invoke(obj, args) 是反射調用方法的一種方式.

此處的Method是 getById 的方法反射類型. 所以, 會調用 obj 的 getById() 方法, 即 DefaultSqlSession 的 getById() 方法.

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <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();
    }
  }

此例中, statement = "com.study.demo.mybatis.mapper.UserMapper.getById"

這裡是通過statement 拿 MappedStatement, 而 MappedStatement 中, 就有 mapper.xml 中對應的 sql 語句.

後續過程中, 就可以拿著 sql 去資料庫執行了.

到這裡, 主體流程其實已經走通了. 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • python數據類型分類 1).數值型:整數型(int)、浮點型(float)、布爾型(bool 取值:True、False) 2).容器類型 : 字元串型(str)、列表(list)、元祖(tuple)、字典(dict)、集合(set) 3).自定義類型:Person、Animal、Dog、Cat ...
  • 《 Java 編程思想》CH03 操作符 在最底層,Java中的數據是通過操作符來操作的。 操作符 ,` `, ,`=`與其他語言類似 幾乎所有的操作符只能操作“基本類似”,而 ,`== !=`是例外 String 類支持 和`+=`操作符,表示拼接操作,在進行拼接的操作會嘗試將非 String 的 ...
  • Spring 是一個開源框架,是為瞭解決企業應用程式開發複雜性而創建的。框架的主要優勢之一就是其分層架構,分層架構允許您選擇使用哪一個組件,同時為 J2EE 應用程式開發提供集成的框架 Spring框架 IOC Spring的核心模塊實現了IOC的功能,它將類與類之間的依賴從代碼中脫離出來,用配置的 ...
  • 今天主要完成了信息領域熱詞分析系統的其他功能,如word導出功能,關係圖以及熱力關係圖功能,搜索功能。 效果如下: 關係圖: 熱度關係圖: 以及搜索功能 word導出 ...
  • 1 import java.util.Scanner; 2 3 public class Demo01 { 4 public static void main(String[] args) { 5 Scanner sc=new Scanner(System.in);//創建一個掃描器對象,用於接收鍵 ...
  • javaSE學習筆記(11) Map 1、Map集合 現實生活中,我們常會看到這樣的一種集合:IP地址與主機名,身份證號與個人,系統用戶名與系統用戶對象等,這種一一對應的關係,就叫做映射。Java提供了專門的集合類用來存放這種對象關係的對象,即 介面。 我們通過查看 介面描述,發現 介面下的集合與 ...
  • 開發環境: Windows操作系統開發工具:Eclipse+Jdk+Tomcat+MySQL資料庫運行效果圖: 源碼及原文鏈接:https://javadao.xyz/forum.php?mod=viewthread&tid=39 ...
  • javaSE學習筆記(10) List、Set 1、數據存儲的數據結構 常見的數據結構 數據存儲的常用結構有:棧、隊列、數組、鏈表和紅黑樹。 1、棧 棧 : stack ,又稱堆棧,它是運算受限的線性表,其限制是僅允許在標的一端進行插入和刪除操作,不允許在其他任何位置進行添加、查找、刪除等操作。 簡 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...